forked from bchanot/claude
fix(seo,geo): C1a — find sees build output, grep does not; the two disagreed
Surfaced by dogfooding on a real Astro repo instead of reading the spec. Claude Code installs a shell function routing `grep` to ugrep with `--ignore-files`, so grep honours .gitignore and never descends into a gitignored dist/. `find` honours nothing. seo-analyzer uses both. Measured on zenquality (Astro, dist/ gitignored but built locally), seo-analyzer.md:497 returned 92 images, 45 of them under dist/ — every asset listed twice, source and generated copy, byte-identical. Two live consequences: - "top 20 by size" was ~10 real images dressed as 20. - Batch C (`cwebp -q 80 <img> -o <img>.webp`) could target dist/og-image.png; the .webp lands in dist/ and the `npm run build` the dispatcher runs to VERIFY the fix erases it. Fix lands, verification passes, nothing survives, report says applied. lib/source-scope.sh separates source from build output, framework-aware. `public/` is deliberately NOT excluded by default: it is Astro/Vite/Next SOURCE and holds favicon.ico, apple-touch-icon.png and robots.txt — the very files STEP 4 curls. It is build output only for Hugo/Gatsby, detected from config (legacy config.toml alone is ambiguous, so it needs archetypes/ too). Blanket-excluding it would blind the audit to its own resource checks. findargs emits one token per line and MUST be consumed via a quoted array. I shipped a flat-string version first and the dogfood caught it: the shell globs */dist/* against the CWD and passes the matches to find as search paths, which turned 90 hits into 135 and kept every dist/ file. Both the header and a functional test now pin that. Also: no bundle item may target build output, in either agent. That is the real safety net — even if some future find leaks a dist path, the fix cannot land there. Fix the source that generates the artifact; if the source cannot be found, that is a finding, not a reason to patch the artifact. SCOPE CORRECTION: the proposal claimed grep was auditing 86 generated files instead of 9 templates. That was FALSE — the ugrep shim already skips them. Killed my own premise before coding it; the real bug is narrower and lives in find only. Do NOT "fix" the grep lines to match: they are already correct, and adding these exclusions there would drop public/. Verified: 90 -> 45 images on the real repo, 0 dist survivors, public/ preserved (favicon.ico still visible); 34 new assertions PASS / 0 FAIL; full suite green; shellcheck clean. Test file addition used the documented one-shot config-edit sentinel.
This commit is contained in:
+10
-1
@@ -545,7 +545,8 @@ sample of a 300-page site says nothing about the other 294.
|
||||
|
||||
```bash
|
||||
# Extract H1/H2/H3 from main pages to assess heading style
|
||||
for f in index.html $(find . -maxdepth 3 -name "*.astro" -o -name "*.tsx" -o -name "*.md" -o -name "*.html" | head -10); do
|
||||
mapfile -t FEXCL < <(bash ~/.claude/lib/source-scope.sh findargs) # C1a: skip build output
|
||||
for f in index.html $(find . "${FEXCL[@]}" -maxdepth 3 \( -name "*.astro" -o -name "*.tsx" -o -name "*.md" -o -name "*.html" \) | head -10); do
|
||||
echo "=== $f ==="
|
||||
grep -oE '<(h1|h2|h3)[^>]*>[^<]+</(h1|h2|h3)>|^#{1,3} .+' "$f" 2>/dev/null | head -20
|
||||
done
|
||||
@@ -970,6 +971,14 @@ PROCHAINE ETAPE : <highest-priority>
|
||||
NEVER `Write` on shared templates. `Write` is reserved for files
|
||||
you solely own: robots.txt, llms.txt, llms-full.txt. Full-template
|
||||
refactor → escalate as user action in §11.
|
||||
- **NEVER emit a bundle item targeting build output (C1a).** No path under
|
||||
`dist/ build/ .next/ .nuxt/ .output/ _site/ .astro/ .svelte-kit/ out/` —
|
||||
run `bash ~/.claude/lib/source-scope.sh list` for the authoritative set.
|
||||
Those files are regenerated: the `npm run build` the dispatcher runs to
|
||||
VERIFY your fix is what erases it. The fix lands, verification passes,
|
||||
nothing survives, and the report claims it was applied. Fix the SOURCE
|
||||
template that generates the file. If you cannot find the source, that is
|
||||
a finding — say so, do not patch the artifact.
|
||||
- **Respect PERMISSIVE/RESTRICTIVE choice.** geo-analyzer defaults to
|
||||
PERMISSIVE (GEO's goal is AI visibility). Only switch if the client
|
||||
explicitly flags premium/regulated content.
|
||||
|
||||
Reference in New Issue
Block a user