docs(plugin-kanban): compile every README snippet, leave the UNGATED_DOCS ledger - #8147
Conversation
…DOCS ledger Batch 26 of the objectui#5174 burn-down. `packages/plugin-kanban/README.md` leaves `UNGATED_DOCS` and all five of its ts blocks now compile against the built types. The page's entire debt was PARSE debt -- 6 diagnostics over 2 codes (TS1109 x5, TS1011 x1) in exactly two blocks, and the removed ledger entry's reason string described it accurately. Schema API block: the leading pseudo-object was an interface-shaped props table that TypeScript could not parse. It is now a real binding annotated with the SHIPPED `KanbanSchema`, with `columns` bound through the page's own `KanbanColumn` interface -- so that interface is load-bearing instead of a local shape that compiles green whatever it says. The two documented interfaces are untouched and still match the shipped types 7 keys of 7 each. Example with Callbacks block: the elided `columns: [...]` becomes a `declare const` stand-in typed from the shipped surface, and the binding is annotated `KanbanSchema`, so the four `onCardMove` parameters are now contextually typed by the shipped signature rather than implicit any. No fragment marker was added: declared fragments stay at 158. No packages/** source touched, no public type widened, no gate loosened -- the gate file's only change is the two lines of that one ledger entry, and the strictness region hashes identically on both sides. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
…y checks
Probe P4 falsified the claim the previous commit's comment made. Adding a key
the shipped `KanbanSchema` does not declare (`swimlanes: []`) to the annotated
board literal does NOT turn the gate red: `KanbanSchema` extends `BaseSchema`,
which declares `[key: string]: any` ("This index signature allows type-specific
extensions", packages/types base.d.ts), so TypeScript's excess-property check
cannot fire on this type family at all.
What the annotation does check is measured and real -- the type of every member
the shipped type declares. Probe P5 retypes the stand-in to `string[]` and gets
TS2322 "Type 'string[]' is not assignable to type 'KanbanColumn[]'"; probe P3
deletes the stand-in and gets TS18004 on the shorthand property.
So the comment now states the narrower true thing and names the bound, instead
of promising a rejection the compiler will never deliver. A README that claims
a check nobody performs is the failure this gate exists to prevent, and it
would have shipped inside the very block this batch was repairing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
…ated-docs-batch26
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
|
Standing down on
Every other check on Generated by Claude Code |
|
Armed — squash auto-merge enabled and the PR is in the merge queue,
On MERGED: content probe on re-fetched Generated by Claude Code |
|
LANDED — merged at 2026-09-06T20:55:56Z as Content probe on re-fetched
#5174 stays open ( Generated by Claude Code |
Part of #5174 — batch 26 of the
UNGATED_DOCSburn-down.packages/plugin-kanban/README.mdleaves the ledger and every ts block on thepage now compiles against the built types. The gate file's only change is the
two lines of that one entry.
Notation, read this first
This repository has measured that GitHub's body sanitizer deletes tag-shaped
fragments even inside backticks and fenced blocks. So generic parameters are
spelled out in words below — "an Array of badge objects", "the KanbanColumn
array" — rather than in their real angle-bracket form. The README itself
carries the real spellings; this page does not.
Concurrency
mainmoved under this batch once, and it was absorbed by merging (neverrebasing, never force-pushing).
535eb224fto21d7989fb— one PR, #8143, the objectui#6892 slice 6 thedispatch named: 27 test files, the vi-mock gate and a changeset. It touches no
packages/**source, so the built types this corpus is judged against did notmove; even so the forced build, the census, every gate, every test and the
load-bearing probes were re-run on the merged head, and every reading came back
byte-identical.
mainwas re-checked after the merge and had not moved again atpush time, so no second cycle was needed and no gap is declared.
All 14 open pull requests' file lists were read over the REST files endpoint and
each count reconciled against that PR's own
changed_files: 8144/2, 8143/29,8142/4, 8109/1, 8104/2, 8091/1, 8090/1, 8076/6, 7749/4, 7685/69, 7058/24,
7054/2, 7053/2, 5400/1086. #5400's 1086-file list was paged to exhaustion
because the endpoint pages at 100 and a truncated list renders as a confident
absence. No open PR touches
packages/plugin-kanban/README.mdorscripts/check-doc-snippet-types.mjs. The two open PRs that touch any packageREADME are both #7685, on
packages/auth/README.mdandpackages/plugin-list/README.md— the first is exactly the standing exclusion,so it doubles as the positive control that this grep can find a hit at all.
Live E2E (informational)is red on every branch today for an upstream reason(objectui#7990 / objectstack#16186), not because of this diff.
Ledger arithmetic
Both rows MEASURED by a real gate run, never computed. Base row at the merged
base
21d7989fbbytes (probe M0, both paths checked out to the main blobs andproven equal by hash, restored under a trap); branch row at
b0ea6a7f8.21d7989fbb0ea6a7f8UNGATED_DOCSentriescheck:readme-exports)That last row is here because it is the one a reader would expect to move and it
does not: this repair adds no documented-type declaration for the pin to
compare (5 documented types in 4 blocks on both sides). What it adds is three
self-import bindings.
Programmatic diff of the two
UNGATED_DOCSobjects — both parsed as OBJECTS,never diffed as text: removed =
['packages/plugin-kanban/README.md'],added =
[], reason-changed =[], survivor key order preserved = true.git diff --numstaton the gate file against the merge base is exactly0 2.On the branch: kanban entries 0,
packages/NAME/README.mdentries 10(from 11), control
'packages/auth/README.md'1.The strictness region — the
Fence scanningbanner line to EOF, 1720 lines onboth sides — hashes to
5dfcd1b876c044ef33b6b5fed8a57ec719622ddbb20ce0ab539e5c985a233017on the mergebase and on the branch. Re-measured here on both sides and not inherited, which
mattered this time: objectui#7864 (PR #8138) added a census mode to that region
hours before this batch, so the value batches 15 through 25 recorded
(
f46b5662…) is no longer the right one.The removed entry's reason string was ACCURATE. It claimed "6 parse
diagnostic(s) — blocks fenced
tsthat are bare object literals or elidedbodies". Measured by probe P2: exactly 6, all of them parse diagnostics, and
both offending blocks are exactly that shape — a bare object literal and an
elided
[...].Census and the rung that decided
Re-taken on this batch's own base through the gate's exported
analyze()/compileSnippets(), one document un-gated at a time, over every non-excludedledger candidate.
Rung 1's first term decides outright:
packages/plugin-kanban/README.mdisthe only remaining candidate with 5 blocks; every other one has 4 or fewer.
Batch 25 predicted exactly this, and the prediction held.
Assumption A1 is CONFIRMED — every block total, diagnostic total and
per-code figure matches batch 25's census exactly. Zero bound refusals on this
page.
Exclusions re-verified against GitHub on this base, not inherited: the root
README.mdis not apackages/NAME/README.md;packages/auth/README.mdisheld by open draft PR #7685;
packages/plugin-chatbot/README.mdis deferred bythe earlier ruling.
The parse-debt decisions, block by block
This page is the first in the series whose debt is entirely parse debt, so
each block got a decision rather than a repair. Five blocks; three already
compiled and were not touched.
Block at line 105, the Schema API — an interface-shaped props table. This is
the block the ruling anticipated: a bare object literal fenced
ts, carrying?optionality markers and inline comments, that TypeScript cannot parse (4 ofthe 5 TS1109 plus the TS1011 come from here). Three shapes were available:
cannot be checked, and this one can — the page is documenting a type this
package actually ships.
interfaceit pretends to be. Refused, and this is theinteresting refusal. The only honest name for it is
KanbanSchema, which isan export of this package — so
check-readme-exports's interface pin wouldthen compare it against the shipped declaration in both directions and
report a stale-omission for each of the seventeen members the section does not
document. Naming it something else instead (
KanbanBoardDocument) would dodgethe pin by teaching readers a type name that does not exist. Either way the
rewrite buys a worse page.
KanbanSchema. Taken.So the section is now
const board: KanbanSchema = { … }with a realself-import, and
columnsis supplied by adeclare conststand-in typedKanbanColumn[]— bound through the page's ownKanbanColumninterface,which is the load-bearing detail. That interface used to be a local declaration
inside a block that never even parsed; it now has to be assignable to the
shipped type or the gate goes red. Probe P6 is what proves that.
The two
interfacedeclarations below it (KanbanColumn,KanbanCard) areuntouched and stay in the same fence. They still match the shipped types 7 keys
of 7 each, with 0 fabricated and 0 omitted, and
badgeskeeps thecolorClassand
colorStylemembers PR #8134 landed there at 18:21Z.Block at line 185, Example with Callbacks — an elided body.
columns: [...]is a spread with no operand: the sixth TS1109. The ruling's remedy for an elided
body is the smallest real value or a
declare conststand-in typed from theshipped surface; the stand-in is right here, because the point of the example is
that your columns go in that slot. Annotating the binding
KanbanSchemadoesa second thing worth naming: the four
onCardMoveparameters used to beimplicit
anywaiting to become TS7006 the moment the block parsed, and theyare now contextually typed by the shipped callback signature instead.
Fragment count: 158 to 158. No marker was written, so no new reason claims a
block cannot be checked.
What the annotation actually checks — and the claim probe P4 falsified
Probe P4 was predicted to go red and did not, and the correction it forced
is the most useful thing this batch measured.
KanbanSchemaextendsBaseSchema, which declares[key: string]: any("This index signature allows type-specific extensions"). So TypeScript's
excess-property check cannot fire on this type family at all — adding
swimlanes: [], a key the shipped type does not declare, to the annotated boardliteral leaves the gate green.
The first commit's comment claimed "an invented or renamed key … fails here".
That was false, and it would have shipped a promise of a check nobody performs
inside the very block this batch was repairing — the exact failure this gate
family exists to prevent. The second commit narrows it to what is measured: the
type of every member the shipped type declares is checked (probes P3, P5, P5b,
P6), and an unknown key is not, with the reason named so a reader knows where
the boundary is.
This bound is not new and nothing is filed for it:
check-readme-exports.mjsalready states it in its own docblock — "
BaseSchemacarries an index signatureand its Zod mirror is passthrough, so no amount of type checking rejects an
invented schema key. That needs a third instrument, not a wider version of this
one."
Probes — a base measurement and eight probes, each predicted in writing first
Predictions were written to the scratchpad before any probe ran and were not
amended:
5174-b26-PREDICTIONS.md(md5bc4a0250f8e532120ab2cf6b10ff8421) atimplementation commit
4a43188d9with a clean tree. Every mutation is proven ondisk by occurrence counts of both the injected and the deleted text plus
git hash-objectagainst the HEAD blob; every restore is proven by an emptygit diff HEAD; every leg runs under atrap … EXIT INT TERMwith absolutepaths, restoring with
git checkout HEAD --against an absolute path ratherthan a bare checkout from the index. No dist preflight is owed, and that is a
measurement rather than an omission: both mutated paths are read from disk by
the gate itself, so no rebuild sits between a mutation and its reading.
swimlanes: [], a key the shipped type lacksBaseSchema's index signature disables excess-property checking. Re-run a second time with a clean anchor to be sure the first was not a no-op; same result. Drove the second commitcardstoitemsin the README's OWNinterface KanbanColumnpackages/plugin-kanban/README.md:106P6 is the load-bearing one for this batch, because it is what makes the
repair shape worth more than a fragment marker. Renaming one key in the README's
own interface turns both gates red, and they say different things:
check:doc-snippetsexit 1 — TS2322, "Property 'cards' is missing in type'KanbanColumn' but required in type … complex.KanbanColumn", at README line
120. The page's own interface is now checked against the shipped type
through the annotated binding. Before this batch it was a local declaration
in a block that did not parse: it compiled green whatever it said.
check:readme-exportsexit 1 — the interface pin reports the rename as thepair its docblock says only the two directions together can see:
itemsas afabricated-key and
cardsas a stale-omission, both at line 128.So the two gates now cover this declaration from two angles — names, and types.
That is strictly more than the ungated entry it replaces, which could have lost
either in silence.
Two predictions were falsified, both reported above rather than smoothed, and
P4's falsification is the reason the second commit exists. M0, P1, P2 and P6 were
re-run on the merged head and came back byte-identical.
Gates — all at
b0ea6a7f8, clean tree, nothing pushed afterExit codes captured by redirect-then-capture, before any pipe; each row quotes
the gate's own verdict line. The build was forced first
(
pnpm exec turbo run build --filter=./packages/* --concurrency=2 --force,39 successful / 0 cached) and again inside the provenance gate (43 successful /
0 cached); every dist-sensitive gate was then re-run on that force-built dist.
Note the invocation: bare
turbois not on PATH in this container, sopnpm exec turbois what ran.pnpm check:doc-snippetsexit 0 — "Scanned 227 document(s): 216 covered (114 of them hold a ts/tsx block), 11 ungated" / "Covered blocks: 735 — 577 to compile, 158 declared fragment(s)" / "Syntax phase: every block parsed, so every one of them reached the semantic phase" / "Root bound: no block imports a specifier that resolves only through this repository's ROOT manifest" / "Semantic phase: 577 of 577 block(s) judged, 0 failed" / "Every covered documentation snippet compiles against the built types."pnpm check:readme-exportsexit 0 — "502 of them self-imports judged (502 real, 0 wrong-path, 0 fabricated)" / "58 key(s) compared both ways (0 fabricated, 0 stale omission(s)"pnpm check:doc-fencesexit 0 — "every TypeScript block in 227 document(s) is fenced ts/tsx/typescript"pnpm check:doc-typesexit 0 — "Every documented component type is registered."node scripts/check-doc-links.mjsexit 0 — "Links are valid across 17 scan roots."pnpm check:doc-example-readersexit 0 — "OK 80 documented symbol(s), 3947 call site(s)"pnpm check:control-bytesexit 0 — "OK (scanned 6509 tracked text file(s); skipped 85 binary)"node scripts/check-node-esm-load.mjs --force-buildexit 0 — "Provenance leg: 37 of 37 gradable entries were built by this tree." Green on its first run, so no gate had to be re-run over a replayed sibling cache.pnpm type-check:scriptsexit 0node scripts/check-changeset-presence.mjsexit 0 — "2 file(s) changed, 0 of them published source of a package the release covers … no changeset is owed." Noskip-changesetlabel applied: this repository declares with an empty-frontmatter changeset rather than that label, and the presence gate says none is owed, so nothing was labelled at all by this seat.node scripts/check-governed-queue-guard.mjs --teston both paths exit 0 — "NOT GOVERNED — 2 path(s) checked against 5 governed surface(s); none matched"pnpm exec vitest run packages/plugin-kanban/— "Test Files 24 passed (24) / Tests 126 passed (126)";pnpm --filter @object-ui/plugin-kanban type-checkexit 0, its output echoingtsc --noEmit && tsc -p tsconfig.test.json, so A4 is confirmed — the test tsconfig is genuinely included and the script really ran.grep -naPover the C0/DEL ranges found nothing (exit 1, zero output bytes).Lint, with the narrowing declared and its three evidence items.
pnpm linthere is
turbo run lintper package, so the repository-wide sweep is CI's run;the narrowed run over exactly this diff is
pnpm exec eslint --no-inline-config --format jsonon both paths — 2 entries,0 errors. eslint's own answer for the README is "File ignored because no
matching configuration was supplied", so the real lint surface of this diff is
the one
.mjsat 0 errors and 0 warnings. (1) The population eslint wouldotherwise judge is 4385 files by its own count. (2) That run exits 1 with
94 errors and 12223 warnings, and the touched files carry 0 of them —
measured, and reported rather than smoothed, because a reader must not mistake
the narrowing for a green repository. (3)
eslint.config.jsdeclares noproject,projectServiceorparserOptions.project(grep count 0), sotype-aware linting is off and this diff cannot move the verdict on any untouched
file. Both readings were taken on the final commit
b0ea6a7f8.Readers of the changed paths
Derived with
git grep -lon both paths on this head, not guessed — 17 testfiles, 614 tests, all green in one run, and the set was identical before and
after the merge.
A2 is CONFIRMED and quantified. The two named readers of the README are
scripts/check-readme-exports.mjsandscripts/__tests__/check-readme-exports.test.ts,and the pin's own JSON output gives the numbers the assumption asked for:
KanbanColumnat line 125 andKanbanCardat line 136, both verdictmatches,both 7 documented of 7 shipped, 0 fabricated, 0 omitted. A2's warning — that
rewriting a block as an
interfacemay ADD an interface the pin then compares —is exactly why the rewrite route was refused above; the pin still compares 5
documented types in 4 blocks and 58 keys, unchanged from the base. The remaining
15 readers read the gate file.
Out of scope
Nothing was filed, and that is a finding rather than an omission. The one
candidate is the
BaseSchemaindex signature that falsified probe P4. It isdeliberate and documented at the declaration ("This index signature allows
type-specific extensions"), and
check-readme-exports.mjsalready records theconsequence in its own docblock as a known bound needing a third instrument. So
this is the same class as batch 25's identity alias — intentional, documented
and already recorded — and filing it would have added noise to triage. It is
reported in the section above instead. No dedup search was run, because a search
with nothing to dedup is not a reading.
This PR is a DRAFT and stays a draft: this seat does not flip ready, does not
enable auto-merge, and touches no labels or assignee.
🤖 Generated with Claude Code
https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
Generated by Claude Code