feat(detector): deprecate --fast (now a no-op, full scan always)

Since the jsdom removal the static HTML/CSS analysis is fast (~4ms/file) and
covers every rule, so the regex-only `--fast` path only loses coverage (it
ran ~10 of 41 rules) for no real speed win. It's a foot-gun: a `--fast` scan
can read "clean" because most rules silently don't run.

Deprecate gracefully rather than hard-remove: the flag is still accepted (so
existing CI scripts don't break) but ignored, with a one-line stderr notice,
and the full scan always runs. Dropped from --help and the example. Removed
the `--fast` suggestion from the many-files warning and from critique.md's
scan guidance.

Ships to users via a CLI release (npm) and rides the next skill release in
the bundled detector. Tests updated to assert the deprecation behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-05-28 15:50:53 -07:00
co-authored by Claude Opus 4.8
parent 772aa73aa3
commit f7f2bfc800
30 changed files with 215 additions and 130 deletions
@@ -50,12 +50,12 @@ Run the bundled detector and browser visualization evidence. Assessment B is man
CLI scan:
```bash
node .claude/skills/impeccable/scripts/detect.mjs --json [--fast] [target]
node .claude/skills/impeccable/scripts/detect.mjs --json [target]
```
- Pass markup files/directories as `[target]`; do not pass CSS-only files.
- For URLs, skip CLI scan and use browser visualization.
- For 200+ scannable files, use `--fast`; for 500+, narrow scope or ask.
- For very large trees (500+ scannable files), narrow scope or ask.
- Exit code 0 = clean; 2 = findings.
- If the detector entrypoint is missing or fails to load, report deterministic scan unavailable and continue with browser/manual review.
@@ -79,7 +79,6 @@ function printUsage() {
Scan files or URLs for UI anti-patterns and design quality issues.
Options:
--fast Regex-only mode (skip static HTML/CSS analysis, faster but misses linked stylesheets)
--json Output results as JSON
--gpt Also report GPT-specific provider tells (off by default)
--gemini Also report Gemini-specific provider tells (off by default)
@@ -89,13 +88,12 @@ Detection modes:
HTML files Static HTML/CSS analysis (default, catches linked CSS)
Non-HTML files Regex pattern matching (CSS, JSX, TSX, etc.)
URLs Puppeteer full browser rendering (auto-detected)
--fast Forces regex for all files
Examples:
impeccable detect src/
impeccable detect index.html
impeccable detect https://example.com
impeccable detect --fast --json .`);
impeccable detect --json .`);
}
async function detectCli() {
@@ -107,7 +105,15 @@ async function detectCli() {
if (args[0] === 'detect') args = args.slice(1);
const jsonMode = args.includes('--json');
const helpMode = args.includes('--help');
const fastMode = args.includes('--fast');
// --fast (regex-only) is deprecated: since the jsdom removal, the static
// HTML/CSS analysis is fast and covers every rule, so the regex-only path
// only loses coverage for no real speed win. Accept the flag for back-compat
// but ignore it and run the full scan.
if (args.includes('--fast')) {
process.stderr.write(
'Note: --fast is deprecated and ignored. The full scan is fast now and runs every rule.\n',
);
}
const providers = [];
if (args.includes('--gpt')) providers.push('gpt');
if (args.includes('--gemini')) providers.push('gemini');
@@ -177,7 +183,7 @@ async function detectCli() {
process.stderr.write(
`\nFound ${files.length} files (${htmlCount} HTML) in ${target}.\n` +
`Scanning may take a while${htmlCount > 10 ? ' (static HTML/CSS processes each HTML file individually)' : ''}.\n` +
`Use --fast to skip static HTML/CSS analysis, or target a specific subdirectory.\n`
`Target a specific subdirectory to narrow scope.\n`
);
const ok = await confirm('Continue?');
if (!ok) { process.stderr.write('Aborted.\n'); process.exit(0); }
@@ -197,7 +203,7 @@ async function detectCli() {
for (const file of files) {
const ext = path.extname(file).toLowerCase();
let fileFindings;
if (!fastMode && HTML_EXTENSIONS.has(ext)) {
if (HTML_EXTENSIONS.has(ext)) {
fileFindings = await detectHtml(file, scanOptions);
} else {
fileFindings = detectText(fs.readFileSync(file, 'utf-8'), file, scanOptions);
@@ -214,7 +220,7 @@ async function detectCli() {
}
} else if (stat.isFile()) {
const ext = path.extname(resolved).toLowerCase();
if (!fastMode && HTML_EXTENSIONS.has(ext)) {
if (HTML_EXTENSIONS.has(ext)) {
allFindings.push(...await detectHtml(resolved, scanOptions));
} else {
allFindings.push(...detectText(fs.readFileSync(resolved, 'utf-8'), resolved, scanOptions));