Repository navigation
feat(scripts): derive the body-dialect producer table from a SCAN under a written criterion - #9940
Merged
Merged
Conversation
…nder a written criterion The producer table was an enumeration, and every round added to it by hand after somebody noticed a site. Two live producers proved the classifier behind it is permanently blind in two directions rather than merely incomplete: it scores a child list only on an object that also carries a string-literal `type`, and it never reads inside a string or template literal. `scripts/body-dialect-producer-scan.mjs` states the criterion instead — C1 emission, C2 resolution, C3 carrier — and derives the table from it on every run. The criterion, its exclusions and its limits travel with the reading in both output modes. C2's reader set is derived per run rather than listed, so a registration that starts or stops resolving `body` joins or leaves it by itself. Both blind spots are closed and each closure is pinned against the census as a differential control, on the values the two implementations disagree about. An item-carried `body` is filed `unruled:item-carrier` and is never folded into objectui#6771's ruled total: the item face declares `content` and declares no `body` on either published face, so deciding it would extend a ruled family rather than apply one. The instrument reports that question, it does not answer it. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq
…wo tree-wide blocks
The re-pointed `the platform SHIPS the dialect it is being asked to refuse`
block timed out on CI shard 3 at the default 15s. The gap was cost, not
correctness: the block runs two tree-wide instruments, and the scan half was
doing avoidable work.
Three cuts, none of which narrows the corpus — every file is still walked and
still read:
- a cheap NECESSARY CONDITION (`CANDIDATE_FILE`) gates the expensive
per-character analysis. 1,852 files contain the substring `body`; 500 can
hold a hit under the criterion, so 61% of the bytes never reach
`scanSource`. The superset argument is written beside the regex, one line
per hit shape, and pinned per shape with a negative control.
- ONE directory traversal, shared by the reader derivation and the producer
pass, instead of two.
- ONE masked projection instead of two. The brace stack and the key tokens
differ only at literal-flagged offsets, which the `literal` flag already
marks, so `walkFrames` reads the structural character as a space wherever
the flag is set and the second `blank()` pass over every candidate file
goes away.
Verified unchanged, not assumed: the whole-tree hit set is identical across the
projection change — total 1092, unique 1083, the same 9 duplicated keys —
measured against a reconstruction of the previous implementation in one command.
Measured, load-normalised against the untouched census in the same command: the
scan went from 2.05x the census to 1.06x, and the block from 10.1s to 7.9s.
The remaining cost is inherent to a tree-wide character scan, so the two blocks
that run one get an EXPLICIT per-test timeout naming the measured duration and
the reason. No global `testTimeout` was touched, and no block was skipped,
quarantined or narrowed.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq
os-tesla
marked this pull request as ready for review
September 18, 2026 19:58
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Sep 28, 2026
…is not a dialect (objectstack-ai#10087) Fixes objectstack-ai#9871 `Clause-②: no` — an internal scanner's disposition string and the pins that read it. It moves no exported symbol and no key on a published payload. ## What this is The second flight on objectui#9871. The first one's code landed (PR objectstack-ai#9940, `Part of` on purpose, while the family question was still open). The director seat then answered it — **summon objectstack-ai#25 class-1 item 2, letter C**, 2026-09-20: > A `body` child list on a non-node ITEM (tab item, list item) is ⛔ not inside objectui#6771's ruled family and ⛔ not a second family: it is a producer violating the item's published schema, fixed at the producer (done: objectui#9941). The scanner's `unruled:item-carrier` disposition is renamed to a ruled, non-dialect disposition … and leaves any pending list; objectui#6771 step 4's landability is judged on the node face only. This PR is that rename and its pins. ⛔ The scan, the criterion and the census are ⛔ not re-opened. ## The spelling, and why it is not neutral `dispositionOf('item')` is now **`ruled:not-a-dialect/item-schema-violation`**. The name carries the answer rather than leaving a reader to guess it, and the ruling is cited **where a reader MEETS the disposition**, not only in the file header: - beside the constant (`dispositionOf`'s docblock); - in the `KNOWN_LIMITS` entry — id `unruled-item-carrier` becomes `item-carrier-not-in-ruled-total` — which **every run emits in both output modes**; - in the note the text mode prints under the dispositions table. Each of those three states in one line what the site actually IS: a producer violating the item's own published schema (`TabItemSchema` declares `content` and declares no `body`; `ListItem` likewise), fixed at the producer. ## The behavioural half, which is not a string - **It leaves every pending list.** The header section「The open question this scan REPORTS and ⛔ does not answer」is gone; its replacement states the ruling. The `KNOWN_LIMITS` entry no longer says「unruled … reports the question rather than answering it」, and the printed note no longer asks it. An `item` hit awaits no seat. - **The arithmetic did ⛔ NOT change.** An item carrier is still never folded into objectui#6771's ruled total. What changed is the reason — not that family at all, rather than not-yet-judged. - **Step 4 landability: already right, ⛔ not "fixed".** Read before changing, as the order required: the scan does ⛔ not compute or report objectui#6771 step 4's landability. `finalVerdict`'s docblock already disclaimed it in as many words (「It is NOT … the day objectui#6771 step 4 becomes landable」), and item carriers were already out of the ruled bucket. Only the prose moved, to state the node-face-only rule. ##⚠️ The hazard this rename introduces, pinned in the same act Both answers now begin `ruled:`. A reader that bucketed on that **prefix** would fold the item carrier straight into the ruled total — the one arithmetic the ruling says must not weaken. The discriminator is the **carrier**, never the prefix, and a new pin holds the two buckets apart on exactly that (`new Set(byDisposition).size` is 2 while both rows start `ruled:`). ## Evidence **RED before / GREEN after, file state proved in the SAME command.** The pins were written first and run against the byte-identical scanner: worktree blob `b51f13aaa` == `HEAD:scripts/body-dialect-producer-scan.mjs` `b51f13aaa`, and the run failed **4 of 26** — `expected 'unruled:item-carrier' to be 'ruled:not-a-dialect/item-schema-viola…'`, the two-bucket equality, the `KNOWN_LIMITS` id lookup (`expected undefined to be truthy`) and the CLI payload's id list. After the scanner change, blob `d42f6ac95` != HEAD: **2 files, 48 tests passed**. **The scan itself, before and after, both buckets lit.** On a planted corpus carrying BOTH carriers in one run, so the split is a discrimination and not an artifact of a one-shaped corpus: | disposition | before | after | |:--|--:|--:| | `ruled:6771` (node, the control) | 1 | 1 | | `unruled:item-carrier` | 1 | — | | `ruled:not-a-dialect/item-schema-violation` | — | 1 | On the **live tree**, nothing moved and nothing was supposed to: 0 producers before and after, with the same-subject controls identical in both runs — 5,189 files scanned, 1,067 candidate `body` spellings, 312 held out by C2, 5 passing C2 with no recognised emission channel. ⭐ Those controls are the point: a zero from a corpus that could not have held the subject would be a broken instrument, and this card exists because a classifier was provably blind in two directions. **Premise re-taken before any edit**, on `origin/main` `0c2eb5eee`, each leg with its control in the same command: the criterion header carries `C1 — EMISSION` / `C2 — RESOLUTION` / `C3 — CARRIER`; `unruled:item-carrier` was present at 8 sites in 3 files while the control spelling `ruled:6771` lit 4 sites in the same command (so the instrument distinguishes *this* spelling from *any* disposition); the shipped `tabs` `defaultProps` spell `content`, objectui#9941 is closed, and the producer-side repair the ruling rests on really is landed. **Gates, re-run as the last action before the push**, at `5d81f6cda` with `git diff HEAD` empty and nothing untracked: `check:control-bytes`, `check:entry-guard`, `check:new-line-citations`, `check:comment-mask-corpus`, `check:unreferenced-sources`, `check:test-path-roots`, `check:esm-specifiers`, `check:changeset-claims`, `check:pending-changeset-literals`, `check:changeset-presence`, `check-hand-rolled-comment-mask`, `check:vi-mock-specifiers`, `check:lint-rule-coverage`, `check:required-check-set --self-test` — all exit 0. Tests behind the shared verify lock (`VERDICT command-exit 0`). **Changeset: decided by the gate, ⛔ not from memory.** `node scripts/check-changeset-presence.mjs` at `5d81f6cda`: `No source or published contract of a released package changed in this range, so no changeset is owed.` (3 files changed, 0 of them published source.) **ESLint narrowing, declared with its three readings** at `5d81f6cda`: `eslint --no-inline-config --format json` over the 3 changed files — ② the JSON carries 3 file entries, 0 errors, 0 warnings; ① the population is read from `eslint.config.js` itself, whose 471 lines configure no type-aware linting (`projectService` / `parserOptions` / `project:` grep exits 1, with `languageOptions` / `rules:` lit as the control in the same command); ③ therefore this diff cannot move the verdict on any untouched file. The repo-wide `pnpm lint` run is CI's. ## Acceptance notes ⛔ No third file reads the disposition string. The census pin `scripts/__tests__/body-dialect-census.test.ts` is one of the scanner's pins under `scripts/__tests__/` and is inside the declared surface; `package.json` and `.changeset/9871-body-dialect-producer-scan.md` name the *script*, never the disposition, and are untouched.⚠️ The census pin's item-carrier assertion stays **vacuous on the live tree** — the item-carried set has been empty since objectui#9941, so the loop body never runs. It is updated to the new spelling and kept, ⛔ not deleted, exactly as its own comment instructs: it fires the moment an item-carried producer returns. The instrument that actually failed before this change is the producer-scan pin set, which is where the new assertions live. `noted, not filed:` `packages/components/src/renderers/layout/tabs.tsx` still reads `item.content || (item as any).body` — a lenient consumer fallback for the spelling the producer no longer emits. It is a **reader**, so the criterion correctly scores it as not a producer, and it is also what keeps C2's item leg resolvable. Retiring it is a consumer-side decision on the item face, ⛔ not this card's rename, and ⛔ not a dialect question under the ruling above. Successor: whoever holds the item face on objectui#9590. --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq --- _Generated by [Claude Code](https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq)_ Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #9871
Part of, ⛔ notFixes— changed fromFixesby thedomain:ui#2execution seat at 2026-09-18T19:06Z (clock read by this write), ⛔ not by the dev. The code deliverable here is complete and independently landable, but this card also carries a question the dev handed back under 回翻条款: is anitem-carriedbodyinside objectui#6771's ruled family? Triage ⛔ forbade splitting that question into a third card — 「⛔ 不该再拆出第三张」 — so it has to stay on objectui#9871, and an auto-close would take it off the board with the question unanswered. The seat moves the card to the decision box after this merges.⭐ The first deliverable is the CRITERION, not another count. This PR adds no
entry to a list. It writes down what counts as a producer of the
bodychild-list dialect, and makes the table an ANSWER to that criterion, computed on
every run.
The criterion
scripts/body-dialect-producer-scan.mjs— stated in its header, emitted byevery run in both output modes, and pinned in
scripts/__tests__/body-dialect-producer-scan.test.ts. A site is a producer iffC1 and C2 hold; C3 is the axis the answer is filed under.
bodyto appear inmetadata a party OUTSIDE this tree then owns. Channels:
default-props,scaffold,snippet. ⭐ A channel is recognised by a KEY PATH or by anEMISSION VERB in the enclosing function — ⛔ never by a file name, which would
be the enumeration this scan replaces, one level up.
bodyoff thatcarrier INTO A CHILD LIST. ⭐ The reader set is DERIVED per run, never listed:
a registration that starts or stops resolving
bodyjoins or leaves it byitself. C2 is APPLIED, not merely derived — it held 313 shipped-source
bodyspellings out of the table on this branch, and the count is PRINTED so the
drop is never silent.
node(aBaseSchemanode — objectui#6771's ruled family),item(a member of a declared item array — ⛔ UNRULED, see below), orstring(written into the author's document; the carrier is theirs, so the scan
reports no family disposition rather than guessing one).
⛔ Excluded, each machine-readable and emitted beside the reading: a
DECLARATION (
inputs: [{ name: 'body' }], a published type member) teaches thespelling and writes nothing; a READER consumes rather than emits; a
bodynoderived reader resolves fails C2; authored corpus and teaching are the dialect's
VICTIMS, bucketed apart and never summed in.
The two blindnesses, closed — each proven by ablation
The census declares both limits, so ⛔ this is not a bug report against it. They
are the two directions a classifier keyed on "object with a string-literal
type" is PERMANENTLY blind in.typeis required.typeis recorded as a carrier FACT,of ANY value. A
bodydirectly underdefaultPropsis node-carried even withno sibling
type, because the node is the registration and itstypeis theregistered key — requiring a sibling there is B1 again one frame up, and it is
the shape objectui#7181's three
defaultPropsproducers had.scanSourcepass from theshared masker (
scripts/js-comment-mask.mjs, ⛔ never a private stripper),projected twice: comments-and-literals blanked for the structural walk,
comments-only blanked for the key sweep. The
literalflag is then what tellsa code key from a spelling inside a string.
Measurements — my own corpus, ⛔ not inherited
Read on⚠️ the dispatch named
origin/mainf5e2fcb4a16ae1b80b81db1dc890399c01a58c6aat2026-09-18T18:20Z (
02bc27b08as the tip; it hadmoved). Producer table on this branch: 6 sites,
pnpm census:body-dialect-producers.packages/components/src/renderers/layout/tabs.tsx88 / 89 / 90unruled:item-carrierpackages/vscode-extension/src/providers/CompletionProvider.ts108 / 133 / 146carrier-undeterminedBoth sites re-verified at source.⚠️ Two corrections to the dispatch: the
snippet string is at 146, not 145 — 145 is the
ifthat guards it; and thesame scan resolves a third item-carrier reader the card does not mention,
packages/components/src/renderers/data-display/list.tsxdrawing each entry asitem.content || renderChildren(item.body).⭐ The shipped
tabsdefaultPropsitem FAILS its own published item contract.Measured against the built
TabItemSchema, with a lit control in the samecommand: the shipped item
{ label, value, body }givesinvalid_type ["content"] expected nonoptional, received undefined, while thesame item spelled
contentparses green with keysvalue, label, content.Ablation — mutate, prove by blob hash, run, restore, prove by hash equality and an empty
git diff HEAD⭐ Each leg is taken on a value the two implementations DISAGREE about, ⛔ not on
the headline value. Baseline 6 producers at
ccad335e5, scanner blob6211cdc04c9f.carrierOfrequires a siblingtype(the census's rule)tabsrows goneliteral-key-syntaxgoneCompletionProvider:146goneCompletionProvider:108/133goneLeg 2 is reported as observed rather than as predicted: inverting the guard made
the sweep re-score code-position keys the frame walk already had, so the
direction was mixed. 2b is the clean form and both are kept.
Every leg restored: blob back to
6211cdc04c9fandgit diff HEADempty.Is an
item-carriedbodyinside objectui#6771's ruled family? The scan filesit
unruled:item-carrierand REFUSES to fold it into the ruled total. Answeringit would EXTEND a ruled family rather than apply one, so 回翻条款 applies and it
returns to the decision box. The measurements behind that, each taken here:
TabItemdeclarescontentREQUIRED and declares nobody;ListItemdeclares
contentoptional and nobody. Neither published face — TS or Zod— carries the key at all, so step 3 ("retire
bodyfrom the published type")has no subject on the item face.
unknown-proponbody— the one child-list key 10+ registrations actually honour #6771's remedy is "childrenis the one spelling". The item face's canonicalchild-list key is
content, ⛔ notchildren, so step 2's remedy names a keythe item face does not have.
bodyonly through ananycast(
(item as any).bodyintabs,items.map((item: any) …)inlist)."that is the ITEM channel, filed under
ListItemand not underListSchema,the same shape as
tabs."What this changes downstream
The
the platform SHIPS the dialect it is being asked to refuseblock inscripts/__tests__/body-dialect-census.test.tsused to name the two sites byhand. It now reads the derived table, with two lit controls before it and its
handoff condition restated: when the table empties, that is the day objectui#6771
step 4 becomes landable — re-point the block, ⛔ do not delete the claim. Leg 4
proves the block is live rather than decorative.
Verification
pnpm exec vitest run scripts/__tests__/body-dialect-census.test.ts scripts/__tests__/body-dialect-producer-scan.test.ts— 41 passed, behind the seat lock.check:control-bytes,check:entry-guard,check:new-line-citations,check:comment-mask-corpus,check:unreferenced-sources,check:test-path-roots,check:esm-specifiers,check:changeset-claims,check:pending-changeset-literals,check:lint-rule-coverage,check:required-check-set,check-hand-rolled-comment-mask,check-changeset-presence.eslint --no-inline-configover the three changed files,--format json: 3 files, 0 errors, 0 warnings. The narrowing is sound for this diff: no rule in this repo's config is type-aware across files, and the diff touches no package source, so it cannot move any untouched file's verdict.check:node-esm-load— it refused 17 of 37 entries on PROVENANCE, because the shared turbo cache replayed a sibling worktree's artifacts (/home/user/objectui-issue-9889) into this one. Environmental, and outside this diff, which touches no package source.Generated by Claude Code
Generated by Claude Code