feat(seo-data): H2 — drift baseline; regressions vs changes, not prose
seo-analyzer.md:1365 keeps history as "date + score + key changes" — prose the
LLM writes about its own previous prose. Lossy, unreproducible, and
machine-uncomparable, so "the redesign silently dropped 40 canonicals" is
invisible unless someone happens to notice.
drift snapshots title/description/canonical/robots/h1_count/jsonld_types per
URL and diffs them. Stdlib only, no auth.
The classification IS the feature: LOSING a signal is a regression, CHANGING
one is a change that may well be intended. The engine says which kind; the
agent judges. A reworded title is not an alert; an evaporated canonical is.
Runs over the WHOLE sitemap, never a sample — caught while designing: a drift
computed over a sample that changes between runs compares nothing.
NOT rank tracking. That is the common misread of this same feature elsewhere;
positions come from GSC `queries`. This is on-page regression detection.
Also caught in my own draft before testing: _capture reused
sm._mock("page.html"), the exact single-fixture flaw I had already fixed in
linkgraph — one fixture cannot express a multi-page snapshot, every URL would
read identical. Now pages.json, same convention.
Proved on a planted failure rather than a happy path — two clean sites would
look identical to a detector that always returns []:
v1 -> v2: canonical lost on /a, h1 + jsonld lost on /, title reworded,
/gone removed, /neuve added
→ 3 regressions, 1 change, gone/new both detected, title correctly NOT a
regression.
Store is ~/.claude/seo-data/drift/<host>.json, 0700, written via os.replace so
a crash never leaves a half-written baseline; a corrupt store degrades to
"first run" instead of killing the audit.
Verified: seo-data 144 -> 155 pass, 0 fail; full suite green.
This commit is contained in:
@@ -193,6 +193,27 @@ fetch.sh linkgraph --url https://ex.com/sitemap.xml [--max 500]
|
||||
• Mock is pages.json ({url: html}), not a single page.html: one fixture
|
||||
cannot express a graph — every node would carry identical links.
|
||||
|
||||
fetch.sh drift --url https://ex.com/sitemap.xml [--max 500]
|
||||
→ {"status":"ok","baseline":true,"captured":"…","pages":24,"store":"…"}
|
||||
→ {"status":"ok","baseline":false,"since":"…","gone":[…],"new":[…],
|
||||
"regressions":[{"url":…,"field":"canonical","was":"…","now":null}],
|
||||
"changes":[{"url":…,"field":"title","was":"…","now":"…"}]}
|
||||
|
||||
On-page drift between audits. seo-analyzer.md:1365 keeps only "date + score
|
||||
+ key changes" as PROSE the LLM writes about its own previous prose: lossy,
|
||||
unreproducible, machine-uncomparable. So "the redesign silently dropped 40
|
||||
canonicals" stays invisible. This snapshots title/description/canonical/
|
||||
robots/h1_count/jsonld_types per URL and diffs them.
|
||||
• NOT rank tracking (the common misread of this feature elsewhere).
|
||||
Positions come from GSC `queries`. This is regression detection.
|
||||
• Runs over the WHOLE sitemap, never a sample: a drift over a sample that
|
||||
changes between runs compares nothing.
|
||||
• LOSING a signal = regression. CHANGING one = change, possibly intended —
|
||||
the agent judges that, the engine only says which kind it is.
|
||||
• Store: ~/.claude/seo-data/drift/<host>.json, 0700, written via
|
||||
os.replace — never a half-written baseline. Corrupt store → treated as
|
||||
a first run rather than crashing the audit.
|
||||
|
||||
fetch.sh forget --label client-a
|
||||
→ {"status":"ok","removed":true|false} # false = label wasn't in the store
|
||||
|
||||
|
||||
Reference in New Issue
Block a user