Skip to content

feat(scripts): derive the body-dialect producer table from a SCAN under a written criterion - #9940

Merged
os-tesla merged 2 commits into
mainfrom
claude/issue-9871-body-dialect-producer-scan
Sep 18, 2026
Merged

os-tesla merged 2 commits into
mainfrom
claude/issue-9871-body-dialect-producer-scan

Conversation

@os-tesla

@os-tesla os-tesla commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

Part of #9871

⚠️ Part of, ⛔ not Fixes — changed from Fixes by the domain:ui#2 execution 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 an item-carried body inside 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 body
child-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 by
every run in both output modes, and pinned in
scripts/__tests__/body-dialect-producer-scan.test.ts. A site is a producer iff
C1 and C2 hold; C3 is the axis the answer is filed under.

  • C1 — EMISSION. The site causes the literal key body to appear in
    metadata a party OUTSIDE this tree then owns. Channels: default-props,
    scaffold, snippet. ⭐ A channel is recognised by a KEY PATH or by an
    EMISSION VERB in the enclosing function — ⛔ never by a file name, which would
    be the enumeration this scan replaces, one level up.
  • C2 — RESOLUTION. Some reader in this tree resolves a body off that
    carrier INTO A CHILD LIST. ⭐ The reader set is DERIVED per run, never listed:
    a registration that starts or stops resolving body joins or leaves it by
    itself. C2 is APPLIED, not merely derived — it held 313 shipped-source body
    spellings out of the table on this branch, and the count is PRINTED so the
    drop is never silent.
  • C3 — CARRIER. node (a BaseSchema node — objectui#6771's ruled family),
    item (a member of a declared item array — ⛔ UNRULED, see below), or string
    (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 the
spelling and writes nothing; a READER consumes rather than emits; a body no
derived 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.

  • B1 — no sibling type is required. type is recorded as a carrier FACT,
    of ANY value. A body directly under defaultProps is node-carried even with
    no sibling type, because the node is the registration and its type is the
    registered key — requiring a sibling there is B1 again one frame up, and it is
    the shape objectui#7181's three defaultProps producers had.
  • B2 — string and template literals are READ. One scanSource pass from the
    shared 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 literal flag is then what tells
    a code key from a spelling inside a string.

Measurements — my own corpus, ⛔ not inherited

Read on origin/main f5e2fcb4a16ae1b80b81db1dc890399c01a58c6a at
2026-09-18T18:20Z (⚠️ the dispatch named 02bc27b08 as the tip; it had
moved). Producer table on this branch: 6 sites, pnpm census:body-dialect-producers.

file:line carrier channel disposition
packages/components/src/renderers/layout/tabs.tsx 88 / 89 / 90 item default-props unruled:item-carrier
packages/vscode-extension/src/providers/CompletionProvider.ts 108 / 133 / 146 string snippet carrier-undetermined

Both sites re-verified at source. ⚠️ Two corrections to the dispatch: the
snippet string is at 146, not 145 — 145 is the if that guards it; and the
same scan resolves a third item-carrier reader the card does not mention,
packages/components/src/renderers/data-display/list.tsx drawing each entry as
item.content || renderChildren(item.body).

⭐ The shipped tabs defaultProps item FAILS its own published item contract.
Measured against the built TabItemSchema, with a lit control in the same
command: the shipped item { label, value, body } gives
invalid_type ["content"] expected nonoptional, received undefined, while the
same item spelled content parses green with keys value, 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 blob
6211cdc04c9f.

leg mutation predicted OBSERVED
1 carrierOf requires a sibling type (the census's rule) item carriers vanish 6 → 3, the three tabs rows gone
2 literal-key sweep INVERTED target gone 6 → 8 — target gone AND three duplicates, the guard does two jobs
2b literal-key sweep DISABLED literal-key-syntax gone 6 → 5, CompletionProvider:146 gone
3 key-name-datum sweep disabled those two gone 6 → 4, CompletionProvider:108/133 gone
4 leg 1's mutation, running the census test file the re-pointed block REDS 1 failed / 21 passed, by its own message

Leg 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 6211cdc04c9f and git diff HEAD empty.

⚠️ The family question is HANDED BACK, ⛔ not answered here

Is an item-carried body inside objectui#6771's ruled family? The scan files
it unruled:item-carrier and REFUSES to fold it into the ruled total. Answering
it 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:

  • TabItem declares content REQUIRED and declares no body; ListItem
    declares content optional and no body. Neither published face — TS or Zod
    — carries the key at all, so step 3 ("retire body from the published type")
    has no subject on the item face.
  • finding(sdui-parser/components): the manifest tier reports unknown-prop on body — the one child-list key 10+ registrations actually honour #6771's remedy is "children is the one spelling". The item face's canonical
    child-list key is content, ⛔ not children, so step 2's remedy names a key
    the item face does not have.
  • Both item reads reach body only through an any cast
    ((item as any).body in tabs, items.map((item: any) …) in list).
  • objectui#9256 filed the ITEM channel SEPARATELY and pinned it LIVE on purpose:
    "that is the ITEM channel, filed under ListItem and not under ListSchema,
    the same shape as tabs."

What this changes downstream

The the platform SHIPS the dialect it is being asked to refuse block in
scripts/__tests__/body-dialect-census.test.ts used to name the two sites by
hand. 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.
  • Gates green: 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-config over 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.
  • ⚠️ NOT MEASURED: 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.
  • ⛔ Draft on purpose. ⛔ Not flipped ready, ⛔ no auto-merge — landing is the seat's act.

Generated by Claude Code


Generated by Claude Code

…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
os-tesla marked this pull request as ready for review September 18, 2026 19:58
@os-tesla
os-tesla added this pull request to the merge queue Sep 18, 2026
Merged via the queue into main with commit ff4ffe4 Sep 18, 2026
37 checks passed
@os-tesla
os-tesla deleted the claude/issue-9871-body-dialect-producer-scan branch September 18, 2026 20:18
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants