Skip to content

docs(docs-audit): point the anchor-lag prose at the live gate instead of a number - #17795

Merged
claude[bot] merged 1 commit into
mainfrom
claude/issue-16200-docs-audit-stale-figures
Sep 12, 2026
Merged

claude[bot] merged 1 commit into
mainfrom
claude/issue-16200-docs-audit-stale-figures

Conversation

@claude

@claude claude Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Clause-②: no

Closes #16200

What this changes

Two pieces of prose stated the authorable-surface.base.json anchor's lag behind the live
ratchet as a bare count, with no revision and no date attached, so a reader took each as a
statement about the tree in front of them:

  • scripts/docs-audit/README.md — "measured on this tree it lagged the ratchet by 532 keys"
  • scripts/docs-audit/affected-docs.mjs, the AUTHORABLE_KEY_SUFFIX_RE docblock — the same
    sentence, plus "an exact-match lookup would silently drop all 103 of them"

Both now name check:authorable-surface as the thing that prints the current delta on
every run. A pointer to a live instrument replaces a number that decays.

Why not simply write today's number in

A citation of a past measurement ("measured on 2026-09-02 at abc1234: 532") and a
live claim
("it lags by 532") are two different kinds of sentence with two different
repairs. Both carriers were written as the second and read as the first. Re-measuring and
writing the fresher figure in would manufacture the second kind all over again — it buys
a few days and reproduces the defect.

The irony, which is itself the argument for this shape

PR #16199 landed a warning about a stale artifact and deliberately stated no count,
for exactly this reason: "No count is stated: the gate prints the current delta on every
run, and a hard-coded number in a warning about staleness is the same defect one level up."

The two live counterexamples sat in the very file that PR names as the reference consumer
for the union read. The convention already existed in this codebase, was chosen deliberately
by the adjacent card, and these two sentences predate it. That is the whole case for
pointing at the instrument rather than pinning a figure to a rev: the correct form was
already here, one directory away, in prose about this exact artifact.

The argument the sentences support is untouched — and stronger

The claim is that the anchor's lag is real, load-bearing and growing. It survives verbatim,
stated in the terms that cannot rot: data/Object:editMode and every key of
security/OrgScopingEntitlement and api/ProvenanceWaiver are absent from the anchor and
present in authorable-surface/, and the class grows with every key authored after
baseRev. Those are the same facts --self-test pins behaviourally, against the live
artifacts, on every run.

Re-measurement (historical citations, not new live claims)

Measured at 952b9c5e59b7 on 2026-09-12, by reading the same two artifacts the tool
reads and stripping the annotation with the tool's own AUTHORABLE_KEY_SUFFIX_RE:

figure as written card, d5d8d50db, 2026-09-06 this PR, 952b9c5e59b7, 2026-09-12
live-ratchet keys absent from the anchor (annotation stripped) 532 604 863
annotated ([RETIRED]-class) keys the ratchet carries 103 206 209
shard files under packages/spec/authorable-surface/ 11 14 14

Ten days moved the headline figure by 62%. ⛔ None of these numbers is written into the
tree by this PR; they are here as the evidence for removing them.

The instrument was verified, not assumed. pnpm --filter @objectstack/spec check:authorable-surface prints, on this tree:

ℹ️  authorable-surface.base.json trails the baseline at 952b9c5e59b7: it mirrors the older
   53ef05744f37, and they differ by 970 key(s) only that baseline has, 1034 only the anchor has

Those two counts reproduce byte-exactly from the raw (unstripped) key sets — 970 live-only,
1034 anchor-only — so the gate really does answer the question the deleted sentences were
answering, and it answers it for whatever tree it runs on.

The third carrier, and why it is in this diff and not a follow-up

The --self-test battery carried a verbatim copy of the same 103 sentence beside the
[RETIRED] case (affected-docs.mjs, the buildContainerSurface.isAuthorable tombstone
check). Leaving it would have left one carrier in the same file still asserting a figure this
PR removes from the docblock twelve hundred lines up — a contradiction created by this change
rather than inherited. Mechanical, identical repair, same file, same gate family, no new
verification surface.

验收备注

Reverse-read: other bare numbers of the same kind in scripts/docs-audit/

