Skip to content

Commit 4019e16

Browse files
claude[bot]os-project-managerclaude
authored
docs-drift: state the rule-carrying pairing no run can reach (#11853)
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. Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx Co-authored-by: os-steve <pm@objectstack.ai> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 387e231 commit 4019e16

3 files changed

Lines changed: 128 additions & 4 deletions

File tree

.github/workflows/docs-drift-check.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,45 @@ jobs:
260260
if (overbroad.length) limits.push(`**${overbroad.length}** anchor(s) matched too much of the corpus to be a work list: \`${overbroad.join('`, `')}\``);
261261
if (weak.length) limits.push(`**${weak.length}** name(s) were too generic to anchor anything (single lowercase words)`);
262262
if (bridge && bridge.measured && bridge.unreachable > 0) limits.push(`the SDK route bridge reached **${bridge.reachable}** of **${bridge.clientRows}** client-bound route-ledger rows — the other **${bridge.unreachable}** have no registrar \`path:\` tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: \`node scripts/docs-audit/affected-docs.mjs --bridge-coverage\``);
263+
// ── THE RULE-CARRYING PAGE AN EMITTER DIFF CANNOT REACH (#11434) ──────
264+
//
265+
// Every line above is a REPORT about this run: a count this run produced, a
266+
// set this run derived. This one is not — it is a BOUNDARY of the anchor
267+
// model, and it is why it is pushed unconditionally.
268+
//
269+
// The pair it describes: a page states a rule by the rule's INPUTS, and the
270+
// code that carries the rule is its EMITTER. They share no identifier, so
271+
// precision-first anchoring (#9192) has nothing to join them with, and no
272+
// per-run signal can be computed — the absence of a shared token is the whole
273+
// defect, and an absence anchors nothing. Reported here rather than detected
274+
// because there is nothing to detect on.
275+
//
276+
// Measured, twice, on the specimen from #11434. On the recorded diff (#11430,
277+
// `sql-driver.ts` `createColumn`, re-run through today's mapper): 9 anchors
278+
// derived, 7 pages listed, and `content/docs/protocol/objectql/types.mdx` —
279+
// the page that diff falsified in four places — listed by none of them. Not
280+
// by the coarse fallback either: the page never names the changed package. On
281+
// today's tree an emitter-only change to `MAX_KEYABLE_VARCHAR_CHARS` derives
282+
// 1 anchor, lists 0 pages, and leaves `anchorlessChanges`, `crossCuttingSymbols`,
283+
// `overbroadAnchors` and `weakAnchorsDropped` all EMPTY — so no line above
284+
// fires, and the run reads as narrow rather than blind. Positive control on
285+
// the same tree and the same page: a change to `FieldSchema`, which that page
286+
// does name, lists it via `FieldSchema (symbol)`. The page is reachable; this
287+
// pairing is not.
288+
//
289+
// ⛔ NOT a detector, and must not grow into one here. The routes that would
290+
// buy real coverage — a curated code↔page ledger, a per-type anchor
291+
// vocabulary, authored enumeration marks — are the open question escalated as
292+
// #11817, and deciding the cheap half of it by implementation would pre-empt
293+
// that ruling. This says the gap exists; it does not close it.
294+
//
295+
// ⛔ And it is deliberately NOT the `notCovered` headline's posture. That
296+
// sentence is conditional because it reports what THIS diff left unanchored,
297+
// and unconditional text there would report nothing (see
298+
// `scripts/docs-audit/check-drift-comment.mjs`). This one is true on every
299+
// run by construction, exactly like the SDK bridge line above, and stating it
300+
// only sometimes would be the lie.
301+
limits.push('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.');
263302
// A broken scan is not a coverage number and must not read like one — see
264303
// `bridgeCoverageFrom`. Rendered outside the collapsed section below, because
265304
// "the bridge could not read the ledgers" is not a limit of this run, it is a
@@ -277,6 +316,11 @@ jobs:
277316
// say is "the wide net exists and holds N pages". A short list is the right
278317
// answer here, but a reader must be able to tell a short list from a blind one
279318
// without leaving the PR.
319+
//
320+
// Since #11434 `limits` is never empty — the rule-carrying line above holds on
321+
// every run — so this fold now renders on every run too. The guard stays: it is
322+
// what keeps the fold from rendering empty if that line is ever moved out, and
323+
// an empty "what this run could not see" is the reading it exists to refuse.
280324
const limitsBlock = (limits.length || coarse.length)
281325
? ['', '<details><summary>What this run could not see</summary>', '', ...limits.map(l => `- ${l}`), ...(limits.length ? [''] : []), `Coarse fallback — **${coarse.length}** page(s) merely *mention* a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): \`${rederive}\` → \`packageMentionDocs\`.`, '</details>']
282326
: [];

scripts/docs-audit/README.md

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -450,10 +450,52 @@ to re-verify.
450450

451451
The comment also carries a collapsed **"What this run could not see"** section:
452452
anchorless files, cross-cutting symbols, over-broad anchors, the coarse package-mention
453-
count, and the `sdk` bridge's reach over the client-bound ledger rows (#9572). That is
454-
the point-of-use half of #9192 — every one of the three derived-list failures in that
455-
shift was caught only because a dev widened the probe past what the tool offered, never
456-
because the tool signalled its own limits where it was read.
453+
count, the `sdk` bridge's reach over the client-bound ledger rows (#9572), and the
454+
rule-carrying pairing no run can reach (#11434, below). That is the point-of-use half of
455+
#9192 — every one of the three derived-list failures in that shift was caught only because
456+
a dev widened the probe past what the tool offered, never because the tool signalled its
457+
own limits where it was read.
458+
459+
### The rule stated by its inputs, the diff touching its emitter (#11434)
460+
461+
Every other entry in that fold is a **report about the run** — a count it produced, a set
462+
it derived. One is not, and renders on every run:
463+
464+
> a page that states a rule by its **inputs** shares no identifier with the **emitter**
465+
> that implements the rule, so an emitter-only diff cannot list it — not on this run and
466+
> not on any run.
467+
468+
A page can document a rule in the rule's own **vocabulary** — the values it maps *from*
469+
while the code carrying the rule is named for what it *does*. The two share no token, so
470+
precision-first anchoring has nothing to join them with. This is not a tuning miss with a
471+
threshold behind it: the absence of a shared identifier **is** the defect, and an absence
472+
anchors nothing, so no per-run signal exists to compute. It is reported because it cannot
473+
be detected — the same posture as the `sdk` bridge line above.
474+
475+
Measured on the specimen (#11434, from PR #11430 / card #11374), re-run through today's
476+
mapper:
477+
478+
| | |
479+
|---|---|
480+
| the diff | `packages/drivers/driver-sql/src/sql-driver.ts``createColumn`, the text family's column mapping |
481+
| derived | 9 anchors (`SqlDriver`, `createColumn`, `keyableTextLength`, …) |
482+
| listed | 7 pages, every one via a `SqlDriver`-family symbol it merely mentions |
483+
| **not listed** | `content/docs/protocol/objectql/types.mdx`**the page that diff falsified, in four places** |
484+
| coarse fallback | misses it too: the page never mentions the changed package |
485+
| existing limit lines | all silent — `anchorlessChanges`, `crossCuttingSymbols`, `overbroadAnchors`, `weakAnchorsDropped` all empty |
486+
487+
The last row is the reason this is stated rather than left to the reader: with every limit
488+
line silent, the run reads as *narrow*, not *blind*. Positive control on the same page and
489+
the same tree — a change to `FieldSchema`, which the page **does** name, lists it via
490+
`FieldSchema (symbol)`. The page is reachable; that pairing is not.
491+
492+
**This states the gap; it does not close it.** Buying real coverage would take a curated
493+
code↔page ledger, a per-type anchor vocabulary, or authored enumeration marks — the open
494+
question escalated as **#11817**, whose ruling also owns the population question (how wide
495+
this class is is deliberately **not measured here**; the sentence's truth does not depend
496+
on the count). `check-drift-comment.mjs` pins the line byte-exact on **every** fixture
497+
case, which is the only meaningful pin for a statement whose contract is that it is always
498+
true — making it conditional, moving it to the headline, or dropping it all go red.
457499

458500
### The headline says what the run did not cover (#11357)
459501

scripts/docs-audit/check-drift-comment.mjs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131
* UNCONDITIONALLY, which is not a report at all. A blind-spot notice that renders on every
3232
* run says nothing about any run.
3333
*
34+
* ⚠️ That reasoning is about REPORTS, and since #11434 the fold also carries one line that
35+
* is not one: a BOUNDARY of the anchor model, true on every run by construction, because
36+
* the pair it names (a page stating a rule by its INPUTS, a diff touching the rule's
37+
* EMITTER) shares no identifier for any per-run signal to be computed from. Its
38+
* unconditionality is the contract, so it is pinned the only way that is meaningful — on
39+
* EVERY case, byte-exact, and in the fold rather than the headline. Removing it, making it
40+
* conditional, or promoting it to the headline all go red. See `RULE_CARRYING_BLIND_SPOT`.
41+
*
3442
* So both directions are measured end to end, from a git diff:
3543
*
3644
* diff → the real affected-docs.mjs → its real --json → the real comment script → bytes
@@ -201,6 +209,27 @@ const ANCHORS_DERIVED_NONE_NAMED =
201209
/** The phrase that tells that verdict apart from every neighbouring one. */
202210
const NARROWED_PHRASE = 'nothing to list';
203211

212+
/**
213+
* The #11434 boundary line, byte-exact — the one fold entry that is not a report about the
214+
* run, and the only one asserted on every case.
215+
*
216+
* The class: `content/docs/protocol/objectql/types.mdx` documents the text-family column
217+
* mapping by the ObjectQL type names it maps FROM, `sql-driver.ts` carries the mapping in
218+
* `createColumn`, and the two share no token — so the advisory listed seven other pages on
219+
* #11430 and not the one that diff falsified in four places. Nothing in the anchor model
220+
* can join that pair (the routes that could are escalated as #11817), so the run says so
221+
* instead of implying coverage by silence.
222+
*/
223+
const RULE_CARRYING_BLIND_SPOT =
224+
'a page that states a rule by its **inputs** shares no identifier with the **emitter**'
225+
+ ' that implements the rule, so an emitter-only diff cannot list it — not on this run and'
226+
+ ' not on any run. Measured on #11430: `content/docs/protocol/objectql/types.mdx`'
227+
+ ' documents the text-family column mapping by the ObjectQL type names it maps FROM'
228+
+ ' (`text` / `textarea` / `html`) while the diff changed `createColumn`; it went unlisted,'
229+
+ ' and it was the page that diff falsified, in four places. No shared token exists to'
230+
+ ' detect this on, so a rule your change carries has to be re-read by hand in the pages'
231+
+ ' that restate it.';
232+
204233
/**
205234
* `want` is the mapper contract each case rides on — asserted before any text is, so a
206235
* case that silently stopped exercising its branch fails here instead of passing there.
@@ -310,6 +339,15 @@ try {
310339
if (typeof c.narrowed !== 'boolean') throw new Error(`case ${c.id} does not declare \`narrowed\``);
311340
check(c.id, `the #11356 narrowed verdict ${c.narrowed ? 'renders here' : 'does NOT render here'}`,
312341
c.narrowed, headline.includes(NARROWED_PHRASE));
342+
// #11434 — the opposite pin to every other one in this file, and deliberately so. A
343+
// model boundary that renders only sometimes is false on the runs it skips, so the
344+
// assertion is "on every case", which is what fails if someone makes it conditional.
345+
// It belongs in the fold: the headline is where a run says what IT did not cover, and
346+
// a sentence true of all runs would dilute exactly that (#11357).
347+
check(c.id, 'the rule-carrying blind spot is stated in the fold, byte-exact',
348+
true, body.includes(`- ${RULE_CARRYING_BLIND_SPOT}`));
349+
check(c.id, 'and it stays out of the headline, which reports this run only',
350+
false, headline.includes('shares no identifier with the **emitter**'));
313351
c.expect(headline, body);
314352
}
315353
} finally {

0 commit comments

Comments
 (0)