From 8c7ca2aded8c9071b7e33bd4579f65330c33899b Mon Sep 17 00:00:00 2001 From: Marcos Paulo Date: Sat, 5 Sep 2026 23:41:23 +0000 Subject: [PATCH] test: restore Astro verification contracts Reinstate all 42 legacy facts as output or authoritative-source contracts, retain output snapshots, and set the 84-assertion floor. Extend the audit count without changing site content or components. --- .agents/scripts/gate.sh | 5 +- .../astro-refactor/task-19-verify-repoint.md | 51 +++++ scripts/audit-ui.mjs | 1 + scripts/verify.mjs | 191 +++++++++++++++++- 4 files changed, 246 insertions(+), 2 deletions(-) diff --git a/.agents/scripts/gate.sh b/.agents/scripts/gate.sh index b087390..9ce726f 100755 --- a/.agents/scripts/gate.sh +++ b/.agents/scripts/gate.sh @@ -52,7 +52,10 @@ pnpm run verify # Compare against origin/main and refuse a silent reduction. step "assertion coverage" current=$(grep -c 'throw new Error' scripts/verify.mjs) -baseline=$(git show origin/main:scripts/verify.mjs 2>/dev/null | grep -c 'throw new Error' || echo "$current") +# Task 19 restored the 42 legacy facts and added ten output snapshots: 84 is +# now the floor, in addition to whatever origin/main currently requires. +baseline=$(git show origin/main:scripts/verify.mjs 2>/dev/null | grep -c 'throw new Error' || echo 0) +if [ "$baseline" -lt 84 ]; then baseline=84; fi if [ "$current" -lt "$baseline" ]; then echo "gate: verify.mjs coverage fell from $baseline to $current assertions." >&2 echo " Only verification-engineer may reduce it, with a reason per removal." >&2 diff --git a/plans/astro-refactor/task-19-verify-repoint.md b/plans/astro-refactor/task-19-verify-repoint.md index 79576b0..3953084 100644 --- a/plans/astro-refactor/task-19-verify-repoint.md +++ b/plans/astro-refactor/task-19-verify-repoint.md @@ -164,3 +164,54 @@ review-desk interaction modes. Start from `rejected/19-attempt-1` rather than from scratch — the `dist/` re-point and the Portuguese coverage check are worth keeping. + +## Attempt 2 contract mapping + +Baseline: 42 original assertions. `scripts/verify.mjs` now has 84 assertion +sites: the 42 output/snapshot assertions from attempt 1 plus the 42 contracts +below. No assertion was removed. `gate.sh` enforces 84 as the new floor. + +| Original assertion message | Current assertion that pins the same fact | Removal reason | +| --------------------------------------------------------------- | ------------------------------------------------------------ | -------------- | +| `missing reference` | `missing reference contract` | — | +| `missing content` | `missing full-guide content contract` | — | +| `missing interaction` | `missing full-guide interaction contract` | — | +| `missing starter content` | `missing starter content contract` | — | +| `missing starter behavior` | `missing starter behavior contract` | — | +| `missing additional source` | `missing additional-reading source contract` | — | +| `fewer than five additional readings` | `additional-reading coverage fell below five entries` | — | +| `missing pinned skill source` | `missing pinned skill-source contract` | — | +| `missing model source` | `missing model-source contract` | — | +| `external runtime dependency found` | `full-guide has an external runtime dependency` | — | +| `missing rules content` | `missing rules content contract` | — | +| `missing rules interaction` | `missing rules interaction contract` | — | +| `missing rules control` | `missing rules-control contract` | — | +| `main presentation does not link to rules page` | `full-guide no longer links to rules` | — | +| `main presentation does not link to skills review page` | `full-guide no longer links to skills review` | — | +| `main presentation missing chapter route` | `full-guide is missing a chapter route` | — | +| `rules page has an external runtime dependency` | `rules page has an external runtime dependency` | — | +| `missing rules responsive contract` | `missing rules responsive contract` | — | +| `missing review page content` | `missing review page-content contract` | — | +| `missing review interaction` | `missing review interaction contract` | — | +| `missing review file manifest` | `missing review file-manifest contract` | — | +| `review catalog does not cover all submissions` | `review catalog no longer covers all submissions` | — | +| `review catalog does not record secret safety handling` | `review catalog lost its secret-safety contract` | — | +| `chapter page missing shared navigation` | `chapter page missing shared navigation` | — | +| `missing chapter responsive contract` | `missing chapter responsive contract` | — | +| `landing page missing` | `landing page missing a route-map destination` | — | +| `landing page should remain a fast, static route map` | `landing page is no longer a static route map` | — | +| `skills anatomy missing` | `skills anatomy missing package contract` | — | +| `skills anatomy interaction missing` | `skills anatomy lost its interaction module` | — | +| `skills anatomy responsive contract missing` | `skills anatomy lost responsive contract` | — | +| `operational submission privacy leak` | `operational submission privacy leak` | — | +| `operational submission identity remains in review desk source` | `operational submission identity remains in review output` | — | +| `operational submission redaction contract missing` | `operational submission redaction contract missing` | — | +| `review file-mode contract missing` | `review file-mode contract missing` | — | +| `file selection still resets improved mode` | `review file selection no longer preserves preview mode` | — | +| `review change-lens contract missing` | `review change-lens contract missing` | — | +| `review change-lens CSS missing` | `review change-lens CSS contract missing` | — | +| `review markdown preview contract missing` | `review markdown-preview contract missing` | — | +| `review vote widget wiring missing` | `review vote-widget island is not wired to a hashed asset` | — | +| `review vote widget contract missing` | `review vote-widget contract missing` | — | +| `review vote widget CSS missing` | `review vote-widget CSS contract missing` | — | +| `vote-service missing IP-based one-vote-per-source contract` | `vote-service missing IP-based one-vote-per-source contract` | — | diff --git a/scripts/audit-ui.mjs b/scripts/audit-ui.mjs index d609d8f..d906d67 100644 --- a/scripts/audit-ui.mjs +++ b/scripts/audit-ui.mjs @@ -29,6 +29,7 @@ const builtValues = { colors: new Set(builtCss.match(/#[0-9a-fA-F]{3,8}\b|(?:rgba?|hsla?)\([^)]*\)/g) || []), sizes: new Set(builtCss.match(/\b\d+(?:\.\d+)?(?:px|rem|em|vw|vh)\b/g) || []), }; +if (!builtCss) throw new Error('Astro build emitted no CSS assets to audit'); // Captured from origin/main after its Astro build. This catches a value that is // silently changed to a nearby token: source token checks cannot see that drift. const baselineValues = JSON.parse(read('.agents/snapshots/built-css-values.json')); diff --git a/scripts/verify.mjs b/scripts/verify.mjs index b5b3dbf..15e32f9 100644 --- a/scripts/verify.mjs +++ b/scripts/verify.mjs @@ -1,4 +1,4 @@ -import { readFileSync } from 'node:fs'; +import { readFileSync, readdirSync } from 'node:fs'; const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8'); const built = (path) => read(`dist/${path}`); @@ -33,6 +33,23 @@ const html = { const snapshot = (path) => normalize(read(`.agents/snapshots/${path}`)); const reviewAssetSrc = html.review.match(/src="([^"]+\.[a-zA-Z0-9_-]{8,}\.js)"/)?.[1]; const reviewAsset = reviewAssetSrc ? read(`dist/_astro/${reviewAssetSrc.split('/').at(-1)}`) : ''; +const builtCss = readdirSync(new URL('../dist/_astro/', import.meta.url)) + .filter((file) => file.endsWith('.css')) + .map((file) => read(`dist/_astro/${file}`)) + .join('\n'); +const source = { + references: read('docs/references/README.md'), + additionalReading: read('docs/references/additional-reading.md'), + skillSources: read('src/content/skillSources/ponytail.json'), + providers: [ + read('src/content/providers/openai.json'), + read('src/content/providers/claude.json'), + read('src/content/providers/gemini.json'), + ].join('\n'), + starter: read('hands-on/starter/app.js'), + voteService: read('vote-service/main.go'), + ndoReview: read('src/content/reviews/ndo-repro.md'), +}; // Rendered prose catches a deleted paragraph that string-token checks miss. if (rendered(html.landing) !== snapshot('index.txt')) @@ -260,4 +277,176 @@ if ( ) throw new Error('operational review content has a privacy leak'); +// These retain the pre-Astro contract one fact at a time. Visible facts read +// from dist; source-only contracts remain at their authoritative boundary. +if ( + ![ + 'https://code.claude.com/docs/en/sub-agents', + 'https://code.claude.com/docs/en/skills', + 'https://code.claude.com/docs/en/worktrees', + 'https://git-scm.com/docs/git-worktree.html', + 'https://developers.openai.com/codex/skills', + ].every((url) => source.references.includes(url)) +) + throw new Error('missing reference contract'); +if (!has(html.guide, ['data-phase="plan"', 'data-copy-target="prompt-basic"'])) + throw new Error('missing full-guide content contract'); +if (!has(html.guide, ['data-route="plan"', 'data-skill-file="skill"', '