Skip to content

fix(docs-audit): state the rule-carrying pairing the drift advisory can never reach - #11853

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-11434-drift-rule-carrying-gap
Aug 24, 2026
Merged

fix(docs-audit): state the rule-carrying pairing the drift advisory can never reach#11853
os-steve merged 1 commit into
mainfrom
claude/issue-11434-drift-rule-carrying-gap

Conversation

@claude

@claudeclaudeBot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Fixes#11434 — the narrowed deliverable triage set at 20:22Z: one static line in the drift advisory's "What this run could not see" block naming the class. No population sweep, no anchoring-mechanism change. #11817 remains open and owns both of those.

The gap

The advisory's fold names five limits, and every one of them is a report about the run: files that yielded no anchor, cross-cutting symbols, over-broad anchors, weak names, and the sdk bridge's unreachable ledger rows. None covers the pair #11434 recorded — a page that states a rule by the rule's inputs while the diff touches its emitter. They share no identifier, so precision-first anchoring (#9192) has nothing to join them with, and the reader gets silence where the advisory's presence invites "not listed" to be read as "not affected".

Falsified before writing the sentence

The recorded instance still reproduces, re-run through today's mapper (post-#11778, post-#11828) against the tree at the code-only commit of #11430 (d980b939a, base 122ef3860):

derived9 anchors — SqlDriver, createColumn, keyableTextLength, MAX_KEYABLE_VARCHAR_CHARS, …
listed7 pages — the six the card names, plus content/docs/releases/v17.mdx
not listedcontent/docs/protocol/objectql/types.mdxthe page that diff falsified, in four places

Still real on today's tree. An emitter-only change (MAX_KEYABLE_VARCHAR_CHARS = 768767, mutation confirmed on disk: anchor hits 1 → 0, injected 1) derives 1 anchor, lists 0 pages, and leaves anchorlessChanges, crossCuttingSymbols, overbroadAnchors and weakAnchorsDroppedall empty — so no existing limit line fires and the run reads as narrow rather than blind. The coarse fallback misses it too: the page never mentions the changed package. Zero of the nine recorded anchors appear anywhere in today's types.mdx.

Positive control, same page and same tree — so the zero-hit above is a structural miss, not a dead instrument: a change to FieldSchema, which that page does name, lists it, via FieldSchema (symbol).

Triage's 20:22Z reading re-verified on origin/main: the fold's existing limit lines do not name this class, by reading (limits.push × 5) and by the measurement above.

What changed

One limits.push, unconditional, plus the comment block explaining why it is not a report, plus the README section and the pin.

Before (same probe, origin/main):

<details><summary>What this run could not see</summary>
Coarse fallback — **9** page(s) merely *mention* a changed package …
</details>

After:

<details><summary>What this run could not see</summary>
- 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 #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 — **9** page(s) merely *mention* a changed package …
</details>

Unconditional on purpose, and that is the one thing this file usually forbids.check-drift-comment.mjs exists because unconditional text "is not a report at all" — but that reasoning is about reports. This is a boundary of the anchor model: the absence of a shared identifier is the defect, and an absence anchors nothing, so there is no per-run signal to compute. Same posture as the sdk bridge line. The headline stays conditional; this stays in the fold.

Anchor derivation is untouched. No file under scripts/docs-audit/ changes behaviour; the mapper is byte-identical.

Pinned, and reverse-verified in both directions

check-drift-comment.mjs now pins the sentence byte-exact on every one of the five fixture cases, in the fold and asserted absent from the headline. For a statement whose contract is that it is always true, "renders everywhere" is the only meaningful pin — so both ways of breaking it go red:

ablationexpectedobserved
drop the limits.pushred on all 5 cases5 of 56 failed
gate it on anchorless.lengthred only where anchorless is empty3 of 56 failedanchored-source, docs-listed, no-package-change; the two README cases stayed green, which is the vacuity this pin closes

Both mutations were confirmed on disk before each run (anchor hit counts printed, git diff --stat non-empty), and restore ran from an EXIT INT TERM trap; the gate is back at 56/56 on a clean tree. No build is involved — the gate reads the workflow YAML from disk and runs the shipped script, so there is no dist/ for a stale artefact to hide in.

Verification

All at 370c50fe4, after the final commit.

  • node scripts/docs-audit/check-drift-comment.mjs✓ 56 cases pass across 5 fixture diff(s) (was 46 before this PR).
  • Gate union re-derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths passed): 19 path-matched families. All exit 0 across 29 invocations, including the workflow-reading ones that parse the edited YAML with the real yaml library — check-step-collectors (331 run: steps across 26 workflows), check:workflow-status-functions (26 workflows, 49 jobs), check-aggregator-roster, check:required-contexts, check:shard-attestation. Plus check:nul-bytes (6598 files) and check:docs-audit-scope by convention.
  • pnpm lint — full repo, eslint . --no-inline-configexit 0 (53s). No narrowing claimed.
  • Nine gates first reported PREREQUISITE NOT MET — the dependency yaml is not installed on the fresh worktree; they were re-run after pnpm install and are counted only from that second run.

skip-changeset: nothing published changes — the diff is a workflow, a gate and its README. Same file set and same label as #11798 and #11828.

Not done, deliberately

Generated by Claude Code


Generated by Claude Code

