Skip to content

Commit 8a82109

Browse files
committed
docs(docs-audit): update the README, CI comment and prose for the injected scope
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Msg17tAHJ3jVTYFgHydCm2
1 parent 97f4200 commit 8a82109

5 files changed

Lines changed: 65 additions & 37 deletions

File tree

.github/workflows/lint.yml

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,17 +1871,19 @@ jobs:
18711871
- name: Template version-time rewriter self-test
18721872
run: pnpm check:template-version-sync
18731873

1874-
# #4851: the docs-accuracy-audit workflow carries its default scope inline
1874+
# #4851: the docs-accuracy-audit workflow's default scope is a generated list
18751875
# (a workflow script runs in a vm with no filesystem, so it cannot enumerate
1876-
# content/docs/ itself). Hand-kept, that list rotted in BOTH directions —
1877-
# 16 entries pointing at files that no longer existed after the
1878-
# protocol/objectos → protocol/kernel rename, and 48 existing docs missing
1879-
# from it — while every "FULL audit" run reported green, which is how the
1880-
# accuracy defects in #4781 and #4817 survived ~2 months. This regenerates
1881-
# the list from the filesystem and fails when the two disagree either way.
1882-
# It lives in this job deliberately: the change that breaks the list is a
1883-
# docs rename, so a `packages/**` paths filter would blind the gate to
1884-
# exactly its own failure mode.
1876+
# content/docs/ itself — the caller hands the list in). Hand-kept, that list
1877+
# rotted in BOTH directions — 16 entries pointing at files that no longer
1878+
# existed after the protocol/objectos → protocol/kernel rename, and 48
1879+
# existing docs missing from it — while every "FULL audit" run reported
1880+
# green, which is how the accuracy defects in #4781 and #4817 survived
1881+
# ~2 months. This derives the list from the filesystem and fails when it and
1882+
# scripts/docs-audit/handwritten-docs.json disagree either way; it also runs
1883+
# the workflow against stub agents to check the body still consumes what it
1884+
# is handed. It lives in this job deliberately: the change that breaks the
1885+
# list is a docs rename, so a `packages/**` paths filter would blind the gate
1886+
# to exactly its own failure mode.
18851887
- name: Docs-audit scope is derived, not hand-kept
18861888
run: pnpm check:docs-audit-scope
18871889