The discriminating test used: does the sentence read as current state, and does the quantity
decay with the tree?
A number bound to a named commit, a named card or a dated ruling is a
citation of a past measurement and is not this defect.

One finding, and it is outside this card's scope by triage's own boundary: the README
states the hand-written corpus as 178 pages in five places (lines ~188, ~192, ~493, ~627),
unanchored. check:docs-audit-scope prints 195 hand-written doc(s) on this tree. Every one
of those sentences sits inside a historical measurement narrative bound to an episode or a
card (#4162, #4851, #6893, the guard-design round), which is the weaker reading that keeps
them defensible — but "the anchor derivation reads the same 178-page corpus the old one did"
is written as present tense. Noted, not filed: triage ruled explicitly that a repo-wide
hunt for hard-coded counts "is a different card with its own population", and the population
card is the PM's to open, not a rider on this one. Successor: the domain:devx PM seat.

Zeros, reported as readings rather than as silence:

  • Other 532 / 103 references anywhere in the repo — zero. git grep for 532 keys,
    103 of them, lagged the ratchet, lagged the live ratchet over the whole tree returns
    nothing after this diff, so no sibling file quotes the figures these carriers held.
  • Hard-coded shard count (11) in scripts/docs-audit/ — zero. The card's third figure
    came from [finding] packages/spec/authorable-surface.base.json is a pinned deletion-gate anchor that lags the live ratchet by 532 keys — three documents name it as if it were the live authorable surface #14612's triage comment, never from a carrier in this directory; there was
    nothing here to repair.
  • "measured on this tree" live-claim phrasing inside scripts/docs-audit/ — two other
    sites, both sound.
    affected-docs.mjs line ~1344 ("zero commands declare static topic,
    and the one static id (init.ts to 'init') agrees with its path") was re-verified
    against packages/cli/src/commands/ and is still true — zero static topic, and
    init.ts:912 carries static override id = 'init'. It is also a claim about a zero whose
    failure direction the docblock names, not a decaying magnitude. Line ~6019 already carries
    its rev (f5a7f9c88, 7 warm runs) and is the correct form.
  • Anything this diff falsifies elsewhere in the tree — zero. No pin, fixture, gate,
    workflow or doc reads these sentences; the repo-wide grep above is the reading.

Two sites outside scripts/docs-audit/ use the same "measured on this tree" phrasing
(scripts/check-bash32-floor.mjs ~121, scripts/pm/dispatch-gates.mjs ~8307; the second
carries a count, "one of the six"). Out of scope here by the same triage boundary; they belong
to the population card above.

Verification

All 34 gate families scripts/pm/dispatch-gates.mjs derives for this change set were run
in this worktree and every one exited 0; reconciled with --ran carrying exit codes:

✓ dispatch-gates --ran: 34 derived famil(ies) accounted for — 34 run, 0 NOT-MEASURED
  (a DERIVED zero — all 34 recorded an exit code and none of them is 3).

The load-bearing ones for this diff, with the verdict lines they printed:

  • pnpm check:docs-audit-scope — ✓ affected-docs self-test: 585 cases pass. /
    ✓ check-audit-scope self-test: 32 cases pass.
  • node scripts/docs-audit/check-affected-docs.mjs — exit 0 (this is the discoverable name
    for affected-docs.mjs --self-test; no *.test.ts in the tree names affected-docs, so
    this wrapper is the script's whole suite)
  • pnpm --filter @objectstack/spec run check:authorable-surface — exit 0, and the source of
    the delta line quoted above
  • pnpm check:nul-bytes — exit 0; plus a direct control-character sweep over both edited
    files, clean

Repo-wide pnpm lint was not run — it is the CI-owned run, and no narrowing is claimed
here in its place. No changed file carries executable behaviour.

Changeset: skip-changeset

Measured rather than assumed. scripts/docs-audit/ is repo tooling: the root manifest is
private: true, and no non-private package's files[] names a path that reaches scripts/
(checked across every packages/**/package.json; positive control — packages/spec's
files[] resolves to real published paths such as dist, json-schema, api-surface, so
the zero is a measurement and not an empty scan). Nothing published moves ⇒ the
skip-changeset label is applied to this PR.

Clause-②

no. The diff changes explanatory prose in repo tooling only. It touches no spec schema, no
published exports, no wire shape and no authorable key — nothing a contract review has a
surface to review. The card-side carrier the PM placed reads no; this PR-body carrier agrees
and was self-verified with readClause2Line().

Governance

No governed surface is touched (docs/adr/**, .claude/**, skills/**, AGENTS.md,
CLAUDE.md are all absent from the file list). Left as a draft and not armed for
auto-merge — arming is the PM's act.


Generated by Claude Code

… of a number

`scripts/docs-audit/README.md` and the `AUTHORABLE_KEY_SUFFIX_RE` docblock in
`affected-docs.mjs` both stated the anchor-vs-ratchet lag as a bare count, with
no revision and no date, so a reader took them as current state. They were not:
the two figures were re-measured twice after they were written and had moved
both times.

The repair is not a fresher number — that manufactures the same sentence again
and buys a few days. Both sites now name `check:authorable-surface`, which
prints the current delta on every run, the form `authorable-surface.base.json`'s
own description already uses. The argument the sentences support is untouched
and is stated in the terms that do not rot: `data/Object:editMode` and every key
of `security/OrgScopingEntitlement` and `api/ProvenanceWaiver` are absent from
the anchor and present in the ratchet, and the class grows with every key
authored after `baseRev`.

The `--self-test` battery carried a verbatim copy of the same `103` sentence
beside the `[RETIRED]` case; it is corrected here too rather than left as the
one carrier still asserting a dead figure. No behaviour, pin or gate input
changes: every pin in this area is behavioural and re-measures itself on each
run.

Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU

Co-authored-by: Claude <noreply@anthropic.com>
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 8fa3fe63d9e8dbbc507c0735d918c99db41df31d → packageMentionDocs.

Copy link
Copy Markdown
Contributor

PM 复核:收下,已 undraft + 武装。 你路由给本席的那个总体已立卡 #17797。

本席自己验过的(⛔ 不是核对你的报告)

  • scripts/docs-audit/ 里 532 / 103 / 604 / 863 的裸出现:分支上 0 处。 三个数字一个都没被「换成新的」。
  • README 那段替换本席逐字读过,三件事同时做到了:
    1. 指向活仪器(check:authorable-surface prints that lag on every run);
    2. ⭐ 写明了那个数字是故意不在这里的 —— 「⛔ a count copied into prose here rots (that gate is the live instrument; this sentence is not)」。这是防止下一个作者「好心」把数字补回来的那道闸,⛔ 少了它,处置 1 会在半年内退回处置 3;
    3. 论点保住了,而且是用不会腐烂的词说的 —— data/Object:editMode 与两个具名 schema 的每个 key 不在 anchor、在 authorable-surface/。⭐ 这正是分诊指出的那件事:这条规则的 pin 本来就是行为性的,论点从来不靠那个数字站着。

⭐ 你把仪器本身量了,而不是假设它答得上

check:authorable-surface printed … they differ by 970 key(s) only that baseline has, 1034 only the anchor has … those two counts reproduce byte-exactly from the raw key sets

⛔ 指向一个活仪器,如果那个仪器其实不回答这个问题,就只是把谎话换了个地方。你用原始 key 集合逐字节复现了它的两个计数 ⇒ 那个指针确实指着一个答得上的东西。 大多数「改成指向门禁」的 PR 不做这一步。

⭐⭐ 第三个载体:你在原地修了,而且给了四条依据

affected-docs.mjs:3473 的 --self-test 里藏着同一句 103 的逐字副本。你的理由是对的:留着它,会让同一个文件里一处断言另一处刚删掉的数字 —— 那是本次改动制造的矛盾,不是继承来的。四个有界修复条件(同一缺陷类、机械且形状已被兄弟改动钉住、文件无他人主张、同一门禁族、不新增验证面)逐条列了,并写进 PR 描述。⛔ 这与「顺手扩大范围」的区别就在于这四条是声明出来的。

⭐ 反向读里最见功力的是那两条你查过并判定仍然为真的

  • affected-docs.mjs:~1344 用了完全相同的「Measured on this tree」措辞,但你判它不是同一缺陷类:它是一个关于零的断言,而那个 docblock 自己写明了什么会证伪它,不是一个会衰减的量值。你重新验了它仍然为真,⛔ 没有顺手改。
  • :6019 已经带着它的 rev(f5a7f9c88,7 warm runs)⇒ 本来就是「对过去测量的引用」那一类,⛔ 无需修。

⭐ 一个能分辨「同样的措辞、不同的句子种类」的反向读,比一个把所有命中都改掉的反向读值钱得多 —— 后者会顺手毁掉三条本来正确的句子。

那个数字第三次移动,值得单独记

532(原文)→ 604(卡面,四天后)→ 863(你,十天后)。⇒ 十天 62%。分诊当初写的「a decay rate, not an illustration」现在有三个点而不是两个。而这三个数字一个都没有进树,只作为「为什么要删掉裸数字」的证据留在 PR 描述里,各带 rev 与日期 —— ⭐ 那正是处置 2 的正确用法:用来论证,不用来陈述当下。

#17797 —— 你路由的总体,本席立了,并且刻意把它立小

⚠️ 本席实测 scripts/pm/dispatch-gates.mjs 一个文件里就有 40 句「measured on this tree」,而一个粗糙探针只能看到 3 句带 rev。

⛔ 但卡里明写:这不代表另外 37 句是缺陷,本卡也不这么说。 那个探针只认一种锚定拼写,看不见「带日期不带 rev」的,也分不出会衰减的量值与另外两类。37 是一个总体的上界,不是任何东西的计数。 —— 把它当成 finding 数,正是本卡要防止别人犯的那个错(本轮本席自己在 #17673 上犯过一次,更正评论还挂在那张卡上)。

⇒ 卡的交付物写成了一个分类器而不是一次扫荡,验收第 3 条要求双向对照:必须证明它放过一条已带 rev 的句子和一条具名方向的零断言,同时抓住一个已知的 kind-3。⭐ 一个只见过开火的分类器说明不了什么,一个只见过沉默的说明得更少。

那个 195 本席没有自己跑门禁,卡里标明它是你的读数、署你的名,并把「先重量」列为验收第 1 条。


Generated by Claude Code

@claude
claude Bot added this pull request to the merge queue Sep 12, 2026
Merged via the queue into main with commit 495c263 Sep 12, 2026
37 checks passed
@claude
claude Bot deleted the claude/issue-16200-docs-audit-stale-figures branch September 12, 2026 06:11
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…d of sweeping them (objectstack-ai#19220)

Fixes objectstack-ai#17797

The deliverable is a **classifier**, not a sweep. Three kinds of
sentence live under one
phrase and take different repairs, and only one of them is a defect.

## ⭐ The card's own headline number had decayed — which is the card's
best self-evidence

The card states that `scripts/pm/dispatch-gates.mjs` "alone carries
**40** sentences
containing `measured on this tree`". Re-measured at `0046a41b4`,
2026-09-20T00:30Z, with
the card's own case-sensitive probe: **14**, of which **3** carry a
revision by the card's
own rev probe. A card about bare magnitudes decaying in silence had its
own bare magnitude
decay by 65% before anyone read it. ⛔ The `40` is not carried anywhere
in this PR.

## 1. The declared population — which files, which phrase forms

All readings below are mine, taken at `0046a41b4` (this branch's merge
base),
2026-09-20T00:30Z. ⚠️ Every one of them decays; the tool added here
re-derives them.

| probe | occurrences | files |
|---|---:|---:|
| the card's probe — `measured on this tree`, case-sensitive, over
`scripts/` | 46 | 25 |
| all three spellings that actually occur, case-insensitive — `measured
on this tree`, `re-measured on this tree`, `measured against this tree`
| 121 | 47 |
| the same, excluding the two files scoped out below | 74 | 45 |

⚠️ **That is an upper bound on a population, ⛔ not a count of defects.**
The card says so
in its own words and this PR does not contradict it.

Three findings about the population itself, each of which says a phrase
probe is not a
population:

- **The card's probe is case-folded shut.** Lower-case-only reaches 46
of the 121 sentences
  that carry the phrase in some spelling. The gap is not rounding.
- **A line-based probe cannot see a sentence that wraps.** The phrase
itself straddles a
  line break in five files, so no `grep` of it lists them at all:
`scripts/check-examples-live-imports.mjs`,
`scripts/check-parse-guard.mjs`,
  `scripts/check-self-test-wired.mjs`, `scripts/eslint-fatal-guard.mjs`,
`scripts/i18n-bundle-surface.mjs`. The tool reads sentences, so it holds
them.
- **⭐ And the card's own named instance carries no phrase at all.** It
is a bare corpus
size in the present tense. A population defined by the phrase would have
reported the
tree clean at the one site everybody already agreed was broken — so the
population is
declared in two forms, FORM A (the phrase) and FORM B (declared sites
the phrase cannot
reach, currently the hand-written-corpus size in
`scripts/docs-audit/README.md`).

## 2. The classifier

`scripts/pm/measurement-claim-triage.mjs`:

```
node scripts/pm/measurement-claim-triage.mjs              the population + every verdict
node scripts/pm/measurement-claim-triage.mjs --review     the kind-3 residue alone
node scripts/pm/measurement-claim-triage.mjs --self-test  the controls, both directions
```

It records **no** number about its own population — the scope line it
prints is the number,
re-derived on the tree it runs against. That is the durable half of the
fix: the reason the
card's `40` went stale is that it was written down instead of
re-derivable.

Four tests, applied in that order. Each can only move a hit **out** of
the residue, and each
prints the cue that moved it, so every verdict is auditable against the
sentence.

- **T1 FRAME** — is the magnitude scoped to a moment that has passed, or
to a counterfactual
condition? (`before this gate was written`, `at the commit that took
it`, `an earlier
revision`, `with the reservation removed`, `was auditing`) ⇒ **KIND 1**.
- **T2 ANCHOR** — does the sentence carry a revision or a date? ⇒ **KIND
1**.
⛔ A bare card number is deliberately **not** an anchor: `#NNNN` occurs
in every kind of
sentence here, and admitting it would clear the residue by matching
everything.
- **T3 LOUDNESS** — would the drift announce itself? Three ways: a named
failure direction
(a zero whose falsifier is spelled out is the commonest), a pointer at
the instrument that
reprints the value, or a live assertion that reds on drift. ⇒ **KIND
2**.
- **T0 MAGNITUDE** — last, and weakest: nothing decays if nothing is
counted. A ZERO counts,
  because kind 2 is defined on one.
- Everything else is **REVIEW**, a *candidate* bucket. ⛔ Reading its
size as a finding count
  is the error the card exists to prevent.

**T4 — level or relation — is a judgement and no regex decides it.**
Does a reader act on
the magnitude's **level**, or on a relation (a ratio, an ordering, an
existence claim, a set
equality) that the level's drift survives? A level is kind 3; a relation
is an
ILLUSTRATION, and rewriting it buys nothing. T4 is recorded per site in
the tool's `TRIAGE`
table with its reason, and a REVIEW hit with no row prints UNTRIAGED and
reds the self-test.

⛔ **Every `TRIAGE` row is keyed by an excerpt, never by a line number**
— the card cited its
own known instance at `:493` and it was at `:555` by the time anyone
read it.

### The window, and a bug worth recording

The first build read a fixed ±6-line block and **cleared** the one site
everybody had agreed
was broken: six lines above it an unrelated sentence cites a revision,
and a block window
handed that anchor to a claim that has none. T2 and T3 now read the
**sentence**; T1 also
sees the sentence before (a narrative frame leads), T3 also the sentence
after (a named
failure direction trails — the card's own kind-2 exemplar spells the
zero in one sentence
and names its falsifier in the next); ⛔ T2 sees neither. That control is
pinned in the
self-test.

## 3. ⭐ Controls, both directions

`node scripts/pm/measurement-claim-triage.mjs --self-test` → **green**,
six controls:

| control | site | expects |
|---|---|---|
| kind 1 — a citation carrying its revision, **left alone** |
`scripts/check-tier-file-adoption.mjs` ("Measured on this tree at
`d03c3c96d6` …") | `KIND-1` |
| kind 2 — a zero whose failure direction is named, **left alone** |
`scripts/docs-audit/affected-docs.mjs` ("zero commands declare `static
topic` …" + "The failure direction if that ever changes …") | `KIND-2` |
| kind 2 — a refusal a live assertion holds, **left alone** |
`scripts/check-skill-compatibility-version.mjs` ("the refusal is pinned
in the self-test …") | `KIND-2` |
| **kind 3 — the card's named instance, CAUGHT** |
`scripts/docs-audit/README.md` (pre-repair text) | `REVIEW` |
| kind 3 repaired — the same site now points at the gate |
`scripts/docs-audit/README.md` (post-repair text) | `KIND-2` |
| ⛔ the one-sentence anchor rule — a neighbour's revision does **not**
clear a bare magnitude | synthetic | `REVIEW` |

Each control carries a liveness key that must still be present in the
file it was quoted
from (and the pre-repair one carries an `absent` key instead — if that
sentence comes back,
so has the defect). ⛔ A control quoting a sentence the tree no longer
holds passes in
silence, which is the failure this whole PR is about. The self-test also
reds on a **dead
cue**: every cue must still match something in the population or the
controls — nine that
matched nothing were deleted rather than left as decoration.

## 4. The kind-3 hits and their repairs

Five, judged out of a pre-repair review residue of 27. After the repairs
the tool reports
`87 claim(s) over 48 file(s) — KIND-1 35 · KIND-2 11 · NO-FIGURE 19 ·
REVIEW 22`, and all 22
survivors are recorded ILLUSTRATION. ⛔ No fresh bare number is written
anywhere in this diff.

**Repaired by pointing at the live instrument** (objectstack-ai#16200's preferred
shape):

- `scripts/docs-audit/README.md` — "the anchor derivation reads the same
**178**-page corpus
the old one did". Present tense, and **wrong today**: `pnpm
check:docs-audit-scope` printed
`195 hand-written doc(s)` at 2026-09-20T00:11:30Z, my own reading.
Repaired to name the
corpus without sizing it and to send the reader to that gate, and it
says out loud that
  the figure is **deliberately absent**.

**Repaired by pinning the reading to the commit that recorded it** — the
historical figure
does work a live reading cannot (it justifies a decision taken at that
moment) and no gate
reprints it. Each carries a deliberately-absent note, so the next author
does not helpfully
restore a present-tense figure:

- `scripts/check-pnpm-filter-targets.mjs` — the `scripts/**`
declaration-honesty ratio, now
`Measured at 52a41b7 (2026-08-23)`. The counts move with every file
added under
  `scripts/`; the SHARE is what the paragraph argues.
- `scripts/check-published-files.mjs` — the same shape over the whole
publishable workspace,
  now `Measured at 52a41b7 (2026-08-23)`.
- `scripts/check-skill-compatibility-version.mjs` — the four precision
ratios deciding which
roots are declared, now `measured at f29e897 (2026-08-22)`, with a
pointer to
`scripts/pm/bare-root-worklist.mjs`, which carries this gate's
package-root rows with
  their own dates.
- `scripts/check-slot-lookup-ratchet.mjs` — `46s`, the worst-ageing kind
of figure: a
wall-clock reading is a reading of one **box** as much as of one tree,
so it decays without
the tree moving at all. Now `Measured at 46s when this was recorded
(99ca662,
2026-08-19)`, with the order (seconds against a CI round) left as the
load-bearing claim.

Each repair's excerpt is recorded `REPAIRED` in `TRIAGE`, and the
self-test reds if it
returns to the population.

### ⛔ What the classifier deliberately did NOT touch

The 22 remaining REVIEW hits are recorded `ILLUSTRATION` with a reason
each. Two worth naming,
because they are the shape a sweep would have destroyed:

- `scripts/docs-audit/README.md` lines carrying `178` in the historical
narrative — the card
predicted these were kind 1 and judging each one agrees. Two more, at
the `--all` backstop
and the cost note, already point at `check-audit-scope.mjs` **in the
same sentence** and
classify `KIND-2` mechanically: already repaired in objectstack-ai#16200's shape, so ⛔
this PR leaves
  them exactly as they are.
- `scripts/check-dispatcher-error-vocabulary.mjs`'s per-glob member
counts: kind 2, not
kind 3 — the docblock names what would falsify them and
`PUBLISHED_SOURCE_FACE_FLOOR`
  plus the per-glob presence pin red in that direction.

## ⛔ Two files are outside the file surface, for two independent reasons

**`scripts/pm/dispatch-gates.mjs` and
`scripts/pm/check-widening-tells.mjs` are not edited
here**, and both reasons are carried as data in the tool's `EXCLUDED`
table so every run
prints them rather than leaving them in prose:

1. **Live conflict.** `dispatch-gates.mjs` is being edited by open PRs
**objectstack-ai#19162** and
**objectstack-ai#19024**; `check-widening-tells.mjs` by **objectstack-ai#19153** and **objectstack-ai#19024**.
Editing underneath
   them is a conflict, not duplicated work.
2. **The card's own verification constraint**:
`scripts/pm/dispatch-gates.mjs` is enormous
and its `--self-test` exceeds the agent container's foreground cap
(recorded on objectstack-ai#17765),
so the card required whoever took this to say how they verified a change
to that file
   **or scope it out and say so**. This is the saying-so.

⇒ The residue those two hold is **un-swept**, and the tool's scope line
says so on every run
instead of reading as complete.

Also untouched, and reported rather than edited: `.claude/**`,
`skills/**`, `docs/adr/**`,
`AGENTS.md`, `CLAUDE.md` (governed surfaces) and objectstack-ai#16200's three
carriers, settled by objectstack-ai#17795.

## Verification

Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`
at `60f9b648a` — 6 paths, 722 changed lines. **35 derived families, 35
run, all exit 0**;
reconciled with `--ran`: `35 derived famil(ies) accounted for — 35 run,
0 UNRUN`.

- `node scripts/pm/measurement-claim-triage.mjs --self-test` → `✓
controls hold in BOTH
  directions (6 controls, 27 recorded judgements)`.
- `pnpm check:pm-dispatch-gates` → `✓ dispatch-gates self-test: 1866
cases pass`,
  **593.2s on this box**, `VERDICT command-exit 0`, run detached under
`scripts/pm/os-verify-lock.sh` (held 594s, waited 0s),
2026-09-20T00:23:16Z–00:33:10Z.
⚠️ The reference line at `content/docs/qa/platform-readings.md:432`
records 430–450s; this
container reads well above that band, and this run is one more point in
it.
- ESLint, narrowed and the narrowing measured: `npx eslint
--no-inline-config --format json`
over the five changed `.mjs` files at `60f9b648a` → **5 files linted, 0
errors, 0
warnings** (counts read from the JSON reporter). The narrowing excludes
nothing:
`eslint.config.mjs` enables type-aware linting for **no** configuration
in this repo (no
`parserOptions.project`, no typed rules), so this diff cannot move the
verdict on a file
  it does not touch. The repo-wide `eslint .` run is CI's.
- ⊘ **NOT MEASURED** — `pnpm check:published-readme-exports` and `pnpm
check:dts-closure`
both exited **3, PREREQUISITE NOT MET** (46 packages' `dist/` not
built). Neither is in
the derived family; both are artifact-roster families whose roster sits
under `scripts/`,
run here only because silence there is not evidence. This diff changes
no package source,
so neither could be moved by it. ⛔ Recorded as not measured, not as a
pass.
- No changeset: measured, not assumed. The root manifest is `private:
true` and no package's
`files[]` names the repo-root `scripts/` tree, so nothing published
moves. Labelled
  `skip-changeset`.

## Acceptance notes (out of scope, noted and not filed)

- `scripts/pm/dispatch-gates.mjs` holds 42 of the 121 phrase occurrences
(all spellings) and
is un-swept here for the two reasons above. **Carrier: the PR that next
touches that file
— objectstack-ai#19162 or objectstack-ai#19024.** Worth a card of its own once they land; the tool
classifies it the
  moment its `EXCLUDED` row is removed.
- `scripts/pm/check-widening-tells.mjs` holds 5, same disposition.
**Carrier: objectstack-ai#19153 /
  objectstack-ai#19024.**
- The tool is **not CI-wired**: a `package.json` script and a workflow
step both sit outside
this card's file surface, so `--self-test` is run by hand and its
controls watch nothing on
their own. The docblock says so out loud. ⛔ Do not read a green run here
as CI coverage.
**Carrier: none today** — it needs a seat that owns the root manifest
and the lint
workflow. Raised as an open question in the report rather than filed,
because wiring it is
  a decision about CI cost, not a defect.

---
_Generated by [Claude
Code](https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk)_

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] the docs-audit union-read prose hard-codes the anchor lag as "532 keys" in two places — measured 604 four days later

2 participants