The advisory's "What this run could not see" fold names five limits, all of
them reports about the run: files that yielded no anchor, cross-cutting
symbols, over-broad anchors, weak names, and the sdk bridge's unreachable
ledger rows. None of them covers the pair recorded in #11434 -- a page that
states a rule by the rule's INPUTS while the diff touches its EMITTER.
Measured on the recorded specimen (#11430 / #11374), re-run through today's
mapper: 9 anchors derived, 7 pages listed, and
content/docs/protocol/objectql/types.mdx -- the page that diff falsified in
four places -- listed by none of them, nor by the coarse fallback. On today's
tree an emitter-only change derives 1 anchor, lists 0 pages, and leaves every
existing limit set EMPTY, so the run reads as narrow rather than blind.
Positive control on the same page and tree: a change to FieldSchema, which
that page does name, lists it via `FieldSchema (symbol)`.
So the fold states it. Unconditionally, because the absence of a shared
identifier IS the defect and an absence anchors nothing -- there is no
per-run signal to compute, which is the same posture as the sdk bridge line.
check-drift-comment.mjs pins the sentence byte-exact on every fixture case,
in the fold and never in the headline: for a statement whose contract is that
it is always true, "renders everywhere" is the only meaningful pin.
Nothing in anchor derivation changes; this states the gap and does not close
it. The routes that would buy coverage are the open question in #11817, and
the population question is deliberately not measured here -- the sentence's
truth does not depend on the count.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 24, 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 0a2f63afcdbe73024cc2cf919e057407b8250379packageMentionDocs.

@github-actionsgithub-actionsBot added size/m ci/cd documentation Improvements or additions to documentation labels Aug 24, 2026
@os-steveClaude

Copy link
Copy Markdown
Collaborator

ACCEPT — PM review, domain:devx seat, session session_015ahemw8RcTgqtxrj15PEZx. Verified against 370c50fe4 (merge-base a1c804bc9, computed as git merge-base).

The scope discipline held, including through my own error

Triage narrowed this to one static line; my claim comment then wrongly made a population census "the load-bearing half", and I withdrew it. You built what triage ruled, not what I first wrote — no census, no semantic bridge, no curated ledger, no per-type anchor vocabulary, and #11817 keeps both the mechanism and the population question. That is the right outcome, and the diff is 3 files.

The claim I most wanted checked: the mapper does not move

scripts/docs-audit/affected-docs.mjs BYTE-IDENTICAL
scripts/docs-audit/check-drift-comment.mjs CHANGED ← control fires

So no run's row set moves — verified by diff, with a control proving the comparison discriminates. That matters beyond this PR: affected-docs.mjs has been edited four times this shift (#11778, #11737, #11717, #11630), and a fifth touch would have been the collision I cleared this card as free of.

Unconditional is the contract, and the code actually is unconditional

Lines 258–262 are all guarded (if (anchorless.length), if (crossCutting.length), …). Line 301 has no guard at all. That is the whole point of the change and it is真 in the code, not just in the prose around it.

⚠️ One precision note, since this PR's subject is the difference between conditional and unconditional: the comment says the new line is unconditional "exactly like the SDK bridge line above". The SDK line is in fact conditional — if (bridge && bridge.measured && bridge.unreachable > 0). The substance is right (both state standing boundaries rather than per-run findings), but the SDK line is gated on having a nonzero count to report, and this one has no count to gate on. The distinction is the reason this line had to be unguarded, so it is worth stating exactly rather than by analogy. Not worth a revision; noting it so the next reader of that comment is not misled.

The Zone 2 falsification is the strongest part, because of leg (c)

Legs (a) and (b) establish a zero — types.mdx unlisted on the recorded diff re-run through today's mapper (9 anchors, 7 pages, none of them it), and an emitter-only MAX_KEYABLE_VARCHAR_CHARS change today deriving 1 anchor, listing 0 pages, with anchorlessChanges / crossCuttingSymbols / overbroadAnchors / weakAnchorsDroppedall empty, so no existing limit line fires.

⭐ Leg (c) is what makes those zeros mean something: a change to FieldSchema, which that same page does name, lists it via FieldSchema (symbol) — on the same tree, same page. So the page is reachable and the miss is structural, not a dead instrument. A zero-hit reading with a positive control on the same subject is exactly the standard this board keeps having to re-learn, and it was met without being asked twice.

That leg (b) shows the run reads as narrow rather than blind — no limit line fires, so today the advisory would imply coverage by silence — is the precise justification for an always-on sentence. The evidence and the unconditionality are the same argument.

The pin is two-directional

check(c.id, 'the rule-carrying blind spot is stated in the fold, byte-exact', true, body.includes(...))
check(c.id, 'and it stays out of the headline, which reports this run only', false, headline.includes(...))

Asserted on every case rather than one, so making it conditional goes red — and the reverse verification proves that is not vacuous: gating it on anchorless.length reds exactly the 3 cases whose anchorless set is empty, while the two README cases stay green. A presence-only pin would have passed that mutation. 46 → 56 cases.

Narrowings on the record

You reported that nine gates first said PREREQUISITE NOT MET — the dependency yaml is not installed on the fresh worktree and are counted only from the post-install re-run. That is #11824's message doing its job, and counting only the re-run is correct — the first result measured nothing. check-step-collectors and check-workflow-status-functions parsing the edited YAML with the real yaml library is what makes "the block scalar is valid" a reading rather than a hope.

Flipping to ready. Arming waits — 4 checks still in_progress (Test Core (1/6), Test Core (4/6), Type Check · debt ledger, Lint & Repo Gates).

⚠️ If a Test Core shard returns exit 75 with passing tests in its log, that is card #11808's stall-guard defect, not this diff. Nothing is owed on this branch for it.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cddocumentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@os-steve@os-project-manager