scripts/docs-audit/README.md

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -456,17 +456,33 @@ node scripts/docs-audit/check-audit-scope.mjs --write # regenerate the list fr
456456
node scripts/docs-audit/check-audit-scope.mjs --self-test
457457
```
458458

459-
The `docs-accuracy-audit` workflow (part 3) carries its default scope **inline**, as
460-
`ALL_HANDWRITTEN`. It has to: a workflow script runs inside a `node:vm` context whose
461-
only globals are `log`/`phase`/`console`/`budget`/timers plus
462-
`agent`/`parallel`/`pipeline`/`workflow`/`args`, with code generation disabled — no
463-
`require`, no `import`, no filesystem. It can neither walk `content/docs/` nor read a
464-
JSON artifact, so the list cannot be derived *at run time*.
459+
**THE single source is `scripts/docs-audit/handwritten-docs.json`** — one generated file,
460+
with two consumers: this gate, and whoever invokes the `docs-accuracy-audit` workflow
461+
(part 3), who reads it and hands its `docs` array in as `args.handwritten`.
465462

466-
It is therefore derived at *generation* time instead: `--write` rewrites the block from
463+
The workflow body cannot read it itself. A workflow script runs inside a `node:vm`
464+
context whose only globals are `log`/`phase`/`console`/`budget`/timers plus
465+
`agent`/`parallel`/`pipeline`/`workflow`/`args`, with code generation disabled — no
466+
`require`, no `import`, no filesystem. It can neither walk `content/docs/` nor open a
467+
JSON artifact. But it does not have to: `args` **is** an injection channel, delivered
468+
verbatim from the invocation, so the read happens in the **caller**, outside the sandbox,
469+
and the list arrives as data.
470+
471+
The list used to live inline in the workflow body, as `ALL_HANDWRITTEN`. `.claude/**` is
472+
a governed surface (human-merge-only, never armed, never queued), so adding one customer
473+
documentation page forced a governed edit — through a bookkeeping list that merely
474+
happened to live there, and invisibly: nothing in such a card's file list showed a
475+
governed path until this gate ran. Maintainer ruling, 2026-09-01, verbatim 「同意」: move
476+
it off. ⚠️ The governed register itself is **unchanged** — narrowing it was the option
477+
that ruling explicitly rejected, and this file is no precedent for relocating anything
478+
else out of `.claude/**`.
479+
480+
The list is derived at *generation* time: `--write` rewrites the artifact from
467481
`affected-docs.mjs --all` (one definition of "hand-written doc", not two), and the plain
468-
run is a CI gate in `lint.yml` that fails when the block and `content/docs/` disagree
469-
**in either direction**.
482+
run is a CI gate in `lint.yml` that fails when the artifact and `content/docs/` disagree
483+
**in either direction**. It also runs the workflow against stub agents and checks that the
484+
body still *consumes* what it is handed — an artifact in sync with `content/docs/` proves
485+
nothing about a body that has stopped reading it, and that failure would be silent.
470486

471487
Both directions matter, and only one had ever been noticed (#4851):
472488

@@ -642,15 +658,22 @@ a consumer, add it to that list.**
642658
A Claude Code multi-agent workflow (`.claude/workflows/docs-accuracy-audit.js`). For each
643659
doc: an agent reads it, locates the real implementation, and applies evidence-backed
644660
fixes in place; a second **adversarial verifier** re-checks every fix against the code and
645-
repairs over-corrections. Scope it with `args.docs`; omit for a full audit.
661+
repairs over-corrections. Scope it with `args.docs`; for a full audit hand in the whole
662+
set as `args.handwritten`.
646663

647664
```js
648665
// scoped to the docs a code change touched:
649666
Workflow({ name: 'docs-accuracy-audit', args: { docs: [/* output of affected-docs.mjs */] } })
650-
// full audit of all hand-written docs:
651-
Workflow({ name: 'docs-accuracy-audit' })
667+
// full audit of all hand-written docs — read the artifact first, OUTSIDE the sandbox:
668+
// node -e "console.log(JSON.stringify(require('./scripts/docs-audit/handwritten-docs.json').docs))"
669+
Workflow({ name: 'docs-accuracy-audit', args: { handwritten: [/* that array */] } })
652670
```
653671

672+
⛔ There is no "omit `args` and audit everything" invocation, and there cannot be: the
673+
body has no filesystem, so with nothing handed in it does not know what "everything" is.
674+
It refuses by name rather than inventing a scope — the two shapes it could invent are a
675+
silent audit of nothing and a stale list, and both report success.
676+
654677
It edits files in place (frontmatter preserved, no moves) and returns a per-doc log of
655678
fixes, verifier repairs, and residual items that couldn't be confirmed against code —
656679
**except** for `content/docs/releases/**`, which is reviewed read-only and returns

scripts/docs-audit/affected-docs.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ let diffBaseRef = null;
236236
* the audit scoping command still returns them and they keep getting audited. #4920
237237
* considered excluding them and REJECTED it: the most-read pages in the docs would go
238238
* permanently unaudited and silently, and a second definition of "docs this tooling
239-
* covers" would grow next to the generated block. What forks is the DELIVERABLE — the
239+
* covers" would grow next to the generated scope artifact. What forks is the DELIVERABLE — the
240240
* drift comment tells the reader to file an issue instead of editing (#6893: a comment
241241
* listing `content/docs/releases/v17.mdx` next to editable pages steers a dev who
242242
* treats the list as a worklist straight into the one edit the repo forbids).

scripts/pm/check-governed-merges.mjs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -607,11 +607,9 @@
607607
* with no governed merges the sweep costs ZERO lookups. `--test` never
608608
* touches the network, and spends anything at all in exactly one case: a hit
609609
* on a `GENERATED_SURFACE_EXCEPTIONS` row recomputes that generator's output
610-
* on the local tree under test (the #9866 row reads the file, `git
611-
* merge-base`/`git show` for the base version and the docs derivation; a
612-
* #11705 row runs that generator's own `--check` once, ~3 s, for every path it
613-
* owns in the diff) — still zero API calls; every other `--test` run reads
614-
* only the register in this file.
610+
* on the local tree under test (a #11705 row runs that generator's own
611+
* `--check` once, ~3 s, for every path it owns in the diff) — still zero API
612+
* calls; every other `--test` run reads only the register in this file.
615613
*/
616614

617615
import { execFileSync, spawnSync } from 'node:child_process';

scripts/pm/check-governed-queue-guard.mjs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,20 @@
133133
*
134134
* ## The generated-artifact exception is honoured, and that is not optional
135135
*
136-
* `.claude/workflows/docs-accuracy-audit.js` holds a required gate's own
137-
* `--write` artifact, so EVERY page-adding docs PR touches the governed
138-
* surface (measured 5-for-5 on #9866). A guard that refuses those refuses
139-
* routine traffic, which is the same poison one level down. The #10277 Option-C
140-
* exception (maintainer 「10277 同意 C」) is therefore applied here through the
141-
* register's own `applyGeneratedExceptions`, with provenance recomputed
142-
* BYTE-EXACT against this build's own base sha — never a stored baseline, and
143-
* fail-closed on every error path, exactly as the four ruled constraints
144-
* require.
136+
* A generator-owned file sitting inside a governed surface makes routine
137+
* traffic cross the fence, and a guard that refuses routine traffic is the same
138+
* poison one level down. So the register's provenance exception is applied here
139+
* through its own `applyGeneratedExceptions`, with provenance recomputed on this
140+
* build's own tree — never a stored baseline, and fail-closed on every error
141+
* path, exactly as the ruled constraints require.
142+
*
143+
* ⚖️ The original case for this — `.claude/workflows/docs-accuracy-audit.js`
144+
* holding a required gate's own `--write` artifact, so EVERY page-adding docs PR
145+
* touched the governed surface (measured 5-for-5 on #9866) — is GONE, and by a
146+
* different remedy: on 2026-09-01 the maintainer ruled that list off the governed
147+
* surface entirely (#13591, verbatim 「同意」). Its register row retired with it.
148+
* The mechanism stays because the #11705 rows still need it, and because the
149+
* shape recurs; ⛔ its absence from a diff is not a reason to relax anything.
145150
*
146151
* NOTHING about the exception is decided here: membership is the register's
147152
* `generatedExceptionFor` and the recompute is its `recomputeProvenanceFor`,

0 commit comments

Comments
 (0)