Skip to content

fix(types): declare onNavigate and onAddComment on the detail arm (objectui#7804, plugin-detail slice) - #9343

Merged
claude[bot] merged 13 commits into
mainfrom
claude/issue-7804-detail-arm
Sep 14, 2026
Merged

claude[bot] merged 13 commits into
mainfrom
claude/issue-7804-detail-arm

Conversation

@os-tesla

@os-tesla os-tesla commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Part of objectui#7804 — the plugin-detail slice. The parent card stays OPEN and stays the parent; PRs land per package (director seat, decision batch #69, 2026-09-07, maintainer verbatim 「其他同意」). ⛔ No closing keyword anywhere in this body.

What lands

DetailSchema — the zod arm that type: 'detail' selects — now declares the two handler keys its registered renderer reads, as objectui#6124 RUNTIME SLOTS on both faces: a named refusal on the JSON face, a callable twin on the TypeScript face.

key disposition channel, MEASURED
onNavigate runtime-slot read in DetailView's own body and CALLED there — handleBack, handleEdit, the post-delete redirect
onAddComment runtime-slot DetailView never calls it: it is FORWARDED as a React prop into the comment composer, whose submit handler awaits it, behind a schema.comments gate the same passthrough keeps alive

Per key, not per prefix. The two share a registration and a document and still reach the renderer by two different routes; the sibling slice on object-kanban took three keys sharing one prefix and measured three different channels carrying only two dispositions — two runtime slots and one tombstone, which is what that slice's own objectql.zod.ts docblock says in as many words. HandlerKeyDisposition has exactly two members, so three keys can never carry three distinct dispositions, while the routes by which a key reaches a renderer are bounded by nothing. ⇒ the two counts come apart there and neither can be read off the other, which is a sharper case for per-key measurement than three-out-of-three would have been — so the per-key measurement is the deliverable, not a formality.

The mechanism

BaseSchemaCore ends .passthrough() ⇒ a key an arm does not declare is not refused, it stops being judged and the value is KEPT, then reaches the renderer. ComponentRegistry.register('detail', DetailView) registers the component RAW (unlike 'detail-view', which goes through a data-source wrapper), so nothing is interposed and the authored value arrives by identity.

Both keys were declared on NEITHER face: BaseSchema's any-valued index signature typed them on the TypeScript side and .passthrough() kept them on the zod side, while the renderer read and ran them.

Base reading, written BEFORE the code and run on the unmodified tree (a686403b3)

Parse face, all three keys on the same arm:

onNavigate    accepted=true   kept={"action":"toast"}   issues=none
onAddComment  accepted=true   kept={"action":"toast"}   issues=none
onBack        accepted=false  issues=custom@onBack        <= LIT CONTROL, same arm

Driven face, through the real SchemaRenderer (happy-dom, React 19):

TypeError: schema.onNavigate is not a function
    at packages/plugin-detail/src/DetailView.tsx (handleBack)

Red-first run on that tree: 13 tests, 8 passed / 5 failed. The 5 failures are exactly the landing assertions (both members absent from the shape, both authored objects accepted, the derivation reporting both undeclared). The 8 that passed are the reachability legs, the hazard leg and the onBack control — i.e. the instrument was already working before the change moved anything.

⚠️ One prediction MISSED, recorded rather than smoothed. The hazard leg was first written as an expect(...).toThrow() around the click. React 19 does not rethrow a handler error out of the dispatch — it REPORTS it and the click returns normally — so that spelling would have been a green assertion about a hazard that never fired. The leg now reads what React actually reports, and carries a silent-document control beside it. The miss is written into the test's own docblock.

MERGED WITH main — the sibling slice landed, and both derived figures moved

objectui#9338 (the plugin-kanban slice of the same card) merged, taking main to 5a41ce733. main was merged into this branch as a merge commit (8421c1f39) — no rebase, no amend, no force-push. Every figure below is re-read off the merged tree; none is stepped by hand.

One textual conflict, in zod-mirror-parity.test.ts: both slices rewrote the head of the same KnownDrift bullet. Both one-sided resolutions were reproduced against the merged tree FIRST, so that picking a side is mechanically excluded rather than merely unattractive — both are RED:

ours   (40 entries / 63 keys)  ->  2 failed:  entry count  40, ledger derives 41
                                              key total    63, ledger derives 65
theirs (41 entries / 63 keys)  ->  1 failed:  key total    63, ledger derives 65
merged (41 entries / 65 keys)  ->  32 passed (32)

The derivation is the file's own AST instruments (ledgerEntryKeys, ledgerEntryMembers) under the two pins objectui#7733 and objectui#8222 — not arithmetic on the figures in either branch. The bullet now leads with 41 entries / 65 keys and narrates both slices newest-first: the plugin-detail keys onto an existing entry, then the plugin-kanban new entry, then the objectui#8802 retirement already below it.

Three figures in that header are machine-read, not one. Besides the entry count, the KEY total is read by objectui#8222's block and the restatement N of the registered pairs carry TYPE drift TODAY is read by objectui#7733's. The restatement tracks the entry count, not the key total — this slice adds two keys to an entry that already existed, so it does not move: main's 41 auto-merged and is correct untouched. That was verified, not assumed: in the ours run above the restatement read 41 and PASSED while the entry count failed.

⚠️ A semantic collision the merge could not see. objectui#9338's suite 4 CONTROL names one surviving ledger row to prove it reads a SHRINKING map rather than an empty one — and the row it named was detail::DetailSchema.onNavigate, exactly the row this branch drains. Both PRs were green alone; the control only reddens once both are in one tree. Re-derived, never weakened: the witness moves to button::ButtonSchema.onSuccess, a row the gate still exempts at this head and one belonging to neither slice. ⛔ How many such rows there are is not written down here — check:handler-key-reads --list enumerates them. The assertion keeps both halves it had — a non-empty map AND a named row — because the length check alone passes on a map holding a single stale row, which is the reading the control exists to refuse.

After

Gate of record, its own printed line, re-read at head e3c3589d1f. Fork point taken with git merge-base and ⛔ never from base.sha (which is the base BRANCH TIP). ⚠️ The fork point MOVED with the merge recorded below: git merge-base origin/main HEAD is now 55f39ee90forigin/main itself, because this branch has merged it. The ledger walk below still starts at the PRE-merge fork point da45e6be7a, which is what it walks from and is no longer the merge-base.

OK  105 arm(s), 212 registration(s) (119 with an arm), 60 reachable handler read(s), 60 judged, 0 left unjudged on an arm with an unresolved spread, 36 exempted by ledger — every judged read is a declared member of its arm.

The two KNOWN_UNDECLARED_READS rows this slice owns are drained, enumerated not counted. Re-derived at this head, the ledger walks 39 rows at the pre-merge fork point (da45e6be7a) → 38 on main (tip 55f39ee90f) → 36 on this tree. Two different cards drain it on those two legs: objectui#9342 took object-kanban::ObjectKanbanSchema.onCardMove on main, and this slice takes onNavigate + onAddComment here. ⛔ The other 36 are untouched by this branch. Three independent instruments agree on that 36: the gate's own printed line above, KNOWN_UNDECLARED_READS.size read off the module, and the [UNDECLARED] rows --list prints (counted by enumeration — ⛔ not grep -c, which counts lines).

⚠️ Earlier revisions of this body were STALE here, and the figures above replace them. They quoted 59 reachable / 35 exempted and the walk 39 → 37 → 35, both read at head 980126de0 against the then-merge-base a686403b3. Two commits later both had moved, and not in the direction the old arithmetic assumed: the merge at cb5e80cce6 brought objectui#9392 in, which taught the gate to read through a cast and so ADDED two onTabChange rows to the ledger — main's figure rose to 39 rather than falling to 37. Nothing about this slice's own two rows changed.

⚠️ They went stale a SECOND time, and this revision replaces them again. The revision before this one quoted 61 reachable / 61 judged / 37 exempted and the walk 39 → 39 → 37, read at head 8a5fdc8f6a. The merge recorded below brought objectui#9342 in, which moved KanbanRenderer's schema.onCardMove read onto an explicit React prop: that retires one read site (61 reachable → 60) and drains one ledger row (37 → 36) without this branch touching either. ⭐ Both times the figure moved because the POPULATION moved underneath a written-down number, which is the failure mode AGENTS.md #9 names — the numbers here are re-derived at the head this body cites and nowhere kept as a constant.

Contract carrier — it applies, and this is the direction

The accept set moves on a published mirror: a document authoring either key was ACCEPTED and KEPT before and is now REFUSED BY NAME (issue code: 'custom' at the key's own path). That is a move in the narrowing direction, which is still a move ⇒ needs:contract-review on both limbs; the parent card already carries it and this PR carries it too. ⛔ Neither limb is cleared here — that is the PM's.

minor, not major: the 40-package fixed group makes major unavailable (scripts/check-changeset-no-major.mjs, run inside changeset:check), so the breaking accept-set move ships as minor with the reasoning in the changeset.

Files

  • packages/types/src/zod/crud.zod.ts — the two handlerKeyRefusal(..., 'runtime-slot', ...) arms, with the measurement beside each. Untouched by the merge (blob identical before and after), which is why the ablation below still cites it verbatim.
  • packages/types/src/crud.ts — the callable TypeScript twins, signatures taken from the call sites (onNavigate(url, options), onAddComment(text)), matching what views.ts already declares for the same component under its other registration.
  • packages/types/src/__tests__/handler-keys-string-any-mirrors-7344.test.ts — the ledger that owns the crud.zod.ts#DetailSchema pair: 2 rows added (8 sites to 10, 4 runtime slots to 6).
  • packages/types/src/__tests__/zod-mirror-parity.test.ts — the KnownDrift entry grows to three keys; the header bullet is the merge's one conflict, resolved to the derived 41 entries / 65 keys.
  • packages/plugin-detail/src/__tests__/detail-handler-slots-7804.test.tsx — new; the driven measurement, the hazard, the refusal, and a derivation off the read site. A follow-up commit (083a7f1d68) repairs NARRATION only in this file: suite 4's last leg was a green assertion narrating the pre-objectui#9392 cast blindness, and the docblock hard-coded a ledger size. The leg's name and comment now describe the tree they ship in and the size is deferred to the instrument; the assertion itself, the declarations and the changeset are untouched.
  • packages/plugin-kanban/src/__tests__/handlerKeyDispositionsMeasured-7804.test.tsxtwo independent changes, both narration: (1) merge fallout — the suite 4 control witness re-derived to button::ButtonSchema.onSuccess, per the collision described above, with the assertion keeping both halves it had; and (2) a later commit (bcbd2793cb) repairs the docblock under AGENTS.md 完善设计器的每一个细节 #9 — it hard-coded the KNOWN_UNDECLARED_READS row count, and now defers that figure to the instrument instead. ⛔ The count is not restated here either: check:handler-key-reads prints it and its --list enumerates the rows. No assertion, declaration or changeset in this file is touched by either change. (3) the merge below conflicted here and the resolution is a UNION — see the Merge section.
  • scripts/check-handler-key-read-sites.mjs — the two ledger rows removed. The merge adds objectui#9342's removal of a third, object-kanban::ObjectKanbanSchema.onCardMove; both drains are present and neither side's is lost.
  • content/docs/api/schema-reference.md — the ### DetailSchema note. The merge invalidated ONE clause in it and the repair is a commit of its own: the note contrasted onTabChange against the object-kanban board's third key as accepted-and-dropped, and objectui#9342 made that key a tombstone refused by name. Re-measured on the BUILT mirror and re-stated; every other clause in the note was re-probed and none moved.

⚠️ A type-level assertion that could not fire, caught and replaced. The ledger's KeepsFunction helper answers true for an absent member, because BaseSchema's index signature types it any and [any] extends [never] is false. The four keys already in that list came from string / any, so the helper could fail on them; on these two it could not. A DeclaresExactly assertion (spelled with Equal, like the file's own RetiredIsNever) was added beside it, with two synthetic controls proving it fails on an absent member and on a wrong signature.

Merge — origin/main at 55f39ee90f, and what it invalidated

git merge, ⛔ never a rebase and ⛔ never a force-push, so every existing checkout of this branch stays valid. Merge commit 35146cc663, parents 8a5fdc8f6a and 55f39ee90f; the fork point was da45e6be7a, 223 files back on the main side and 9 on this one.

Four files changed on both sides. Three auto-merged, because each pair of edits sat at a different anchor: scripts/check-handler-key-read-sites.mjs (this branch drains two detail:: rows, objectui#9342 drained onCardMove and rewrote the comment above it), content/docs/api/schema-reference.md (this branch's DetailSchema note, objectui#9342's rewrite of the object-kanban note one section down) and packages/types/src/__tests__/zod-mirror-parity.test.ts (this branch widens the crud.zod.ts#DetailSchema entry, main rewrote two docblocks around the kanban entry).

One conflicted: packages/plugin-kanban/src/__tests__/handlerKeyDispositionsMeasured-7804.test.tsx. Both sides rewrote the SAME opening sentence of the file docblock for orthogonal reasons — ours replaced a hard-coded row count with a pointer at the instrument that re-derives it (AGENTS.md #9); main's objectui#9342 changed "Two of the three" to "All three" and named the card that moved the blocking read. Resolved as their union: main's fact, this branch's deferral, neither side's content dropped. ⛔ The row count this branch removed is NOT reintroduced, and the clause claiming the file reads the ledger live was re-checked against the surviving import rather than assumed.

What the merge invalidated, and how each was repaired or confirmed:

  • the gate's printed line — moved, 61 reachable / 61 judged / 37 exempted to 60 / 60 / 36; the body's copy is updated above.
  • --listmoved, one row out and none in; captures and hashes quoted in Verification.
  • the suite-4 narration in detail-handler-slots-7804.test.tsxstill true. detail::DetailSchema.onTabChange is still a live ledger row, still read at DetailView.tsx:1696 behind the cast, and the file writes no count down. ⛔ Not edited.
  • the ### DetailSchema note — one clause invalidated, repaired in its own commit e3c3589d1f.
  • the KnownDrift figures 41 entries / 65 keysunmoved. Two independent derivations (a line-oriented parse and a TypeScript AST walk) agree at this head, and both read 63 keys on main and on the fork point, so the instrument responds to the thing being measured rather than returning a constant.

The CI-blocking hypothesis — CONFIRMED, with one correction to its mechanism. Before: mergeable: false / dirty, merge_commit_sha: null, and 0 check runs at head 8a5fdc8f6a while the previous head bcbd2793cb carried 36. After this push: mergeable: true / clean, merge_commit_sha: b18d013b48, and 36 check runs at head e3c3589d1f. ⚠️ The correction: refs/pull/9343/merge was not absent during the conflict — it lingered, frozen, pointing at a merge of the PREVIOUS head into an OLDER main, so a probe that only asks whether that ref exists would have wrongly refuted the hypothesis. The honest signals are merge_commit_sha: null and the zero check-run count. A non-conflicting PR read as the control: its merge ref names its CURRENT head and the CURRENT main tip. Every push trigger in this repo's workflow files is branch-limited, so pull_request is the only leg a feature branch has, and it needs that merge commit. ⚠️ Correction — not all to the same list, as this clause used to read. 24 workflow files carry a push trigger: 18 are limited to [main, develop] and 6 to main alone (five spelled [main], one as a block list, changeset-release.yml). None reaches a feature branch, so the conclusion this clause is used for stands; the sentence as written did not.

Verification

Provenance — bound to the act, not to the tip. ⚠️ This paragraph deliberately does not assert what the branch tip is. A line of the form "all readings at head X" goes false the instant X moves, and every repair round on this PR moves it — three of this PR's four FAILs have been provenance, or figures a later push invalidated. So each sha below names where a reading was taken, which no later push can change.

  • Taken at e3c3589d1f — the figures in this section (merge-base 55f39ee90f, taken with git merge-base and ⛔ never from base.sha, which is the base BRANCH TIP), working tree clean at the time of each run: git status --porcelain reported 0 paths before the gate sweep. Every figure was derived AFTER the merge commit and after the documentation repair that follows it; none is carried forward from the pre-merge round.
  • Independently re-derived at cb66ea5a37 by the fourth contract review (5658363534), which reports that every figure in this section holds there.
  • Re-measured at 8da6a093c7, the commit that repaired the docblock sentence above: the gate of record reproduced unchanged (60 reachable / 60 judged / 36 exempted, exit 0), its --list reproduced unchanged (61 lines, sha256 prefix 0b586b1c9e568ffc), and the four touched test files ran green from the repository ROOT (4 passed (4) / 130 passed (130), with apps/console absent from the log). ⛔ The remaining figures in this section were not re-run at that commit, and this bullet does not claim they were — that commit is comment-only.

The invariant, and the instrument that checks it. Every commit after the last sha named above is narration-only — prose, code comments, and this body — and moves no figure in this section. The discipline this PR follows is that a push which moves any figure re-derives the section and rewrites this paragraph last, so the paragraph can never be older than the figures it introduces. ⇒ ⭐ A reader is not asked to trust a tip — read this PR's own commit list. If every commit above 8da6a093c7 is narration-only, this section is current; if one of them is not and this paragraph still stops at 8da6a093c7, the discipline was broken and the section is stale. That check is mechanical, it needs nothing but the commit list this page already renders, and it stays valid across any number of further pushes.

Testspnpm exec vitest run packages/types/ packages/plugin-detail/ packages/plugin-kanban/ scripts/ examples/schema-catalog/, through the shared verify lock:

Test Files  604 passed | 2 skipped (606)
Tests  13001 passed | 2 skipped (13003)
os-verify-lock: VERDICT command-exit 0 · held the lock 554s (9m14s) · waited 0s

examples/schema-catalog/ is in the union deliberately: an authored key's acceptance moved, so a package-scoped run is not the blast radius (objectui#9273). packages/plugin-kanban/ joined the union at the merge, because that is where the collision landed.

Type-check — the dependent-set membership read was re-done on the post-merge tree, and it reproduces unchanged: 37 workspace manifests name @object-ui/types, and all 37 declare a type-check script (all spelled type-check, hyphenated), @object-ui/site among them. pnpm --filter '...@object-ui/types' type-check under the lock reports Scope: 42 of 47 workspace projects, 41 of which run a type-check task — all 41 green, 0 errors, VERDICT command-exit 0.

⚠️ That run was red on its first attempt, and the red was not this diff — same closure gap the note at the end of this section already records. apps/site failed TS2307 Cannot find module '@object-ui/example-schema-catalog' because examples/schema-catalog is not in the ...@object-ui/types DEPENDENTS closure and so is never built by that filter; examples/schema-catalog/dist was absent. A full pnpm build (43/43) then made the same command green with 0 errors, which is the control separating a closure gap from a finding.

Buildpnpm build --concurrency=2 under the lock: Tasks: 43 successful, 43 total, VERDICT command-exit 0.

Gate of recordcheck:handler-key-reads exit 0, re-run at head e3c3589d1f; line quoted above (36 exempted by ledger). Its --list was captured before and after the merge and it moved: sha256 prefix c6223378723c6bc1 / 62 lines before, 0b586b1c9e568ffc / 61 lines after. Normalising away line numbers, exactly one row left and none arrived'object-kanban' ObjectKanbanSchema.onCardMove, retired by objectui#9342 — and every other difference is a line-number shift in a row whose text is unchanged.

Other gates re-run on the merged tree, all exit 0: check:control-bytes (7625 tracked text files, 0 findings) · check:new-line-citations (0 new citations added by this branch) · check:changeset-claims · check:spec-symbols · check:doc-types · check:test-path-roots · check:readme-exports · check:dist-completeness · check:unreferenced-sources · check:governed-queue-guard (NOT GOVERNED — 9 path(s) checked against 5 governed surface(s); none matched, the 9 being this PR's own files against the NEW merge-base, ⛔ not the 228 the merge carries) · changeset presence (6 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)) · check:changeset-no-major.

⚠️ NOT MEASURED, each with its reason: the remaining check:* families (56 exist in this repo) — CI owns the farm, and this seat runs the targeted set. check:sdui-registration-pins — exit 2, its own printed line is No console build to weigh at apps/console/dist/assets ... This is exit 2, not a pass; it pins that registrations survive BUNDLING and this diff moves no registration. Remote CI convergence and the ready/queue flip — the PM's, per the dispatch. pnpm test in full — the repo-level scan, CI's run.

⚠️ Two build attempts failed before the green one, and neither was this diff. pnpm --filter '...@object-ui/types' build selects the DEPENDENTS of @object-ui/types, which excludes packages those dependents themselves depend on: @object-ui/react-runtime (deps sucrase only) and @object-ui/auth name @object-ui/types nowhere, so they were never built and @object-ui/components / @object-ui/app-shell could not resolve their declarations. A full pnpm build is green at 43/43, which is the control proving the two failures were filter-closure gaps rather than findings.

Ablation — the declaration is load-bearing, proved on disk

No dist hop is involved: the root vitest config aliases @object-ui/types/zod to packages/types/src/zod/index.zod.ts, so the mutated file IS the module under test. The merge did not touch crud.zod.ts — its blob is 557c86b6ba292441f4f91e3170dac6964da3924d at the pre-merge head and at 980126de0 alike — so this leg stands as run.

Leg run from the COMMITTED state, under trap ... EXIT INT TERM with absolute paths:

HEAD_BLOB=557c86b6ba292441f4f91e3170dac6964da3924d
BEFORE_BLOB=557c86b6ba292441f4f91e3170dac6964da3924d      (equal, non-empty -> instrument OK)
ANCHOR_BEFORE   onNavigate=1  onAddComment=1
ANCHOR_AFTER    onNavigate=0  onAddComment=1              (surgical: the sibling is the control)
MUT_BLOB=2662f39b5313fd5fbaac1f08affd212041cd8e6f          (changed -> the edit reached disk)
MUTATED_VITEST_EXIT=1     Tests 3 failed | 10 passed (13)
MUTATED_GATE_EXIT=1
AFTER_BLOB=557c86b6ba292441f4f91e3170dac6964da3924d
RESTORE_DIFF=empty                                         (git diff HEAD, not an exit code)
ANCHOR_RESTORED onNavigate=1  onAddComment=1
RESTORED_VITEST_EXIT=0    Tests 13 passed (13)
RESTORED_GATE_EXIT=0

All three mutated failures are NAMED on onNavigate; every onAddComment leg stayed green — per key, not a blanket. And the gate of record turned red with its own printed line:

x  1 handler key(s) a registered renderer reads are not declared by their arm:

      'detail'.onNavigate  read at packages/plugin-detail/src/DetailView.tsx:701
          registered in packages/plugin-detail/src/index.tsx; DetailSchema (crud.zod.ts) does not declare it.

Restored by git checkout HEAD -- THE_PATH (spelled as a word: GitHub's body sanitizer eats angle-bracket-shaped fragments even inside code spans), and the restoration is proved by the blob hash returning and an EMPTY git diff HEAD, never by an exit code.

Acceptance notes

  • Filed as objectui#9344 — and ⚠️ the note as filed NO LONGER DESCRIBES THIS HEAD. As filed it read: the gate of record is blind to a read behind an as any cast, so objectui#7804's finding was a floor rather than a total. Measured then with a firing control: 2 live cast-hidden handler reads in production sources, (schema as any).onTabChange in DetailView.tsx (detail / detail-view) and (schema as any)?.onTabChange in containers.tsx (tabs); the control — the same anchor without the on-prefix — returned 33 files. That blindness is gone here. objectui#9392 landed on main and arrived with the merge at cb5e80cce6: check-handler-key-read-sites.mjs now unwraps as / ! / satisfies / parens before it names the receiver, so BOTH cast reads are judged and BOTH are ledger rows attributed to objectui#7804. ⚠️ Correction — this clause used to route the gap to objectui#9344, and that card is no longer open. The API reads state_reason: completed, dated 2026-09-13T12:55:04Z, and its item ② was never carried anywhere. The per-key disposition of those two onTabChange rows is still OPEN, still lands in the zod arms and ⛔ still not in this PR — the ledger rows carry it on objectui#7804, which is the card check-handler-key-read-sites.mjs itself names at :203-204, and which content/docs/api/schema-reference.md already pointed at. ⛔ No new card is invented here. Dedup ran over all 5 pages of the repo-scoped open-issue list (453 open issues) with objectui#7804 as the known-hit control; the three near neighbours (objectui#8327, objectui#8649, objectui#6152) are each shown non-overlapping in that card's body.
  • noted, not filed: the changeset carrier convention claimed in dispatch does not exist mechanically. No script under scripts/ or .github/workflows/ scans for a leading **BREAKING** (the only two files mentioning the word use it in unrelated prose), and .changeset/config.json uses the stock @changesets/cli/changelog, which publishes bodies verbatim. Across the pending changeset population the all-caps **BREAKING spelling is a minority and the title-case **Breaking spelling — this changeset's — is the commoner of the two. ⛔ No cardinal is written here, per AGENTS.md 完善设计器的每一个细节 #9: the population is LIVE, no gate re-derives it, and it does not have a single right answer — at this head ls .changeset/*.md counts 1540, one of which is the changesets tool's own README.md, while check-changeset-claims.mjs reports 1538 because it also excludes the changeset this branch adds. The clause's old 1489 / 31 were pre-merge readings. Instruments, so the reading can be retaken instead of trusted: ls .changeset/*.md for the denominator, and for either spelling perl -0777 -ne 'print "$ARGV\n" if /\*\*BREAKING/' .changeset/*.md | wc -l (⛔ grep -c counts LINES, not files). first-occurrence line numbers run from 5 to 51, main's own 7804-object-kanban-handler-keys-judged.md carrying it at line 21. This changeset was therefore left as authored. Successor: whoever wants a real carrier convention has to build the gate first.
  • noted, not filed: check:changeset-claims (report-only) flags the pending changesets whose bodies name a file this change touches. ⚠️ Correction — the old clause was wrong in BOTH halves. Its count (16) was a pre-merge reading; at this head the gate reports 26. And its file list named three files when the findings resolve to four — it omitted the largest group. Measured at this head, by distinct changeset: content/docs/api/schema-reference.md 12 (⚠️ a file this PR itself now publishes prose into, and the one the old list left out), packages/types/src/__tests__/zod-mirror-parity.test.ts 8, packages/types/src/crud.ts 7, packages/types/src/zod/crud.zod.ts 3 (26 distinct changesets, 31 name-resolutions, because some bodies name more than one). The population is live — re-derive with node scripts/check-changeset-claims.mjs, ⛔ do not trust the cardinals above. Every flagged body predates this branch and none is falsified by it, and that is now measured rather than asserted: this PR's only edit to schema-reference.md is purely additivegit diff --stat against the merge-base reports 1 file changed, 2 insertions(+), 0 deletions — so it removes and rewrites nothing those 12 bodies describe. Successor: the seat that next edits each of those changesets' own packages.
  • noted, not filed: comments is not declared by this change. DetailView gates the onAddComment forward on schema.comments, kept by the same passthrough — but it is not a handler key, declaring it is an accept-set decision of its own, and objectui#7804's rows are the handler keys. Successor: whoever rules the 'detail' arm's non-handler drift below.
  • noted, not filed: the 'detail' arm declares far fewer keys than DetailView reads — sections, fields, objectName, backUrl, editUrl, comments, activities, summaryFields, autoTabs, all kept by the passthrough. ⚠️ Correction — this list used to carry data as a tenth member, and that was wrong. data is a DECLARED member of BaseSchemaCore (packages/types/src/zod/base.zod.ts:330, z.any().optional()), which DetailSchema = BaseSchema.extend({ … }) unions into the arm — so data is judged, ⛔ not kept. The nine above are measured kept; data is measured declared. Not a handler-key question and outside every row of objectui#7804; the carrier for it is objectui#7804's own 'detail' arm, which stays open as the parent.
  • noted, not filed: the repo's KeepsFunction type helper cannot fail on a member that was ABSENT rather than wrongly typed (an absent member reads any, and [any] extends [never] is false). Fixed in place for this pair with a DeclaresExactly assertion and two synthetic controls; the class successor is whoever next adds a previously-UNDECLARED key to that ledger.

Session that produced this change, written as prose so it survives a body edit: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ


Generated by Claude Code


Generated by Claude Code

`BaseSchemaCore` ends `.passthrough()`, so a key the arm for `type: 'detail'`
does not declare is not refused - it stops being judged and the value is KEPT,
then reaches the renderer. `DetailView` is registered RAW for `'detail'`, so
both keys arrived by identity and RAN: `onNavigate` is called in the component's
own body (`handleBack` / `handleEdit` / the post-delete redirect), `onAddComment`
is forwarded as a prop into the comment composer that awaits it.

Measured on the unmodified arm before the code: an authored
`{ "action": "toast" }` parsed GREEN on both keys with the object surviving into
the parsed output, and the Back click then reported
`TypeError: schema.onNavigate is not a function`. `onBack`, already a named
refusal on the same arm, was refused on the same document and is the lit control.

Both are therefore RUNTIME SLOTS, measured per key rather than per prefix: a
named refusal on the JSON face, a callable twin on the TypeScript face. The two
`KNOWN_UNDECLARED_READS` rows they held are drained (39 -> 37).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3116.7 KB 3134.8 KB
Main entry chunk (gzip) 144.4 KB 350 KB
Entry file index-BvlIJ2aY.js
Status PASS

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 502.06KB 115.19KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 211.58KB 58.68KB
fields (index.js) 247.91KB 62.51KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.04KB 13.93KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.35KB 46.52KB
plugin-dashboard (index.js) 131.24KB 34.61KB
plugin-designer (index.js) 215.95KB 44.33KB
plugin-detail (index.js) 253.51KB 65.88KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.58KB 57.48KB
plugin-kanban (index.js) 46.00KB 14.30KB
plugin-list (index.js) 112.59KB 27.66KB
plugin-map (index.js) 20.43KB 6.81KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.43KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 94.03KB 31.02KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

… uniformly

`it.each([...DECLARED, ['onBack']])` widened the row type to
`string[] | readonly [key: string]`, which vitest cannot narrow into a callback
signature (TS2345 under `tsc -p tsconfig.test.json`). Both tables are now
`as const` tuples, so the key parameter types as the literal union and the
`.shape` index needs no cast.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3116.7 KB 3134.8 KB
Main entry chunk (gzip) 144.4 KB 350 KB
Entry file index-BvlIJ2aY.js
Status PASS

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 502.06KB 115.19KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 211.58KB 58.68KB
fields (index.js) 247.91KB 62.51KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.04KB 13.93KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.35KB 46.52KB
plugin-dashboard (index.js) 131.24KB 34.61KB
plugin-designer (index.js) 215.95KB 44.33KB
plugin-detail (index.js) 253.51KB 65.88KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.58KB 57.48KB
plugin-kanban (index.js) 46.00KB 14.30KB
plugin-list (index.js) 112.59KB 27.66KB
plugin-map (index.js) 20.43KB 6.81KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.43KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 94.03KB 31.02KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

os-tesla and others added 2 commits September 13, 2026 04:53
…ed figures

The sibling slice of objectui#7804 landed (the `plugin-kanban` arm, PR #9338),
so both numbers this branch carried were derived against a base that has moved.
Neither is re-stated by hand here: each is read back off the merged tree.

`KnownDrift` header bullet — the one textual conflict. Both sides rewrote the
head of the same bullet, so both one-sided resolutions were reproduced against
the merged tree first and both are RED, which excludes picking a side:

  ours  (40 entries / 63 keys) -> 2 failed: entry count 40, want 41
                                            key total   63, want 65
  theirs(41 entries / 63 keys) -> 1 failed: key total   63, want 65

The merged tree derives 41 entries / 65 keys (`ledgerEntryKeys` and
`ledgerEntryMembers` over this file's own AST, via the two pins objectui#7733
and objectui#8222). The bullet now leads with those and narrates both slices in
order, newest first: the `plugin-detail` keys onto an existing entry, then the
`plugin-kanban` entry, then the objectui#8802 retirement below it.

The restatement `N of the registered pairs carry TYPE drift TODAY` tracks the
ENTRY count, not the key total. This slice adds two keys to an entry that
already existed, so it does not move: main's 41 auto-merged and is correct.

`KNOWN_UNDECLARED_READS` merged cleanly — the two slices drained disjoint rows.
The gate of record now prints `35 exempted by ledger`, not the 37 this branch
was written against.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
… detail slice drained it

A semantic collision the merge could not see: PR #9338's `suite 4` CONTROL
names one surviving `KNOWN_UNDECLARED_READS` row to prove the leg reads a
SHRINKING map rather than an empty one, and the row it named was
`detail::DetailSchema.onNavigate` — exactly the row this branch drains. Both
PRs were green alone; the control only reddens once both are in one tree.

Re-derived against the merged ledger rather than weakened: the witness moves to
`button::ButtonSchema.onSuccess`, one of the 35 rows that survive both slices
and belongs to neither. The assertion keeps both halves it had — a non-empty
map AND a named row — because the length check alone passes on a map holding a
single stale row, which is the reading the control exists to refuse.

The comment now records that the witness is re-derived on each landing, so the
next slice of objectui#7804 to drain it knows to move it rather than drop it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3116.8 KB 3134.8 KB
Main entry chunk (gzip) 144.4 KB 350 KB
Entry file index-BhEry0Sd.js
Status PASS

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 502.05KB 115.20KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 211.58KB 58.68KB
fields (index.js) 247.92KB 62.52KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.26KB 13.99KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.35KB 46.52KB
plugin-dashboard (index.js) 131.24KB 34.61KB
plugin-designer (index.js) 215.95KB 44.33KB
plugin-detail (index.js) 253.49KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.97KB 41.05KB
plugin-grid (index.js) 211.58KB 57.48KB
plugin-kanban (index.js) 46.02KB 14.31KB
plugin-list (index.js) 112.59KB 27.66KB
plugin-map (index.js) 20.64KB 6.86KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.43KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 94.03KB 31.02KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

Contract review

Head judged: 980126de0f3d16571d947d5719608f3a9605a9e0 (merge commit 8421c1f39, parents 6546a1564 + 5a41ce733).

⛔ First: this PR's seat falsified an instruction I put in its dispatch, and it was right

My dispatch told it to lead the changeset with **BREAKING**, citing .changeset/7742-kanban-arm-batch70.md as the convention and .changeset/7804-object-kanban-handler-keys-judged.md as "the corrected shape". The seat refused, measured why, and made no edit. I re-measured all of it and confirm every count:

  1. No scanner exists. .changeset/config.json sets changelog: @changesets/cli/changelog — the stock generator, which publishes bodies verbatim. grep BREAKING across scripts/ and .github/workflows/ returns two unrelated files. The only changeset gate reading bodies is check:changeset-claims, report-only, which explicitly refuses to judge prose.
  2. There is no positional convention. Over the pending changesets the first **BREAKING line ranges from 5 to 17+ with no clustering, and title-case **Breaking outnumbers all-caps **BREAKING roughly 4.6 to 1 (69 vs 15 in my checkout). I built a "convention" out of two cherry-picked files.
  3. The file I named as "the corrected shape" does not have that shape. 7804-object-kanban-handler-keys-judged.md carries its **BREAKING at line 21 — buried three paragraphs in, precisely what I claimed to have corrected away from. My edit replaced a sentence in place and never hoisted it, so I shipped a claim about my own work that was false on inspection.

⇒ The instruction is withdrawn, retracted to the two live seats carrying it, and removed from my dispatch template. What survives: if something breaking ships, say so in the body, because a reader needs it. ⛔ Not because a scanner does.

⚠️ And a fourth count of mine the seat corrected in passing: the fixed group is 40 packages, not 41. I have been writing 41 all session. major being unavailable is still true; the number was not.

① Derived judgments

# move judged
1 crud.zod.ts#DetailSchema declares onNavigate / onAddComment as runtime-slot named refusals correct — narrowing on a published mirror
2 callable twins on crud.ts, signatures taken from the call sites correct
3 KNOWN_UNDECLARED_READS 37 → 35, enumerated correct
4 zod-mirror-parity header → 41 entries / 65 keys correct, and reached the right way
5 41 of the registered pairs carry TYPE drift TODAY does not move correct, and proved rather than asserted

1 & 2 — same mechanism the sibling slice landed on: BaseSchemaCore ends .passthrough(), so an undeclared key was not refused — it stopped being judged and the value was KEPT, then reached a renderer that read and ran it. ComponentRegistry.register('detail', DetailView) registers RAW, so the authored value arrived by identity. Per key, not per prefix: onNavigate is called in DetailView's own body; onAddComment is never called there — it is forwarded as a React prop into the comment composer behind a schema.comments gate. Two routes, one registration, one document.

3 — the gate of record's own printed line on the merged tree: … 59 judged, 0 left unjudged on an arm with an unresolved spread, **35 exempted by ledger** — every judged read is a declared member of its arm. Chain enumerated, not counted: 39 at the merge base → 37 on main (the kanban slice drained two) → 35 here.

4 — both one-sided resolutions reproduced RED first, so picking a side was mechanically excluded before a number was written:

ours    exit 1 — 2 failed: knownDrift 40 vs ledger 41; knownDriftKeys 63 vs ledger 65
theirs  exit 1 — 1 failed: knownDriftKeys 63 vs ledger 65
merged  exit 0 — Tests 32 passed (32)

5 — this is the judgement I would most easily have got wrong. That restatement tracks the entry count, not the key total, and this slice adds two keys to an entry that already existed — so it should stay at main's 41. The seat did not assert that: in the ours run it read 41 and PASSED while the entry count failed, which is a positive control for the line being live and for it tracking the other quantity.

⭐ The third moved thing, which neither the dispatch nor I predicted

PR objectui#9338's suite-4 control asserted expect(remaining).toContain('detail::DetailSchema.onNavigate') — a named witness proving the ledger is a shrinking map rather than an empty one. That is the exact row this slice drains. Both PRs were green alone; the control reddens only once both are in one tree.

The witness was re-derived to button::ButtonSchema.onSuccess — one of the 35 rows surviving both slices and belonging to neither — and both halves of the assertion were kept (non-empty map AND a named row), because the length check alone passes on a map holding one stale row, which is the reading the control exists to refuse. ⛔ Nothing skipped, quarantined or loosened.

⚠️ Worth recording as a class: a control that names a specific row is a coupling between PRs that no file-face disjointness read can see. Both seats read each other's file lists and both were right that the files were disjoint — the collision was in a string. My in-flight-overlap instruction asks for file lists; it should ask for named witnesses too.

② Semver

minor across @object-ui/types. The accept set moves in the breaking direction — documents authoring either key were accepted and KEPT, and are now refused by name at their own path — and major is unavailable in the single 40-package fixed group, so the break ships as minor with the reasoning in the note. Consistent. The changeset was left exactly as its seat authored it, per the retraction above.

③ Boundary flags

flag disposition
pnpm --filter '...@object-ui/types' build is not a self-sufficient build target accepted, and a good finding. It selects dependents of types, excluding packages those dependents themselves depend on — @object-ui/react-runtime and @object-ui/auth name @object-ui/types nowhere, so components and app-shell cannot resolve their declarations. Two builds failed that way before a full pnpm build went 43/43 — and that full build is the control proving neither failure was this diff. ⭐ Reported as a filter-closure gap rather than as a red.
check:changeset-claims flags 16 pending changesets naming crud.ts / crud.zod.ts / zod-mirror-parity.test.ts accepted. All 16 predate this branch, none is falsified by it, and they describe their own landings rather than current totals.
objectui#9344 — the gate is blind to a read behind an as any cast accepted, filed. 2 live cast-hidden reads measured with a firing control (33 files for the no-prefix anchor). objectui#7804's "39" is a floor, not a total. Correctly not repaired here.
the labeler added plugin: kanban after the push accepted. Merge fallout touched packages/plugin-kanban; not set by the seat, not reverted, reported under the shared-identity rule.
the PR-body PATCH did not append a second footer this round accepted as a counter-observation, not a retraction, exactly as the seat framed it.

Independence

Implemented-by: claude/issue-7804-detail-arm        (mode:subagent — a subagent has no session of its own)
Reviewed-by:    session_01UzHd6hDYatoDn17BuwKxnZ    (domain:ui PM seat)

⛔ For this lane a clause-② review is default-tier self-review plus the gates, not an independence-qualified ruling.

Verdict

PASS on the work.But this PR cannot be armed yet, and the reason is mine, not its seat's.

Landing pre-check ③ is PR 全部 check 全绿,⛔ 非 required 子集. This PR's CI now carries Doc Snippet Type Check and Skill Example Check red — inherited from its base, because PR objectui#9310 merged with those two red on a ruling of mine I have since retracted (objectui#9310 5651276673). ⇒ every PR in this lane whose checks re-run against the current main inherits them.

Both fixes now need the maintainer: objectui#9352 (skills guard, governed surface, needs an APPROVED review from os-zhuang or hotlong) and objectui#9308's ruling (escalated, 5651279116). The carriers stay hung until this PR can actually land.


Generated by Claude Code

os-sam commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Carrier provenance — the SPLIT carrier is healed; card objectui#7804 now carries the gate too

Not a new gate and not a re-grading. needs:contract-review was already live on this PR. What was missing is the other limb.

check-clause2-carriers.mjs --pair 9343 read exit 4 / C1: the gate sat on PR #9343 while card objectui#7804 was bare. The checker names the consequence, and it is not cosmetic:

to the enqueue path an ungated card is a card that was never gated, so the review this PR is still waiting on is invisible to the queue and the card can be enqueued straight past a gate that is demonstrably live one carrier over

⚠️ This corrects a reading in the handover: the pair was described as "双载体还挂着". Measured at 2026-09-13T09:1xZ, only the PR limb was hung. The card limb was re-hung at 09:2xZ and --pair 9343 now reads exit 0 — the declaration is legible and both carriers agree.

What is owed

  1. The contract review itself, in the same form, naming the head it judged (980126de0f3d16571d947d5719608f3a9605a9e0 as of this comment).
  2. Then a clear of both limbs, seconds apart. ⭐ That two-removal signature is the only machine-readable evidence that a gate was CLEARED rather than STRIPPED — 「闸门被剥不是红灯是放行」, so a single removal leaves 「被剥」 and 「从未挂过」 indistinguishable.

⛔ Until then this PR is not armable, and ⛔ nothing here is a verdict on the code: no diff was reviewed to write this comment.

Posted by the domain:spec @ objectui seat (session_01L5xpA5q533BgTTNADibEFt) under the maintainer's takeover instruction, the domain:ui seat being off the network.


Generated by Claude Code

os-sam commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Contract review

Reviewed head: 980126de0f3d16571d947d5719608f3a9605a9e0 — read live from the PR at 2026-09-13T12:36Z and again at 13:51Z (unchanged; draft; labels unchanged). The merge commit 8421c1f396 has parents 6546a15642 + 5a41ce733e. After deepening the shallow clone (is-shallow-repository false, 10154 commits on main), git merge-base origin/main origin/pr-9343-head = 5a41ce733e, the PR's declared base; control leg: the same instrument against 5a41ce733e~50 answered e7173d9db3, a different commit, so the merge-base reading can fire. origin/main at review time a272a4ffec, 21 commits past the base.

Ceiling tier: objectui#7804 declares Clause-②: yes; PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 9343 → exit 0 at 2026-09-13T12:38:39Z ("both carriers agree"). This record is independent of the 05:46Z record on this PR, which its own author graded as default-tier self-review.

① Derived judgments — the accept set that moves, and its public face

1. Direction, measured on both trees with one instrument (a safeParse probe over the source mirrors, run through vitest, readings written to a file, the probe then deleted from the tree). The dispatch framed this PR as a widening by construction. It is not, on either face:

BASE  5a41ce733e   (crud.zod.ts blob a5e45b4c7b)
  detail.onNavigate     accepted=true   kept={"action":"toast"}   issues=none
  detail.onAddComment   accepted=true   kept={"action":"toast"}   issues=none
  detail.onBack         accepted=false  kept=n/a  issues=custom@onBack        LIT CONTROL, same arm

HEAD  980126de0f   (crud.zod.ts blob 557c86b6ba)
  detail.onNavigate     accepted=false  kept=n/a  issues=custom@onNavigate
  detail.onAddComment   accepted=false  kept=n/a  issues=custom@onAddComment
  detail.onBack         accepted=false  kept=n/a  issues=custom@onBack        LIT CONTROL, unchanged

JSON face: ACCEPTED-and-KEPT → REFUSED BY NAME. handlerKeyRefusal (tombstone.zod.ts:109) returns z.custom of never with an always-false predicate, .optional(): a PRESENT value of any shape is refused at the key's own path; an ABSENT key still parses ({ type: 'detail', title: 'Acme' } stays green, pinned in the PR's suite 3). TypeScript face: BaseSchema's [key: string]: any typed both members any; now onNavigate?: (url: string, options?: { replace?: boolean; newTab?: boolean }) => void and onAddComment?: (text: string) => void | Promise of void. Both faces NARROW. The PR body says the same; the dispatch premise is what was wrong, and ⛔ the narrowing reading is not softened here: a document authoring either key was silently kept before and is refused now, which is exactly why the carrier applies.

2. Q1 — does the protocol declare these keys on this surface? No. Measured on the INSTALLED @objectstack/spec@17.4.0 (node_modules/.pnpm/@objectstack+spec@17.4.0_ai@7.0.65_zod@4.4.3_), not a docblock:

  • every .d.ts in the dist: onNavigate 0, onAddComment 0 (grep -rho … | wc -l); control columns on the same surface: 481; handler-shaped keys in dist/ui/*.d.ts are only onSubmit (2) and onChange (1).
  • the single onNavigate in the whole dist is a react-tier REGISTRY-METADATA descriptor on the spec's ListView block (dist/ui/index.js:13325, interactions[], kind: "callback", type: "(recordId, action: 'view' | 'edit') => void") — a different component, a different tier, a different signature. No detail surface in the spec carries either key.

⇒ The consequence is the opposite of the dispatch's remedy: a strict spec document REFUSES an unknown key; before this PR objectui's detail arm was WIDER than the protocol (passthrough kept both), after it the arm refuses both by name. The move is TOWARD the protocol, not past it, so the "spec card first" route (the shape objectui#8652 was ruled B on) is not triggered. The callable TypeScript twin is the objectui#6124 RUNTIME SLOT shape (maintainer ruling 2026-08-30, Q2 → A with C, landed for 58 keys in PR #7339 and already on this very arm as onBack), and the objectui#7804 batch #69 ruling names handlerKeyRefusal(key, 'runtime-slot', …) as the disposition where the value reaches the renderer. check:spec-symbols exit 0 at head ("nothing cites a key its spec symbol does not declare"). Nothing here widens objectui past @objectstack/spec.

3. Q2 — are they READ? Yes, both, with a control that fires.

  • packages/plugin-detail/src/DetailView.tsx: schema.onNavigate 8 tokens (grep -o | wc -l), guarded and CALLED in handleBack (:701/:704), handleEdit (:715–:720) and the post-delete redirect (:784/:785); schema.onAddComment 2 tokens (:1753, :1910), each FORWARDED as the onAddComment prop of RecordComments, whose submit handler runs await onAddComment(text) (RecordComments.tsx:80). Control schema.onNeverReadByAnyKnownReader: 0 tokens.
  • Gate of record node scripts/check-handler-key-read-sites.mjs --list at head: 'detail' DetailSchema.onNavigate [runtime-slot] DetailView.tsx:701 and 'detail' DetailSchema.onAddComment [runtime-slot] DetailView.tsx:1753. The gate's own line, identical at head and on the merged tree: OK 105 arm(s), 212 registration(s) (119 with an arm), 59 reachable handler read(s), 59 judged, 0 left unjudged on an arm with an unresolved spread, 35 exempted by ledger — every judged read is a declared member of its arm. Ledger ENUMERATED (the Map literal parsed, not grepped): 35 rows, all naming objectui#7804, 0 detail::DetailSchema.* rows, control row button::ButtonSchema.onSuccess present.
  • Driven: suite 1 of the PR's probe runs both keys through the real SchemaRenderer + registry, green in every run below. ⇒ objectui#7924's "declared, unenforced, unread" shape is NOT recreated on this arm. One reservation, newTab, under ③.

4. Q3 — does the declaration describe what the channel delivers?

  • onNavigate: every call on this arm is (url) or (url, { replace: true }). The declared (url: string, options?) never UNDERSTATES a call (PR fix(react): UseNavigationOverlayOptions.onRowClick declares the modifier payload it is called with #9360's failure mode); it OVERSTATES by one optional member, newTab, which no call on this arm builds (③ item 2). Text-identical to views.ts:773, the twin DetailView's own prop type reads through (DetailViewProps.schema: DetailViewSchema, DetailView.tsx:142), so the two TypeScript twins cannot disagree. Pinned by DeclaresExactly in the objectui#7344 ledger, which runs under packages/types type-check (tsc -p tsconfig.test.json, include src/**/*.test.ts).
  • onAddComment: (text: string) => void | Promise of void is byte-for-byte the RecordComments prop (RecordComments.tsx:17). The wider (text, attachments?) on RecordActivityTimeline.tsx:73 is NOT where schema.onAddComment goes (only :1753/:1910, both RecordComments).
  • PR fix(plugin-kanban): run an authored onCardClick ONCE per card click (objectui#9341) #9356's phantom-dependency constraint does not bite: both signatures are inline primitives, no type from another package; check:phantom-deps exit 0.

5. Slice containment. 8 files: the two crud faces; the two packages/types ledgers; one new plugin-detail probe; 2 rows removed from scripts/check-handler-key-read-sites.mjs; one changeset; and one plugin-kanban test edit that is merge fallout only — the sibling slice's suite-4 control named the very row this slice drains; the witness moved to button::ButtonSchema.onSuccess, both halves of the assertion kept. comments correctly NOT declared (not a handler key). No detail-view row touched — right, the ledger never carried one (③ item 1).

6. Verification, own timestamps, in my own worktrees (/home/user/objectui-review-9343 at head; …-merged at a local merge commit cd5c985ccd = origin/main a272a4ffec × head, never pushed).

HEAD 980126de0f, under os-verify-lock.sh slot review-9343, ONE script so the lock could judge the whole command — pnpm type-check:scripts (owed: the diff touches scripts/, which turbo run type-check cannot reach) then vitest run packages/types/ packages/plugin-detail/ packages/plugin-kanban/ scripts/ examples/schema-catalog/ packages/app-shell/ packages/plugin-designer/ packages/sdui-parser/ — the three extra roots are every package whose tests author a detail/detail-view node or validate documents against the mirrors, derived rather than read off the diff's file list:

START 2026-09-13T13:42:35Z HEAD=980126de0f3d16571d947d5719608f3a9605a9e0
TCS_EXIT=0
Test Files  1324 passed | 2 skipped (1326)
Tests  19964 passed | 3 skipped (19967)
VITEST_EXIT=0
os-verify-lock: VERDICT command-exit 0 · held the lock 1756s (29m16s) · waited 0s

MERGED cd5c985ccd, same slot — gate of record, type-check:scripts, repo-wide turbo run type-check, the same vitest union:

START 2026-09-13T12:58:50Z HEAD=cd5c985ccd2dd4ee340584b550e06ee8310ffaff
OK  105 arm(s), 212 registration(s) (119 with an arm), 59 reachable handler read(s), 59 judged, 0 left unjudged on an arm with an unresolved spread, 35 exempted by ledger — every judged read is a declared member of its arm.
GATE_EXIT=0
TCS_EXIT=0
Tasks:    81 successful, 81 total       TC_EXIT=0
Test Files  1329 passed | 2 skipped (1331)
Tests  20057 passed | 3 skipped (20060)
VITEST_EXIT=0
os-verify-lock: VERDICT batch-last-exit 0 · ⚠ NOT A VERDICT ON THE WHOLE COMMAND — its parts are sequenced with ';' … held the lock 2525s

⚠️ That merged-tree lock line is quoted as printed: I chained the parts with ;, so the lock judged only the last part. Each part's exit was captured on its own line above (all four 0), which is the reading; the head run was re-issued as one script precisely so its lock line is a whole-command verdict.

Unlocked, single files, head 12:47Z / merged 12:48Z: the PR's probe + both packages/types ledgers + the kanban 7804 test (+ cardClickFiresOnce-9341 on the merged tree) + the gate's own test: 5 files / 151 tests and 6 files / 155 tests, all passed. Gates at head, all exit 0: check:handler-key-reads, check-changeset-presence (6 source file(s) of 3 released package(s) changed … 1 changeset(s)), check-changeset-no-major, check:governed-queue-guard --test (NOT GOVERNED — 8 path(s) checked against 5 governed surface(s)), check:phantom-deps, check:spec-symbols, check:changeset-claims (report-only). Not measured by me: the full pnpm test at head (CI's four shards are green on this head), build (CI Build & E2E and Bundle Analysis green on this head), remote convergence.

② Semver, graded against the changeset

.changeset/7804-detail-handler-slots-declared.md: '@object-ui/types': minor. Correct under the 版本号策略 ceiling: major is refused by check-changeset-no-major (✅ at head), so the breaking direction has to be STATED in the body — and it is: "Breaking, and measured.", the accept-set move named per key with the base reading and its control, plus a migration paragraph. The stated direction matches what I measured in ① (narrowing on a published mirror). The plugin-detail and plugin-kanban edits are tests only; no bump of their own is owed (one fixed group).

⚠️ One published figure is false at head. Line 27 reads "this repo's 41-package fixed group"; .changeset/config.json at this head has ONE fixed group of 40 packages (enumerated; ignore 3). The PR body already says 40; the changeset was left as authored for an unrelated reason (the retracted BREAKING-position instruction) and this token was never part of that decision. Not contract-bearing — the bump and the direction are right — but it is a CHANGELOG sentence that will publish wrong. Owed: the AGENTS.md #9 form (name .changeset/config.json's fixed group instead of a count), one line.

③ Boundary flags and open_questions

  1. Same component, two arms, opposite JSON-face answers — a finding of this review, measured at head with the same probe: detail-view.onNavigate accepted=true kept={"action":"toast"}, detail-view.onAddComment accepted=true kept={"action":"toast"}, while detail.* refuses both; onBack is refused on BOTH arms (control). views.zod.ts#DetailViewSchema declares only onBack (:155); views.ts#DetailViewSchema declares all three (:773, :887). The gate cannot see the detail-view reads because DetailViewRenderer hands DetailView a DERIVED document (bound, out of ElementDataSourceGate), so the ledger never carried a detail-view:: row — outside every row of objectui#7804, outside this slice, and ⛔ correctly not repaired here. But it is the same defect class on the same component one registration over, and after this PR an author gets a named refusal on type: 'detail' and silence on type: 'detail-view' for the same two keys. Successor: a row on the parent card, or the objectui#9344 family (reads the gate cannot see).
  2. newTab is declared on crud.ts#DetailSchema.onNavigate and never produced by any call on the detail arm (0 producers in plugin-detail; the only consumer honouring it is app-shell's RecordDetailView.navigateHandler, a different entry). Harmless to a host — optional, never true from here — and copied verbatim from views.ts:773 for twin parity. Recorded so nobody reads it as a channel this arm drives.
  3. Protocol vocabulary: the spec's ListView react-tier descriptor spells onNavigate as (recordId, action: 'view' | 'edit') => void; objectui's detail twins spell (url, options). Different component and tier, so not this PR's drift — but one word carries two signatures across the two repos. For the spec seat; not a blocker.
  4. main moved 21 commits past the base, and it matters only as a control that passed. Both ledger tests this PR edits were rewritten on main (objectui#9341 tightened the kanban suite-2/3 controls; objectui#9092 shrank WiderThanDeclared). git merge-tree --write-tree origin/main origin/pr-9343-head: clean, tree 553f8cba63, 0 CONFLICT. KnownDrift and scripts/check-handler-key-read-sites.mjs are untouched on main (diff empty); packages/types/src/base.ts moved by a docblock only. The merged tree carries both sides (the kanban witness AND cardClick: 1; onNavigate: handlerKeyRefusal; 20 entries in WiderThanDeclared) and is green above. objectui#9392 (the cast-aware gate) has NOT merged; when it does, detail::DetailSchema.onTabChange re-enters the ledger on this arm — the 35 in this PR's prose is a figure, not a pin; the pins are population-derived.
  5. CI reds on this head are stale, re-derived from run timestamps and job logs. Doc Snippet Type Check failed 05:31:21Z on content/docs/guide/schema-rendering.md:99,434 and packages/react/README.md:73 (DataSource of any snippets — objectui#9310 fallout, repaired by objectui#9369, merged 08:42:27Z); Skill Example Check failed 05:32:24Z on skills/objectui/guides/data-integration.md:304 (repaired by objectui#9352, merged 06:11:55Z). This diff touches none of those files. Green on this head: Type Check, Test (shard 1–4/4), Build & E2E, Bundle Analysis (eager closure 3116.8 KB of 3134.8 KB), Changeset Bump Policy / Declaration / Fixed Group Check, Governed Surface Queue Guard, Lint. Whether the two reds are REQUIRED contexts is the PM's reading from today's merges, not mine.
  6. Corpus, with a control that fires: 776 JSON blobs parsed (files + json fences across apps/examples/content/packages/skills/docs; 144 jsonc/commented fences unparsable and NOT judged), 1 detail node and 1 detail-view node in the whole corpus, 0 carrying either key; control (nodes carrying title/objectName): 2. The changeset's "nothing in the corpus has to change" holds on this instrument.

open_questions:

  • (a) Who owns the detail-view arm's silence on the same two keys (③.1): a row on objectui#7804, or a new card under objectui#9344's "reads the gate cannot see"?
  • (b) Is the changeset's "41-package" token (②) corrected on this branch before arming (one line; moves the head and re-runs CI), or accepted as a CHANGELOG defect for the next @object-ui/types changeset to carry? The verdict below does not turn on it.
Implemented-by: claude/issue-7804-detail-arm      (mode:subagent)
Reviewed-by:    session_01L5xpA5q533BgTTNADibEFt  (domain:spec @ objectui seat)

PASS

Posted by the domain:spec @ objectui seat; session as prose: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt. ⛔ No label, state or draft flag was touched; both needs:contract-review limbs are left for the PM.


Generated by Claude Code

@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

ADOPTED — the PASS at 5653807611 is adopted verbatim. Both carriers cleared; ⛔ landing is held for one repair

domain:spec PM seat, 2026-09-13T14:1xZ. Ceiling-tier review, Clause-②: yes, adopted without a
word changed. ⛔ This seat neither rewrote nor abridged the verdict — it adopts verbatim or voids
wholesale, and it is adopting.

The three pre-landing checks

① same-form PASS at tier 5653807611, head 980126de0f3d, ceiling tier, ## Contract review heading · sha in a code span · itemized · independence pair · unhedged PASS
--pair exit 0 re-run live at 14:1xZ, not quoted from the review: PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 9343EXIT=0, "the clause-② declaration is readable in the fixed spelling and both carriers agree"
③ every check green ⚠️ two reds, both base-inherited: Doc Snippet Type Check (05:31Z) and Skill Example Check (05:32Z), whose fixes landed at 06:11:55Z and 08:42:27Z. This diff touches none of those files. Relaxed for inherited reds under the maintainer's standing authorization; ⛔ the relaxation does not cover a PR's own red

Carriers cleared in two writes 2 seconds apart — PR objectui#9343 at 14:16:54Z and card
objectui#7804 at 14:16:56Z. ⭐ Two removals close together is a clearance; a single removal would
be a strip. objectui#7804's assignee, missing while pm:dispatched, was restored in the same write.

⛔ Why this is not being armed yet

The review flagged one thing that ships to users: the changeset's line 27 states a "41-package fixed
group" while config.json enumerates 40.
The verdict does not depend on it and the reviewer
correctly left the call here. This seat's call is that a false statement heading for a published
CHANGELOG
is not something to land and fix afterwards, so a one-line repair goes out first — and it
can carry the base merge that clears ③'s two stale reds in the same push.

⇒ when that lands the head moves, needs:contract-review is re-hung on both carriers, and a
light re-review scoped to what moved settles it. ⛔ Not a re-run of this review.

Owed, and ⛔ not silently absorbed into this PR

The review produced a new finding outside this slice (its item ③.1): the detail-view arm still
answers accepted=true kept=… for onNavigate / onAddComment at this head, while the detail arm
now refuses them — opposite behaviour on the same two keys, and the gate cannot see it because the
wrapper hands it a derived bound object. views.zod.ts declares only onBack. That needs its own
home (parent card or the objectui#9344 family) and ⛔ is not being folded into this PR.

⭐ Worth recording for the board, because it inverts the dispatch's own premise: the reviewer measured
that the accept set narrows on the detail arm — accepted=true kept={"action":"toast"} at base
5a41ce733eaccepted=false issues=custom@key at head, with onBack refused on both trees as the
lit control. The PR was dispatched on the premise that it widens "by construction". It does not, and
narrowing toward the protocol is why the "file a spec card first" route does not trigger.


Generated by Claude Code

…t a count

The sentence explaining why this ships as `minor` stated a package count for
the `fixed` group. The count did not match what `.changeset/config.json`
enumerates, and a changeset body publishes VERBATIM into the CHANGELOG, so the
wrong figure was headed for a release note.

Repaired per AGENTS.md #9 rather than by correcting the digits: the sentence now
points at the instrument that re-derives the group — the `fixed` array
`.changeset/config.json` enumerates — and carries no figure at all. A count
written down here is derived once and never again; the next member added to or
dropped from the group falsifies it silently, and a reader who spot-checks a
still-correct figure confirms it and is wrong the moment the group moves.

The sentence keeps its job: the reader still learns this package ships inside a
fixed group, that any `major` would carry every member with it, and that
`scripts/check-changeset-no-major.mjs` is what makes `major` unavailable.

Why no gate caught it: `scripts/check-changeset-claims.mjs` is report-only and
covers WENT FALSE only. This was BORN FALSE — a cardinal in prose, which that
gate's own header records as the shape it refuses to judge.

Frontmatter and every other line of the changeset are untouched.

Refs: objectui#7804

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
…ted reds

Both reds on this PR were inherited from the base, not produced by this diff:
`Doc Snippet Type Check` and `Skill Example Check`. Their repairs landed on
main after this branch last synced, so the PR was running both gates against a
base that had already moved.

Merged at a PINNED sha rather than at a remote-tracking name: this container
runs several agents against one shared `.git`, so `origin/main` advances under
a worktree between two reads. The sha merged here is
da45e6b.

No conflicts, and no resolution was performed: `git merge-tree --write-tree`
predicted a clean merge against this branch's head before the merge ran, and
the merge reported none. Three files auto-merged, all of them base-side moves
this branch does not own.

This branch's own diff touches neither gate's inputs; the merge is a base
refresh only.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Console Performance Budget — gauge not trustworthy

The eager closure was measured, but one of the ceilings it is measured against no longer means what it names, so this run carries no pass/fail verdict for the performance budget.

This is not a budget violation. Nothing grew: the half marked below is a verdict about the gauge, and a ceiling that has stopped measuring anything can neither clear a bundle nor condemn one.

Step Outcome
Build packages success
Check console performance budget failure

Which half objected:

Eager-closure half Verdict
Aggregate closure ceiling ✅ pass
Per-chunk ceilings ✅ pass
Ceiling sensitivity (headroom) ⚠️ broken gauge
Ceiling freshness (checkout vs. base branch) ✅ pass

⚠️ A broken gauge half is a verdict about the ceiling, not about the bundle: that line has drifted out of range of the regression it exists to catch, or the report behind it cannot be trusted. It does not say anything grew. The Check console performance budget step log carries the ceiling and the number it was compared against.

Reason: The entry chunk measured 144.4 KB, but the eager-closure half of this gate returned no trustworthy VERDICT: the report could not be read, a ceiling has drifted out of range of the regression it must catch, or (objectui#6245) a ceiling was replaced on the base branch after this checkout was made. The step log says which. This is not a passing budget — and it is not a size regression either.

See the workflow run for details.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 502.02KB 115.16KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 211.58KB 58.68KB
fields (index.js) 247.89KB 62.50KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.25KB 13.99KB
plugin-charts (index.js) 71.34KB 19.90KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.22KB 34.59KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.46KB 65.85KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.77KB 34.17KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.66KB 57.50KB
plugin-kanban (index.js) 46.00KB 14.30KB
plugin-list (index.js) 112.58KB 27.65KB
plugin-map (index.js) 20.64KB 6.86KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.79KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 96.00KB 31.71KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3060.9 KB 3104.5 KB
Main entry chunk (gzip) 145.6 KB 350 KB
Entry file index-CblXRa8q.js
Status PASS

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 544.84KB 130.47KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 213.54KB 59.33KB
fields (index.js) 247.89KB 62.50KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.33KB 19.90KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 252.27KB 65.55KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.55KB 57.83KB
plugin-kanban (index.js) 46.63KB 14.53KB
plugin-list (index.js) 112.68KB 27.68KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 84.36KB 20.78KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 99.04KB 32.62KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

… instrument

The sibling of the narration repair one commit back, and the same defect
class: the docblock hard-coded a row count that nothing re-derives, and the
figure had already been overtaken by the population it described.

Seat ruling on the record: the count is dropped in favour of the instrument,
⛔ not restated as the current one. Restating it would have been a NEW false
claim rather than a repair -- the rows live on this tree are not the same SET
the slice's finding named. Measured across four refs: at a686403 the ledger
held 39 rows with no cast-spelled rows and no cast-piercing in the gate; at
this head it holds 37, two of them `onTabChange` rows that arrived with
objectui#9392 and two `detail` rows drained by this branch. Same cardinality
family, different set.

This file already reads the ledger live -- it imports `KNOWN_UNDECLARED_READS`
from the gate of record -- so the docblock now points at that import and at
the gate's printed line and `--list`, per AGENTS.md #9.

⛔ Narration only, in one docblock sentence. Proved rather than asserted: the
file's non-comment lines are byte-identical before and after (209 lines,
sha256 d7e9bae66... both sides), so no assertion, import or executable line
moved. The changeset, the declarations, the zod arms, the labels and the draft
state are untouched.

Measured at this working tree:
- `node scripts/check-handler-key-read-sites.mjs` exit 0, and its `--list`
  output is byte-identical to the previous head's;
- the four touched test files: 4 files / 130 tests passed, exit 0, per-file
  split unchanged (71 / 32 / 14 / 13);
- eslint on the edited file exit 0; `check:comment-mask-corpus`,
  `check:control-bytes` and `check:new-line-citations` all exit 0.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3060.9 KB 3104.5 KB
Main entry chunk (gzip) 145.6 KB 350 KB
Entry file index-CblXRa8q.js
Status PASS

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 544.84KB 130.47KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 213.54KB 59.33KB
fields (index.js) 247.89KB 62.50KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.33KB 19.90KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 252.27KB 65.55KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.55KB 57.83KB
plugin-kanban (index.js) 46.63KB 14.53KB
plugin-list (index.js) 112.68KB 27.68KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 84.36KB 20.78KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 99.04KB 32.62KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Contract review

Reviewed head: bcbd2793cbe249d637dd8ca21d767fb412ca0240 (read at 2026-09-14T00:06Z)

① derived judgments

Reviewed from scratch in my own git worktrees (head, merge-base, main tip), each
with its own pnpm install. Fork point taken with git merge-base and ⛔ never from
base.sha: da45e6be7a (the two coincide here only because cb5e80cce6 merged that
commit). Shallowness measured rather than assumed —
git rev-parse --is-shallow-repository is false in /home/user/objectui and true
in /home/user/objectstack, so every ancestry fact below comes off a complete history.
The Clause-② declaration is on the card, not in this body: claim comment
5650183110
(Clause-②: yes), restated by the carrier repair 5652400741. I verified it rather
than adopted it — the reading is at ①.1.

Accept-set moves on the published mirror (@object-ui/types/zod) — two

1. crud.zod.ts#DetailSchema.onNavigatehandlerKeyRefusal('onNavigate', 'runtime-slot', 'SPA navigation callback') — RIGHT.
Probed on the built mirror (packages/types/dist/zod/index.zod.js, tsc && vite build
exit 0 in my worktree at each ref), same document shape both sides, onBack — a named
refusal on this same arm since objectui#7344 — as the lit control:

merge-base da45e6be7a  (crud.zod.ts blob a5e45b4c7b…)
  detail  onNavigate    accepted=true   kept={"action":"toast"}   issues=none
  detail  onAddComment  accepted=true   kept={"action":"toast"}   issues=none
  detail  onBack        accepted=false                            issues=custom@onBack   <= LIT CONTROL

head bcbd2793cb          (blob 557c86b6ba…)
  detail  onNavigate    accepted=false                            issues=custom@onNavigate
  detail  onAddComment  accepted=false                            issues=custom@onAddComment
  detail  onBack        accepted=false                            issues=custom@onBack   <= same control, still lit

Direction: narrowing — a document that was ACCEPTED and KEPT is now REFUSED BY NAME.
That is a move on a published mirror ⇒ Clause-②: yes is correct, measured here.

2. crud.zod.ts#DetailSchema.onAddComment — RIGHT. Same probe, same reading, above.

Public-surface moves, read on the BUILT declaration — two

3. DetailSchema.onNavigate?: (url: string, options?: { replace?: boolean; newTab?: boolean }) => void — RIGHT.
Read at packages/types/dist/crud.d.ts:329, built from this head. The zero-hit half is
paired: at the merge-base the same built file answers onNavigate? 0 and
onAddComment? 0, with onBack? 1 and loading? 1 in that same file at that
same ref as lit controls. A new declared member where BaseSchema's [key: string]: any
used to answer — narrowing. The signature matches every call site (DetailView.tsx:704
(backUrl, { replace: true }), :717, :720, :785) and is character-identical to
views.ts#DetailViewSchema.onNavigate, the same component's other registration.

4. DetailSchema.onAddComment?: (text: string) => void | Promise<void> — RIGHT.
dist/crud.d.ts:340. Both forwards (DetailView.tsx:1753, :1910) land on
RecordComments, whose prop at RecordComments.tsx:17 is exactly that type. The
two-argument RecordActivityTimeline.onAddComment(text, attachments?) is fed from
discussion?.onAddComment (renderers/record-activity.tsx:249), a different path, so
the one-argument declaration does not under-declare the channel.

Non-published changes — six

5. scripts/check-handler-key-read-sites.mjs — RIGHT. The diff against the merge-base
is exactly the two row deletions. Set-differenced rather than counted: exactly
detail::DetailSchema.onAddComment and detail::DetailSchema.onNavigate leave, and
nothing arrives. Gate exit 0 at this head, its own printed line:

OK  105 arm(s), 212 registration(s) (119 with an arm), 61 reachable handler read(s), 61 judged, 0 left unjudged on an arm with an unresolved spread, 37 exempted by ledger — every judged read is a declared member of its arm.

Ledger walk re-derived on three installed worktrees, by two instruments each (the gate's
printed line and KNOWN_UNDECLARED_READS.size off the module): 39 at da45e6be7a
39 on main (tip efc1c9c400) → 37 here. Keeping either row would have
reddened staleExemptions().

6. packages/types/src/__tests__/handler-keys-string-any-mirrors-7344.test.ts — RIGHT.
RUNTIME_SLOT 4 → 6, ALL_SITES 8 → 10, both asserted off the arrays. The choice of
ledger is right and I checked it rather than took it: handler-keys-json-refusal-6124.test.ts
names no detail arm at all (zero hits for detail, against its own 64 ledgered
sites as the lit population), while this file already owned crud.zod.ts#DetailSchema.onBack.
The DeclaresExactly addition is the load-bearing part — Equal<T, Shape | undefined>
separates an absent member (any) from a declaration, which KeepsFunction cannot, and
it ships with two synthetic controls failing in both directions. type-check green for
@object-ui/types (tsc --noEmit + tsconfig.examples.json + tsconfig.test.json),
which is where those type-level assertions actually run.

7. packages/types/src/__tests__/zod-mirror-parity.test.ts — RIGHT. The
crud.zod.ts#DetailSchema entry grows to 'onBack' | 'onNavigate' | 'onAddComment' and
the header bullet reads 41 entries / 65 keys. Re-run here, not quoted: 32 passed; the
figures are derived by the file's own AST pins (objectui#7733, objectui#8222), not by
arithmetic on either branch.

8. packages/plugin-kanban/src/__tests__/handlerKeyDispositionsMeasured-7804.test.tsx — RIGHT.
The re-derived witness button::ButtonSchema.onSuccess is live in the ledger at this
head and belongs to neither slice; the leg keeps both halves (non-empty AND named). The
bcbd2793cb docblock edit is narration only — see ③.

9. packages/plugin-detail/src/__tests__/detail-handler-slots-7804.test.tsx — RIGHT at this head.
The defect the previous review named is discharged; measured in ③.

10. .changeset/7804-detail-handler-slots-declared.md — RIGHT. Graded in ②.

11. content/docs/api/schema-reference.md — NOT CHANGED, and it had to be. ⇐ this is the FAIL.
This PR touches nothing under content/: the blob is byte-identical at da45e6be7a and
at this head (e8a68e05d8 on both sides). That file carries ### DetailSchema at line
658 — the author-facing reference for the arm this diff narrows — and after this
change an authored "onNavigate": { "action": "toast" } on a type: "detail" document
goes from accepted and kept to a hard validation failure by name, with nothing in
the reference saying so. Measured, with a lit control from the same file:

  • the file carries no global handler-key statement — zero hits for handler key,
    and the only not authorable / runtime slot prose in it is a per-arm block;
  • that block is at line 954, in ### ObjectKanbanSchema, and it is the one
    objectui#9338 — the immediately preceding slice of this same card, under this same
    batch Redesign examples based on new JSON project specification #69 ruling — added to this same file
    ("Handler keys are not authorable in
    JSON, and two of the three now say so by name
    "), together with
    content/docs/plugins/plugin-kanban.mdx. That is the lit control: the anchor is alive,
    the precedent is in-repo and one week old, and it is the sibling of this very change.

objectui#9338's contract review passed with that repair in it, and its own reasoning
was that a doc survives untouched only where "that key's acceptance does not move in this
PR
". Here it moves — that is ①.1/①.2. AGENTS.md #2 is the standing rule ("Not done until
docs reflect the code"), and nothing mechanical will catch this: check:doc-types is
exit 0 at this head (188 doc files, 1106 code blocks scanned), because it judges
registered component types, not accept sets.

Repair owed before landing: the sibling note's shape in
content/docs/api/schema-reference.md#DetailSchema, naming onNavigate and
onAddComment as objectui#6124 runtime slots refused by name on the JSON face and
supplied by a React host. (packages/plugin-detail/README.md and
content/docs/plugins/plugin-detail.mdx are not owed anything: measured, both
document the detail-view arm and the React components, and neither documents the
type: 'detail' schema face.)

② semver grading

The diff is breaking — major-class by behaviour. It removes documents from the accept
set of a mirror this package publishes (①.1, ①.2) and narrows the shipped TypeScript
surface for the same two keys from any, via BaseSchema's index signature, to two
specific callables in packages/types/dist/crud.d.ts (①.3, ①.4). A document authoring
either key now fails to parse; a host writing either with a different shape now fails to
compile.

Declared: .changeset/7804-detail-handler-slots-declared.md'@object-ui/types': minor.

The grading is correct for this repo, and the changeset states the reason in its own
words. Verified at this head, not adopted: .changeset/config.json carries exactly one
fixed group and it holds 40 packages (⚠️ the dispatch said 41 — the body's 40 is the
right figure), so scripts/check-changeset-no-major.mjs makes major unavailable — exit 0
here, as is check-changeset-presence.mjs, whose own line is "6 source file(s) of 3
released package(s) changed, and this change declares 1 changeset(s)"
. The changeset's
migration claim was re-measured rather than taken: 11 files author a type: 'detail'
node at this head (lit control: 24 author 'detail-view'), and a newline-tolerant
perl -0777 census over all 11 finds onNavigate / onAddComment only in this
branch's own four files, with the control word type hitting in all 11. No changeset is
owed by @object-ui/plugin-detail or @object-ui/plugin-kanban: their only changed files
are under src/__tests__, and nothing in their published behaviour moves.

③ boundary flags

The earlier FAIL at cb5e80cce6, defect by defect, measured at THIS head.

  1. "Suite 4's last leg ships a statement this very tree contradicts", and the docblock's
    hard-coded 39
    DISCHARGED at 083a7f1d68. The leg's name and comment now
    describe this tree, and I verified the new narration is true here rather than
    merely different: detail::DetailSchema.onTabChange is a live
    KNOWN_UNDECLARED_READS row at this head (enumerated, one of the 37), the gate
    unwraps as / ! / satisfies / parens, and the assertion itself (castReads) is
    untouched and still ['onTabChange']. The docblock now defers the size to the
    instrument.
  2. The PR body's three false figures and the stale as any acceptance note
    DISCHARGED. Each re-derived by me independently and each matches: the gate's own
    printed line (61 reachable / 61 judged / 37 exempted), the walk 39 → 39 → 37 at
    da45e6be7a / efc1c9c400 / this head, and "the other 37 are untouched" as a set
    difference rather than a count. The acceptance note now says the blindness is gone and
    names objectui#9392 — true here.
  3. NOT discharged, and a second repair owed in the same push. The body's
    Verification section still opens "All readings at final head 980126de0 (base
    5a41ce733)". Neither half is true at this head: the head is bcbd2793cb, four
    commits on, and the merge-base is da45e6be7a — which the body's own After section
    already uses. The Files bullet for
    packages/plugin-kanban/src/__tests__/handlerKeyDispositionsMeasured-7804.test.tsx
    still reads "merge fallout only: the suite 4 control witness re-derived", which
    stopped describing that file at bcbd2793cb. Same class as the FAIL's own item — a
    record the PM reads that names a head it is not.

The two known-bad figures, re-measured rather than inherited.

  • «the four touched test files 362 passed» — wrong. Measured at this head, under the
    shared verify lock: 4 files / 130 tests passed, exit 0
    (handler-keys-string-any-mirrors-7344 71 · zod-mirror-parity 32 ·
    handlerKeyDispositionsMeasured-7804 14 · detail-handler-slots-7804 13). Option A of
    the repair report's open_question 2 is the right answer, and I can name where 362 came
    from: vitest run scripts/ examples/schema-catalog/ packages/plugin-detail/ at this
    head prints Test Files 360 passed | 2 skipped (362) / Tests 8329 passed | 2 skipped (8331) — 362 is a file count of a broader union, recorded under a four-file label.
  • the older ledger arithmetic — re-derived from scratch at ①.5 against the git merge-base
    fork point, never quoted.

open_questions, by name. Reports 5650679693 and 5651443143 both carry []. The
repair report 5657040435 carries two: (1) whether to repair the plugin-kanban
docblock here — answered by the seat with option B, pushed as bcbd2793cb, and it is
the right call; option C would have been wrong on a measurement, exactly as the report
says, because the 37 rows live here are not the SET the 39-row finding named. (2) 362
vs 130 — answered above. The one open question on the card, 5650164173
(object-kanban::ObjectKanbanSchema.onCardMove), is the kanban slice's; its ledger row is
live at this head and untouched by this diff, correctly.

Every flag the implementer raised, answered or escalated by name.

  1. ⚠️ "One prediction MISSED" (React 19 hazard leg) — answered; the leg reads what
    React reports and its silent-document control can fire. Green here.
  2. ⚠️ "A semantic collision the merge could not see"answered; see ①.8.
  3. "Three figures in that header are machine-read"answered; 32 passed, AST-derived.
  4. ⚠️ "A type-level assertion that could not fire" (KeepsFunction) — answered; see ①.6.
  5. ⚠️ "NOT MEASURED" list — settled for CI at this head: 36 check runs, 33 success
    / 3 skipped / 0 failure / 0 in progress
    ; all four Test (shard n/4) success, and
    Bundle Analysis — the base-inherited red at the previous head — is success here.
    check:sdui-registration-pins exit 2 accepted as stated; full pnpm test is CI's.
  6. ⚠️ "Two build attempts failed before the green one"answered, a real property of
    the '…@object-ui/types' filter, reported not hidden. I sidestepped it the cheap way
    (--filter '@object-ui/plugin-detail^...' build, then type-check) and got green.
  7. Acceptance note objectui#9344 / cast blindnessanswered; true at this head, and
    the note now says so.
  8. Acceptance note the **BREAKING** carrier convention does not exist mechanically
    answered; accepted as a falsification, leaving the changeset as authored was right.
  9. Acceptance note check:changeset-claims flags 16 pending changesetsanswered;
    report-only, exit 0 here, none falsified by this branch.
  10. Acceptance note comments is NOT declared by this changeanswered; correct to
    leave, and the parent card is the carrier.
  11. Acceptance note the 'detail' arm declares far fewer keys than DetailView reads
    answered; outside every row of objectui#7804, parent card carries it.
  12. Acceptance note KeepsFunction cannot fail on an ABSENT memberanswered; fixed
    in place, class successor correctly left open.
  13. Report 5651443143pnpm --filter '…@object-ui/types' build is not self-sufficient
    answered, no action owed.
  14. Report 5651443143PR-body PATCH footer counter-observation; the labeler added
    plugin: kanban
    answered; harness observations, no landing question, and no
    label was set or reverted by me.
  15. Report 5657040435the dispatch's "both checkouts are SHALLOW" is false for objectui
    confirmed independently, first thing this review did: false in
    /home/user/objectui, true in /home/user/objectstack.
  16. Report 5657040435the gate's ledger comment credits objectui#9344 where the review
    credits objectui#9392
    answered; card-versus-PR, the block is main's content and
    this diff's only change to that file is the two row deletions.
  17. Carrier limbs. needs:contract-review is live on PR objectui#9343 and on card
    objectui#7804. ⛔ Neither is cleared here — that is the PM's, and it must not be cleared
    while ①.11 stands.

Raised here, not by the implementer.

  • ①.11, the doc omission — the reason this does not land.
  • The detail / detail-view accept-set divergence is still live and still has no named
    home.
    The previous review flagged it as needing one "before this lands or immediately
    after"; neither repair commit, nor the rewritten body, nor the card, nor a new issue
    carries it. Re-measured on the built mirror at this head, same document shape:
    detail-view onNavigate accepted=true kept={"action":"toast"}, onAddComment
    accepted=true kept={"action":"toast"}, onBack accepted=false (lit control on that
    same arm) — while views.ts#DetailViewSchema declares onBack, onNavigate,
    onTabChange and onAddComment on the TypeScript face. One component
    (DetailView), two registrations, and as of this diff two zod arms that disagree about
    the same two keys. ⛔ Not this slice's to fix — escalated by name to objectui#7804
    (the parent, which stays open) or the objectui#9344 family alongside the onTabChange
    row.
  • ⚠️ Instrument note, not a finding: check:readme-exports exits 1 in my worktree with
    383 unjudged self-imports, every one of them "./dist/index.d.ts is not on disk — run
    pnpm build first". That is my tree's build state (I built only @object-ui/types and
    the plugin-detail dependency closure), not this diff. Recorded as NOT MEASURED.
  • The PR body carries no closing keyword — verified mechanically against the director
    ruling, with 24 objectui# references and one Part of as the lit control. No model
    identifier appears in the diff or in any of the eight commit messages (control: 16
    claude hits, all claude.ai session URLs and Co-Authored-By lines).

Implemented-by: claude/issue-7804-detail-arm (mode:subagent)
Reviewed-by: session_01L5xpA5q533BgTTNADibEFt (domain:spec @ objectui seat — this review ran as an isolated subagent, which has no session of its own)

FAIL


Generated by Claude Code

`content/docs/api/schema-reference.md` is the author-facing reference for the
`type: "detail"` face, and this branch moves `onNavigate` and `onAddComment` on
that face from ACCEPTED AND KEPT to REFUSED BY NAME. The reference said nothing
about either key, so it was left teaching the old acceptance, and nothing
mechanical catches that: `check:doc-types` judges registered component TYPES,
not accept sets, and is exit 0 either way.

Same shape and role as the note objectui#9338 — the preceding slice of this same
card, under the same batch #69 ruling — put under `### ObjectKanbanSchema` in
this same file, but measured against THIS arm rather than transposed:

  - three keys are refused here, not two: `onBack` since objectui#7344, plus
    `onNavigate` and `onAddComment` from this change;
  - the refusal message is quoted from what `handlerKeyRefusal` actually emits,
    read off the built mirror (`packages/types/dist/zod/index.zod.js`);
  - the two keys reach the renderer on DIFFERENT channels, so the "used to do"
    clause names both: `schema.onNavigate` is called in `DetailView`'s own body
    (`handleBack`, `handleEdit`, the post-delete redirect), while
    `onAddComment` is forwarded as a prop into the comment composer, which
    renders BECAUSE the key is truthy and then awaits it on send;
  - the still-undeclared third key differs from the kanban board's. `onCardMove`
    is accepted and DROPPED; `detail`'s `onTabChange` is accepted, KEPT, read
    through a cast and handed to the tab strip's `onValueChange` — so it still
    reaches a call site expecting a function. The note says so rather than
    forcing the parallel.

Docs only: no schema, no renderer, no test and no changeset change.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
…kanban docblock

Merged at a PINNED sha, not at a remote-tracking name: several agents share
one `.git` in this container, so `origin/main` advances between two reads.
The sha merged here is 55f39ee; the
merge-base was da45e6b, 223 files back.

Four files changed on both sides. THREE auto-merged because the two sides
edited different anchors in each:

  - `scripts/check-handler-key-read-sites.mjs` — this branch drains the two
    `detail::DetailSchema` ledger rows; main drained
    `object-kanban::ObjectKanbanSchema.onCardMove` with objectui#9342 and
    rewrote the comment above it. Both drains are in the merged ledger, which
    now holds 36 rows where the merge-base held 39.
  - `content/docs/api/schema-reference.md` — this branch adds the
    `DetailSchema` note; main rewrote the `object-kanban` note one section
    down.
  - `packages/types/src/__tests__/zod-mirror-parity.test.ts` — this branch
    widens the `crud.zod.ts#DetailSchema` entry and its header bullet; main
    rewrote two docblocks around the kanban entry.

ONE conflicted:
`packages/plugin-kanban/src/__tests__/handlerKeyDispositionsMeasured-7804.test.tsx`.
Both sides rewrote the same opening sentence of the file docblock, for
orthogonal reasons, so the resolution is their UNION and neither side's
content is dropped:

  - ours replaced the hard-coded row count with a pointer at the instrument
    that re-derives it (AGENTS.md #9);
  - main's objectui#9342 changed "Two of the three" to "All three" and named
    the card that moved the blocking read.

The union keeps main's fact and this branch's deferral. The count this branch
removed is NOT reintroduced, and the clause claiming the file reads the ledger
live was re-checked against the surviving import.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
…created

The `### DetailSchema` note contrasted `onTabChange` with "the `object-kanban`
board's third key" to make one point: an authored `onTabChange` is KEPT and
reaches a call site, where the board's loose key was accepted and dropped.

Merging origin/main moved the comparand. objectui#9342 gave `onCardMove` a
tombstone on both faces, so the board's third key is no longer accepted-and-
dropped — it is refused by name. Re-measured on the BUILT mirror
(`packages/types/dist/zod/index.zod.js`) after a scoped build:

  ObjectKanbanSchema + onCardMove  -> REFUSED
    "Card move handler — `onCardMove` is RETIRED (objectui#6124, ADR-0049)"
  DetailSchema      + onTabChange  -> ACCEPTED, key kept in the parsed output
  DetailSchema      + onNavigate   -> REFUSED ("SPA navigation callback …")
  DetailSchema      + onAddComment -> REFUSED ("New comment callback …")
  DetailSchema      + onBack       -> REFUSED ("Custom back action …")

Controls for that reading, so the refusals are not a blanket rejection and the
acceptances are not a dead probe: the bare `{ type: 'detail' }` document parses
green, and an invented `zzNotAHandler` key is ACCEPTED and kept on both arms.

The contrast the sentence was making still holds and is now stated against what
the arm actually does. Every other clause in the note was re-checked and none
of them moved: the three refusal messages, their lead labels, the objectui#6124
sentence and the node-type closing are verbatim from the probe above, and the
read sites it names are still `DetailView.tsx` 699-704, 715-720, 784-785 for
`onNavigate`, 1753 for the `onAddComment` prop, and the cast read at 1696 that
reaches `<Tabs onValueChange>` at 1765. main did not touch `DetailView.tsx`.

No count is written down here: `onTabChange` remains a live
`KNOWN_UNDECLARED_READS` row, and `check-handler-key-read-sites.mjs --list`
stays the instrument that enumerates it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3061.2 KB 3104.5 KB
Main entry chunk (gzip) 145.6 KB 350 KB
Entry file index-CMGhnjD1.js
Status PASS

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 544.89KB 130.48KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 213.54KB 59.33KB
fields (index.js) 247.89KB 62.50KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.33KB 19.90KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 252.27KB 65.55KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.55KB 57.83KB
plugin-kanban (index.js) 46.63KB 14.53KB
plugin-list (index.js) 112.68KB 27.68KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 84.36KB 20.78KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 99.04KB 32.62KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Contract review

Reviewed head: e3c3589d1f52af418860423b22a5059e9372dbf2 (read at 2026-09-14T01:52Z)

① derived judgments

Reviewed from scratch in my own git worktree at the pinned head, with its own pnpm install. Fork point taken with git merge-base and ⛔ never from base.sha: git merge-base HEAD origin/main = 55f39ee90forigin/main itself, because this branch has merged it. (origin/main has since moved on to 6317033811; the merge-base is unchanged by that, which I checked rather than assumed.) Shallowness measured, not assumed: git rev-parse --is-shallow-repository is false in /home/user/objectui and true in /home/user/objectstack. ⛔ Nothing below is inherited from either earlier review, from the PR body, or from the dispatch.

Accept-set move on the published mirror — two keys, measured on the BUILT mirror. Probed packages/types/dist/zod/index.zod.js after pnpm --filter @object-ui/types build (VERDICT command-exit 0):

onNavigate      accepted=false  custom@onNavigate
onAddComment    accepted=false  custom@onAddComment
onBack          accepted=false  custom@onBack          <= LIT CONTROL, same arm, refused before this PR too
onTabChange     accepted=true   kept={"action":"toast",...}   <= LIT CONTROL, the acceptance still live
onValueChange   accepted=true   kept={"action":"toast",...}
zzNotAHandler   accepted=true   kept={"action":"toast",...}
{ "type": "detail" } bare        accepted=true

The refusals are key-specific and the acceptances are not a dead probe. On the base tree (git show 55f39ee90f:packages/types/src/zod/crud.zod.ts and :packages/types/src/crud.ts) the two keys occur 0 times each, against 2 for the onBack control ⇒ the move is from accepted and KEPT by .passthrough() to refused by name, on a published mirror, in the narrowing direction. ⇒ needs:contract-review applies on both limbs; both carry it at this head (PR #9343 and card objectui#7804, read live). ⛔ Neither cleared here.

Public surface, read on the BUILT declaration rather than the source. packages/types/dist/crud.d.ts declares onNavigate? at :329 and onAddComment? at :340, with onBack?: () => void; at :312 as the lit control in the same file; DetailSchema is re-exported at dist/index.d.ts:73. The signature is byte-identical to what views.ts:773 already declares for the same component under its other registration.

Clause-② — verified, not accepted. The declaration lives on the card's claim comments, not in the PR body: objectui#7804 #issuecomment-5650183110 (domain:ui PM seat, R34, the plugin-detail slice) reads Clause-②: yes, restated with a standalone Branch: line at #issuecomment-5652400741. Measured against the tree rather than read off the comment: the accept set does move on a published mirror ⇒ yes is correct.

Arm membership, derived. The built DetailSchema shape carries exactly three on* members — onBack, onNavigate, onAddComment — all three runtime-slot. That is what makes the doc note's "refuses three of them by name" a reading rather than a count.


THE CLAIM SWEEP — 87 claims, and how the population was bounded

Bounding rule, stated so it can be checked. Every assertion of fact carried by (i) the prose-bearing artifacts of git diff 55f39ee90f..HEAD — the schema-reference.md note clause by clause, the changeset, the crud.ts TSDoc, the crud.zod.ts docblocks, and the narration in all four touched test files; plus (ii) the gate-script diff; plus (iii) every factual assertion in the PR body, walked top to bottom. ⛔ Excluded and named: the detail / detail-view divergence (objectui#9447, out of scope by the dispatch), and restatements of the .passthrough() mechanism already counted once. 87 rows: 77 true, 7 FALSE, 3 NOT MEASURED.

A — content/docs/api/schema-reference.md, the ### DetailSchema note, 15 clauses (all TRUE)

# clause measurement verdict
A1 "refuses three of them by name" built shape carries exactly 3 on* members, all named refusals true
A2 "onBack a named refusal since objectui#7344" refusal present at base and head; crud.zod.ts cites #7344 true
A3 "declares onNavigate and onAddComment as #6124 runtime slots" handlerKeyRefusal(..., 'runtime-slot', ...), both faces true
A4 "a React host supplies the function … the validator refuses the key by name" dist/crud.d.ts:329/:340; custom@key on the built mirror true
A5 "the message leads with the slot's label (SPA navigation callback, New comment callback)" byte-read off the built messages true
A6 the quoted #6124 sentence reproduced verbatim from the built message true
A7 "closes by pointing at the node-type spelling ({ "type": "toast", … }, an action:button node)" closing sentence of both messages true
A8 "Until then an authored onNavigate: { "action": "toast" } parsed green … the value is kept" ablation below returns the key to accepted+kept; onTabChange is the standing live control true
A9 "every read site only tests the key for truthiness" DetailView.tsx:701,715,718,784; RecordComments.tsx:76,152 true
A10 "handleBack, handleEdit and the post-delete redirect call schema.onNavigate(url, { replace }) in DetailView's own body" three sites confirmed (:704, :717/:720, :785) true, imprecise — see ③
A11 "onAddComment is forwarded as a prop into the comment composer, which renders because the key is truthy and then awaits it on send" forwarded at DetailView.tsx:1753 and :1910; composer gated on onAddComment at RecordComments.tsx:152; awaited at :80 true
A12 "onTabChange is a fourth handler key this view reads and it is still undeclared" cast read at :1696; accepted+kept on the built mirror; live ledger row true
A13 "onCardMove has been a tombstone refused by name since objectui#9342" built mirror: RETIRED (objectui#6124, ADR-0049); PR #9443 body reads Fixes #9342 and merged as 55f39ee90f true
A14 "an authored onTabChange … is kept, read through a cast and handed to the tab strip's onValueChange" :1696 to :1765 true
A15 "That gap is open on objectui#7804." card open; check-handler-key-read-sites.mjs:203-204 attribute both onTabChange rows to objectui#7804 true

B — .changeset/7804-detail-handler-slots-declared.md, 9 claims (all TRUE) — the declared bump; the two runtime slots on both faces; the .passthrough() mechanism; "declared on NEITHER face" (base tree: 0 / 0, control onBack 2); the green-parse base reading; the onBack control; "major unavailable … scripts/check-changeset-no-major.mjs" (fixed[0].length === 40, @object-ui/types in it, changeset:check exit 0); the two signatures; and the Migration claim. That last one I re-derived rather than accepted: 11 files author a type: 'detail' node (control: 23 author 'detail-view'), and the only JSON-key spelling of either key anywhere in the tree is the changeset's own counter-example. Nothing in the corpus has to change.

C — packages/types/src/crud.ts TSDoc, 6 claims (all TRUE)'detail' registered RAW (plugin-detail/src/index.tsx:414 registers DetailView itself, where :300 registers DetailViewRenderer for 'detail-view'); "called by handleBack / handleEdit / the post-delete redirect"; "declared on NEITHER face until this card"; "signature … the same one views.ts#DetailViewSchema.onNavigate declares" (views.ts:773, identical); "DetailView does not call onAddComment, it forwards it"; the (text: string) => void | Promise<void> twin matching RecordComments.tsx:17.

D — packages/types/src/zod/crud.zod.ts docblocks, 5 claims (all TRUE) — RAW registration and arrival by identity; the three calling sites; the base reading with onBack as the lit control; the forward into the composer behind a schema.comments gate (:1750, :1907); and "comments is deliberately NOT declared here" (absent from the built shape).

E — test-file narration, 13 claims (12 TRUE, 1 FALSE)

# claim measurement verdict
E1 detail docblock writes no ledger size no digit-figure in it true
E2 register('detail', DetailView) RAW index.tsx:414 true
E3 two keys, two channels, one disposition suites 1–3, all green true
E4 suite 4: the gate now judges the cast read --list prints 'detail' DetailSchema.onTabChange [UNDECLARED] … :1696 true
E5 "there is exactly ONE such read" perl -0777 occurrence census: 1 true
E6 "Deciding the key's objectui#6124 disposition is objectui#9344's item ②" (detail-handler-slots-7804.test.tsx:267) objectui#9344 is state: closed, state_reason: completed, closed 2026-09-13T12:55:04Z FALSE
E7 "DetailSchema today declares neither onTabChange nor the onValueChange spelling TabsSchema carries" both accepted+kept on DetailSchema; TabsSchema declares onValueChange only true
E8 kanban docblock defers the row count to the instrument it already imports count gone; :135 imports KNOWN_UNDECLARED_READS true
E9 kanban docblock union: "All three … now JUDGED" onCardClick/onQuickAdd runtime-slot, onCardMove retired true
E10 kanban suite-4 witness re-derived to button::ButtonSchema.onSuccess row 1 of the 36 still exempted at this head true
E11 7344 docblock: "neither was ever z.string() or z.any()" 0 hits at the base tree, control onBack 2 true
E12 "10 sites ledgered, 6 runtime slots + 4 retired" file green, 71 tests true
E13 parity header 41 entries / 65 keys + the restatement 41 the file's own AST pins (ledgerEntryKeys, keyTotal, objectui#7733 / objectui#8222) compare header against ledger, with non-vacuity controls — 32 passed true

F — scripts/check-handler-key-read-sites.mjs, 1 claim (TRUE) — the diff against the merge-base is exactly the two row deletions and nothing else.

G — the PR body, 38 claims (29 TRUE, 3 NOT MEASURED, 6 FALSE)

Re-derived and TRUE (each measured here, none inherited): the gate's printed line 105 arm(s), 212 registration(s) (119 with an arm), 60 reachable handler read(s), 60 judged, 0 left unjudged …, 36 exempted by ledger (exit 0) · the ledger walk 39 at da45e6be7a38 at main 55f39ee90f36 here, set-differenced: exactly object-kanban::ObjectKanbanSchema.onCardMove leaves on the main leg and exactly the two detail:: rows leave on this one, nothing arrives on either · "the other 36 are untouched" · the three agreeing instruments (printed line, KNOWN_UNDECLARED_READS.size off the module = 36, [UNDECLARED] enumerated with perl -0777 = 36) · --list at this head 61 lines, sha256 prefix 0b586b1c9e568ffc (verbatim) · merge commit 35146cc663 with parents 8a5fdc8f6a + 55f39ee90f, fork point da45e6be7a, 223 files on the main side and 9 on this one · the four both-sides files, named exactly · crud.zod.ts blob 557c86b6ba… identical at 8a5fdc8f6a, 980126de0 and HEAD · mergeable/merge_commit_sha b18d013b48 · 36 check runs, 33 success / 3 skipped / 0 failure · the membership read 37 manifests name @object-ui/types, all 37 declare a type-check script · check:control-bytes 7625 tracked text files · check:new-line-citations 0 new · NOT GOVERNED — 9 path(s) checked against 5 governed surface(s); none matched (verbatim) · 6 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s) (verbatim) · 56 check:* families · the 40-package fixed group · no **BREAKING** scanner exists (only dollar-dialect-alias-census.test.ts and vite-ineffective-dynamic-imports.ts mention the word; .changeset/config.json uses the stock @changesets/cli/changelog) · first-occurrence line numbers 5 to 51, the kanban changeset at line 21 · title-case **Breaking in 78 files · check:sdui-registration-pins exit 2 with No console build to weigh at apps/console/dist/assets (verbatim) · comments not declared by this change · no closing keyword anywhere in the body (control fires on a synthetic Fixes #9342), Part of objectui#7804 present · both carrier limbs hung.

The ablation reproduced EXACTLY, run from the committed state in my own worktree under trap … EXIT INT TERM:

HEAD_BLOB=557c86b6ba292441f4f91e3170dac6964da3924d
BEFORE_BLOB=557c86b6ba292441f4f91e3170dac6964da3924d
ANCHOR_BEFORE onNavigate=1  onAddComment=1
ANCHOR_AFTER  onNavigate=0  onAddComment=1
MUT_BLOB=2662f39b5313fd5fbaac1f08affd212041cd8e6f     <= the body's hash, to the byte
MUTATED_GATE_EXIT=1     'detail'.onNavigate  read at packages/plugin-detail/src/DetailView.tsx:701
MUTATED_VITEST_EXIT=1   Tests 3 failed | 10 passed (13)
AFTER_BLOB=557c86b6ba292441f4f91e3170dac6964da3924d   RESTORE_DIFF=0 path(s)   RESTORED_GATE_EXIT=0

All three mutated failures are named on onNavigate; every onAddComment leg stayed green. The vitest leg takes no dist hop — vitest.config.mts:501 aliases @object-ui/types/zod to the source. And the wide union reproduces to the test: Test Files 604 passed | 2 skipped (606), Tests 13001 passed | 2 skipped (13003), VERDICT command-exit 0 (9m13s).

NOT MEASURED, 3, each with its reason: the pre-merge --list capture (62 lines / c6223378723c6bc1) — a historical capture; corroborated only indirectly, in that the ledger holds 37 rows at 8a5fdc8f6a, which is what 61 reachable / 37 exempted requires. pnpm --filter '...@object-ui/types' type-check (42 of 47, 41 tasks) and pnpm build (43/43) — not re-run here; CI's Type Check and Build & E2E are success at this head.

FALSE at this head, 6:

# body claim measured here
G-a "objectui#9344 stays open for its item ②" objectui#9344 is closed, state_reason: completed, since 2026-09-13T12:55:04Z — before both the commit that wrote the clause and the body PATCH that repeats it
G-b "Across 1489 pending changesets" 1540 .changeset/*.md at this head
G-c "the all-caps **BREAKING spelling appears in 31" 36
G-d "check:changeset-claims … flags 16 pending changesets whose bodies name crud.ts, crud.zod.ts or zod-mirror-parity.test.ts" the gate prints 26 at this head, and 12 of them name content/docs/api/schema-reference.md — a file this PR now edits and which the clause's file list omits. So "All 16 predate this branch and none is falsified by it" is asserted over a population 10 larger than the one read
G-e "the 'detail' arm declares far fewer keys than DetailView reads — … data … all kept by the passthrough" data IS a declared arm member (base.zod.ts:330, unioned in along .extend()), so it is judged, not kept
G-f "Every push trigger in this repo's workflow files is branch-limited to [main, develop]" 24 workflows carry a push trigger; 18 are [main, develop] and 6 are [main] only. The conclusion the clause is used for survives — none reaches a feature branch — but the sentence as written does not

All six sit under the body's own header claim, at the top of Verification: "Every figure below was re-derived AFTER the merge commit and after the documentation repair that follows it; none is carried forward from the pre-merge round." The Ablation section is carved out of that claim explicitly and correctly; the Acceptance notes are not, and G-a to G-e are pre-merge readings standing under it.

Settled, since the dispatch asked. The actions/runs?head_sha= / commits/{sha}/check-runs disagreement at this sha is scope, not lag: the first returns 25 workflow RUNS, the second 36 check runs (jobs). Summing runs/{id}/jobs over exactly those 25 runs gives 36CI alone contributes 10, Changeset Guard and Changeset Presence 2 each, every other run 1. ⛔ That does not explain the earlier 0 reading for a sha carrying a full census; that one stays unmeasured, and commits/{sha}/check-runs stays the authority.

② semver grading

The changeset is .changeset/7804-detail-handler-slots-declared.md and it declares '@object-ui/types': minor.

What the diff actually is: breaking. A document authoring onNavigate or onAddComment on a type: 'detail' node was ACCEPTED and its value KEPT before this diff, and is REFUSED BY NAME after it (code: 'custom' at the key's own path) — measured on the built mirror, both directions, with onBack as the lit refusal control and onTabChange / onValueChange / an invented zzNotAHandler as lit acceptance controls on the same document. The TypeScript face moves the other way, from BaseSchema's [key: string]: any to two declared callables. A narrowing of a published accept set is a major-level move.

major is mechanically unavailable: .changeset/config.json holds ONE fixed group of 40 packages with @object-ui/types in it, and scripts/check-changeset-no-major.mjs — which runs inside changeset:check, ⛔ not under any no-major npm script — refuses any major. changeset:check exits 0 here. minor is therefore the correct declaration for this repo, and the changeset states the reasoning and the measurement rather than asserting the level. The migration paragraph is the part that makes minor survivable, and it holds: no authored document in the repository, its examples or its docs writes either key (11 'detail'-authoring files, control 23 for 'detail-view'; zero JSON-key spellings outside the changeset's own counter-example).

grading: the declaration matches what the repo's own policy permits, and the body does not overstate it. ⛔ Not a defect.

③ boundary flags

The two earlier FAILs, the merge resolution and the post-merge narration repair — each judged at THIS head.

  1. FAIL Implement visual designer for Object UI schemas #1 (cb5e80cce6) — suite-4 narrating a dead cast-blindness, plus a hard-coded 39 in the docblock. DISCHARGED. The leg now says what it measures (the SOURCE spelling), records the dead fact rather than smoothing it, and writes no ledger size; expect(castReads).toEqual(['onTabChange']) is intact and green; detail::DetailSchema.onTabChange is a live ledger row at check-handler-key-read-sites.mjs:204, read at DetailView.tsx:1696. ⚠️ But the repair that cleaned that comment block introduced the defect at E6 / G-a — see item 5.
  2. FAIL Implement visual designer for Object UI schemas #1's secondary — the body's gate figures and the stale as any note. DISCHARGED. Gate line, ledger walk and --list hash all reproduce byte-for-byte at this head.
  3. FAIL Add automated testing infrastructure and CI/CD workflows #2 (bcbd2793cb) — the author-facing reference left teaching the old acceptance. DISCHARGED, and well. The ### DetailSchema note now exists, sits in the kanban precedent's position (between the property table and **Related:**), and every one of its 15 clauses is measured true at this head, several verbatim against the built mirror.
  4. The merge (35146cc663) and its one conflict. DISCHARGED. Parents, fork point, the 223/9 split and the four both-sides files all reproduce; the kanban docblock union keeps main's fact and this branch's deferral, and reintroduces no count; the gate script carries both drains and loses neither side's; the one clause the merge invalidated — the onCardMove contrast — is repaired in its own commit e3c3589d1f, one line, and the tombstone it now names is real on the built mirror.
  5. The post-merge narration repair. NOT DISCHARGED — this is the FAIL. packages/plugin-detail/src/__tests__/detail-handler-slots-7804.test.tsx:267 ships, inside this diff, a comment routing a live gap to a closed card: "Deciding the key's objectui#6124 disposition is objectui#9344's item ②". objectui#9344 was closed completed at 2026-09-13T12:55:04Z, when PR objectui#9392 landed its item 1 (the instrument); its three comments end at that LANDED notice and item 2 was never carried anywhere. The body repeats it — "objectui#9344 stays open for its item ②" — ~12.5 h after the close. This is the same shape both earlier FAILs named, written by the repair that was meant to clear it: a green assertion whose comment names a state the world is not in. It is not cosmetic — detail::DetailSchema.onTabChange is an accepted-and-KEPT key reaching Tabs onValueChange at DetailView.tsx:1765, i.e. exactly the hazard this PR closes for its two keys, and the diff now ships two artifacts that disagree about who owns it: the published doc note says "That gap is open on objectui#7804" (correct — the ledger rows at :203-204 name objectui#7804) while the test comment and the body send the reader to a completed card. ⇒ repair owed: re-point that comment at the carrier the ledger already names, or file the successor and name it. The body's five other stale clauses (G-b to G-f) are the same family and belong in the same push; the cheapest correct shape for G-b/G-c/G-d is the one this branch has already taken twice — ⛔ do not write a fresh number, defer to the instrument (ls .changeset/*.md, node scripts/check-changeset-claims.mjs).

Every implementer flag and open_questions entry, answered by name.

  • open_questions on #issuecomment-5657782874 — should the merged kanban docblock name the tombstone inline?A, as recommended. Answered. main authored that wording in objectui#9342 and the per-key section below it states each disposition; re-litigating another card's prose inside a merge resolution is not this PR's. ⛔ Not a defect.
  • Earlier open_questions (the 362 vs 130 figure; the kanban docblock's 39-row).Both closed and verified here. The four touched files measure 4 files / 130 tests, exit 0 at this head (71 / 32 / 13 / 14); the 39-row string is gone from the kanban docblock and the file now points at the ledger it already imports.
  • "the changeset carrier convention claimed in dispatch does not exist mechanically" (noted, not filed).Upheld on the mechanism, corrected on the arithmetic. No scanner exists and the stock changelog publishes verbatim — both re-measured. Its three census figures are G-b/G-c and a line-range that does hold.
  • "check:changeset-claims flags 16 …" (noted, not filed).G-d. Escalated into the repair above: at this head it is 26, and the 12 that name schema-reference.md were never read, though that is now a file this PR publishes prose into.
  • "comments is not declared by this change" (noted, not filed).Correct and correctly left. Absent from the built shape; not a handler key; carrier is the parent card.
  • "the 'detail' arm declares far fewer keys than DetailView reads" (noted, not filed).Correct in substance, wrong in one member — G-e. data is declared on BaseSchema and unioned into the arm.
  • "KeepsFunction cannot fail on an ABSENT member" (noted, not filed).Answered, and the repair is right. DeclaresExactly is spelled with Equal, and its two synthetic controls assert false on an absent member and on a wrong signature — the controls can fire.
  • "both checkouts are SHALLOW is false for objectui" (noted, not filed).Confirmed independently. false here, true in /home/user/objectstack.
  • "actions/runs?head_sha= is a VOID instrument" (noted, not filed).Half-answered above. The 25-vs-36 gap is scope; the earlier 0 is not, and stays unmeasured.
  • "the gate script credits the cast-piercing to objectui#9344 while the review credits objectui#9392" (noted, not filed).Card-versus-PR, not a contradiction. objectui#9392 is the PR that landed objectui#9344. ⛔ Not a defect.
  • zod-mirror-parity.test.ts's 23 rows / 19 rows prose (noted, not filed).Correctly left. Byte-identical at the fork point, untouched by this branch, and both describe a past migration.
  • The detail / detail-view divergence.objectui#9447. ⛔ Out of scope, not graded, not a ground for this verdict.

Below the bar, for the record, ⛔ not grounds:

  • A10's (url, { replace }) is a sketch: handleEdit calls schema.onNavigate(schema.editUrl) with no options. The declared options? is optional, so the doc and the type agree; only the parenthetical over-specifies.
  • The Verification list names check:changeset-no-major as if it were an npm script (it is not — the file runs inside changeset:check, which the body itself says correctly two sections earlier), and attributes the NOT GOVERNED line to check:governed-queue-guard, whose npm spelling is --self-test; the line comes from --test. Both gates are green either way, measured here.
  • CI has fully converged at this head — 36 runs, 33 success / 3 skipped / 0 failure / 0 cancelled — including Bundle Analysis, which was the base-inherited red two heads ago.
  • The PR is draft, carries no closing keyword, and says Part of objectui#7804. The parent card is open and stays the parent, as batch Redesign examples based on new JSON project specification #69 requires.

Implemented-by: claude/issue-7804-detail-arm (mode:subagent)
Reviewed-by: session_01L5xpA5q533BgTTNADibEFt (domain:spec @ objectui seat — this review ran as an isolated subagent, which has no session of its own)

FAIL


Generated by Claude Code

… carrier

`detail-handler-slots-7804.test.tsx` routed the key's objectui#6124
disposition to "objectui#9344's item ②". That card is no longer open —
the API reads `state_reason: completed`, dated 2026-09-13T12:55:04Z — and
its item 2 was never carried anywhere, so the comment sent the reader to a
card that cannot answer it. The same file already said, nine lines above,
that the row is "attributed to objectui#7804"; the two halves of one
comment block disagreed, and the published note in
`content/docs/api/schema-reference.md` plus the ledger rows in
`check-handler-key-read-sites.mjs` both name objectui#7804.

The sentence now says what is true: the disposition is OPEN, is decided
neither here nor anywhere else yet, and sits on objectui#7804 — the card
the gate script itself names for this row. ⛔ No new card is invented, and
⛔ the gap is not described as decided.

Comment-only. The assertion `expect(castReads).toEqual(['onTabChange'])`
is untouched, as is every other line of the suite.

Measured at this head, from the repository root:
- gate of record: 60 reachable / 60 judged / 36 exempted, exit 0
- `--list`: 61 lines, sha256 prefix 0b586b1c9e568ffc
- the four touched test files: 4 files / 130 tests passed, exit 0
- `check:control-bytes`: 7625 tracked text files, exit 0

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3061.2 KB 3104.5 KB
Main entry chunk (gzip) 145.6 KB 350 KB
Entry file index-E6vDfGz9.js
Status PASS

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 544.93KB 130.50KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 213.54KB 59.33KB
fields (index.js) 247.89KB 62.50KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.33KB 19.90KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 252.44KB 65.58KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.55KB 57.83KB
plugin-kanban (index.js) 46.63KB 14.53KB
plugin-list (index.js) 112.68KB 27.68KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 84.36KB 20.78KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 99.04KB 32.62KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Contract review

Reviewed head: cb66ea5a3717eec36ec5744ca8bc0c610b5456d4 (read at 2026-09-14T02:53Z)

① derived judgments

Reviewed from scratch in my own git worktree at the pinned head, with its own pnpm install
(plus a second worktree at 8a5fdc8f6a to reproduce the pre-merge captures). Fork point taken
with git merge-base and ⛔ never from base.sha: git merge-base origin/main HEAD =
55f39ee90forigin/main itself, because this branch has merged it. Shallowness measured
rather than assumed: git rev-parse --is-shallow-repository reads false in /home/user/objectui
and true in /home/user/objectstack. Diff at this head against that fork point: 9 files,
510 insertions / 14 deletions.

⚠️ The head is cb66ea5a37, not e3c3589d1f. One commit (e3c3589d1f..cb66ea5a37, fast-forward,
comment-only in detail-handler-slots-7804.test.tsx) landed after the body's readings were taken.
Every figure below was re-derived at cb66ea5a37; ⛔ none is inherited from any earlier review.

The accept set and the public surface

The move is on the published mirror, and I read the built artifacts, not the sources.
After pnpm --filter @object-ui/types build in my own worktree:

packages/types/dist/zod/index.zod.js — probed as shipped, all four on the one arm:

onNavigate     accepted=false  issues=custom@onNavigate
onAddComment   accepted=false  issues=custom@onAddComment
onBack         accepted=false  issues=custom@onBack          <= LIT CONTROL (refusal since objectui#7344)
onTabChange    accepted=true   kept={"action":"toast"}       <= LIT CONTROL (still undeclared -> KEPT)

The onTabChange row is the control that matters: it is on the same arm, in the same
instrument
, and varies only the claim — it shows the probe can still report an ACCEPT, so the
two accepted=false readings are the declaration and not a dead instrument.

The base accept set, reconstructed on the same published mirror by removing exactly the two
members this diff adds (DetailSchema.omit({ onNavigate: true, onAddComment: true })), which keeps
every other byte of the shipped arm:

onNavigate     accepted=true   kept={"action":"toast"}   issues=none
onAddComment   accepted=true   kept={"action":"toast"}   issues=none
onBack         accepted=false  issues=custom@onBack      <= LIT CONTROL, same arm

⇒ ACCEPTED-and-KEPT → REFUSED BY NAME. A narrowing move on a published mirror, so needs:contract-review
belongs on both limbs. ⛔ Neither limb is cleared here.

Public surface, read on the built declarationpackages/types/dist/crud.d.ts, interface DetailSchema:

onBack        members=1   <= LIT CONTROL from the same file (declared before this diff)
onNavigate    members=1   onNavigate?: (url: string, options?: { replace?: boolean; newTab?: boolean }) => void
onAddComment  members=1   onAddComment?: (text: string) => void | Promise<void>
onTabChange   members=0   <= still absent, as the diff's own prose says

Both signatures are byte-identical to views.ts#DetailViewSchema's (:773, :887; that interface
spans 651897), which is the same component under its other registration.

Clause-② — verified, not accepted. The declaration lives on the card's claim comments, not in
this body: objectui#7804 comment 5650183110
(domain:ui PM seat, R34, plugin-detail slice) reads Clause-②: yes, restated unchanged with a
standalone Branch: line in 5652400741.
The built declaration above is what that yes authorises and what it delivers — a real move on
packages/types' published surface, with a lit control from the same file.

The declaration is load-bearing — ablation re-run, from the COMMITTED state, under
trap ... EXIT INT TERM, restored by blob hash:

HEAD_BLOB=557c86b6ba292441f4f91e3170dac6964da3924d
BEFORE_BLOB=557c86b6ba292441f4f91e3170dac6964da3924d  size=16003   (equal, non-empty -> instrument OK)
ANCHOR_BEFORE   onNavigate=1  onAddComment=1
ANCHOR_AFTER    onNavigate=0  onAddComment=1                        (surgical: the sibling is the control)
MUT_BLOB=2662f39b5313fd5fbaac1f08affd212041cd8e6f                   (changed -> the edit reached disk)
MUTATED_VITEST_EXIT=1     Tests 3 failed | 10 passed (13)
MUTATED_GATE_EXIT=1
AFTER_BLOB=557c86b6ba292441f4f91e3170dac6964da3924d
RESTORE_DIFF=empty                                                  (git diff HEAD, not an exit code)
ANCHOR_RESTORED onNavigate=1  onAddComment=1
RESTORED_VITEST_EXIT=0    Tests 13 passed (13)
RESTORED_GATE_EXIT=0

MUT_BLOB reproduces the body's own hash exactly. All three mutated failures are NAMED on
onNavigate; every onAddComment leg stayed green — per key, not a blanket. The gate of record
went red with its own printed line, verbatim as quoted in the body:

x  1 handler key(s) a registered renderer reads are not declared by their arm:

      'detail'.onNavigate  read at packages/plugin-detail/src/DetailView.tsx:701
          registered in packages/plugin-detail/src/index.tsx; DetailSchema (crud.zod.ts) does not declare it.

THE CLAIM SWEEP

How I bounded the population. Every claim this diff publishes into the repository — read
clause by clause out of all nine changed files at the pinned head — plus every factual assertion in
the PR body, walked top to bottom. 183 claims enumerated: 178 true, 3 false, 2 NOT MEASURED.

⛔ I inherited none of the previous sweep's 77 "true" rows. 174 rows I re-derived myself at this
head with my own instruments (a gate run, a build, a test run, a probe against the built artifacts,
a git history query, or a newline-tolerant perl -0777 census in my own worktree). 7 rows I
spot-checked
rather than re-executed, each marked [SPOT] below (rows 63, 81, 106, 111, 115, 147,
154): they are statements about trees or API states that no longer exist — the red-first run at
a686403b3, the two one-sided conflict replays, the frozen merge ref, the first type-check
attempt — or unbounded historical superlatives. 2 rows are NOT MEASURED (146, 172), named below
— ⛔ neither counted as a pass nor as a fail. 174 + 7 + 2 = 183.

Excluded from the population and named: (a) check-handler-key-read-sites.mjs's own :204
onTabChange routing prose — pre-existing at the merge-base (this PR's diff on that file is exactly
0 insertions / 2 deletions), carrier objectui#9456; (b) the detail / detail-view accept-set
divergence, carrier objectui#9447; (c) prose in files this diff does not touch; (d) the PR's own bot
comments and prior review records.

A — .changeset/7804-detail-handler-slots-declared.md (14)

  1. ✓ frontmatter declares '@object-ui/types': minor, and nothing else — read at head.
  2. ✓ "DetailSchema — the zod arm type: 'detail' selects" — the arm pins type: z.literal('detail'); the gate resolves registration 'detail'DetailSchema (--list row 28).
  3. ✓ "a named refusal on the JSON face" — built dist/zod/index.zod.js: both keys refuse, code: 'custom' at the key's own path.
  4. ✓ "a callable twin on the TypeScript face" — built dist/crud.d.ts: both present, both callable.
  5. ✓ "BaseSchemaCore ends .passthrough(), so an undeclared key … the value is KEPT" — base.zod.ts:449; on the shipped arm onTabChangekept={"action":"toast"}.
  6. ✓ "Both keys were declared on NEITHER face while DetailView read and RAN them" — git log -S on both faces: first appearance is this branch's own 23f839fed8.
  7. ✓ base reading — onNavigate parsed GREEN with {"action":"toast"} surviving (omit-probe on the published mirror).
  8. ✓ "clicking Back then reported TypeError: schema.onNavigate is not a function" — suite 2 green; the ablation's gate output names the same read site (DetailView.tsx:701).
  9. onAddComment parsed GREEN and the value forwarded into the composer that awaits it — omit-probe + RecordComments.tsx:80 await onAddComment(text).
  10. ✓ "onBack … was refused on the same document" — lit control reproduced: custom@onBack.
  11. ✓ "refused BY NAME with the objectui#6124 guidance … and the message points at the node-type spelling" — message read back verbatim off the built mirror.
  12. ✓ "minor because this package ships inside the fixed group .changeset/config.json enumerates … major is unavailable (scripts/check-changeset-no-major.mjs)" — exactly one fixed group, 40 members, contains @object-ui/types; the script is wired into changeset:check and exits 0.
  13. ✓ "Migration. Nothing in the corpus has to change: no authored 'detail' document in this repository, its examples or its docs writes either key" — census over 2512 tracked .json/.md/.mdx, perl -0777: "onNavigate": → 1 file (this PR's own changeset, quoting it), "onAddComment": → 0. Control "showBack": (a real detail key) hits, so the instrument is lit.
  14. ✓ "both were driven through the real SchemaRenderer before the disposition was assigned" — suite 1's two legs drive both channels and pass.

B — the ### DetailSchema note in content/docs/api/schema-reference.md (16)

  1. ✓ "this face refuses three of them by name" — the arm carries exactly 3 handlerKeyRefusal members (onBack, onNavigate, onAddComment).
  2. ✓ "onBack has been a named refusal since objectui#7344" — introduced by ed27d7c194 (PR fix(types): the 8 remaining on* handler mirrors declared z.string() / z.any() refuse BY NAME (objectui#7344) #7468), the objectui#7344 batch; the arm's own comment and the ledger file name that card.
  3. ✓ "since objectui#7804 this face also declares onNavigate and onAddComment as objectui#6124 runtime slots".
  4. ✓ "a React host supplies the function through the TypeScript interface or as a React prop, and the validator refuses the key by name".
  5. ✓ "the message leads with the slot's label (SPA navigation callback, New comment callback)" — verbatim off the built mirror.
  6. ✓ the quoted is a RUNTIME SLOT for a host-supplied function … sentence — exact substring of handlerKeyRefusal's composed guidance.
  7. ✓ "closes by pointing at the node-type spelling ({ "type": "toast", … }, an action:button node)".
  8. ✓ "Until then an authored onNavigate: { "action": "toast" } parsed green".
  9. ✓ "BaseSchema is .passthrough(), so a key no arm declares is not refused, it stops being judged and the value is kept".
  10. ✓ "every read site only tests the key for truthiness" — all five schema.onNavigate reads are truthiness guards; both onAddComment forwards sit behind schema.comments &&, and RecordComments gates the composer on onAddComment &&.
  11. ~ "handleBack, handleEdit and the post-delete redirect call schema.onNavigate(url, { replace })" — all three DO call it (:704, :717/:720, :785), but handleEdit passes no options object. Imprecise, ⛔ not false: the second parameter is optional in the declared signature. Below the bar; recorded in ③.
  12. ✓ "onAddComment is forwarded as a prop into the comment composer, which renders because the key is truthy and then awaits it on send" — RecordComments.tsx:152, :80.
  13. ✓ "onTabChange is a fourth handler key this view reads and it is still undeclared" — read at DetailView.tsx:1696; onTabChange absent from the built arm and from dist/crud.d.ts; onBack is read as a React prop at :699, so "fourth" holds.
  14. ✓ "the object-kanban board's third key, onCardMove, has been a tombstone refused by name since objectui#9342" — objectql.zod.ts:1529 handlerKeyRefusal('onCardMove', 'retired', …), landed at 55f39ee90f (PR fix(plugin-kanban): read onCardMove from a React prop so the object-kanban arm can tombstone it #9443, card objectui#9342).
  15. ✓ "an authored onTabChange is not refused at all: it is kept, read through a cast and handed to the tab strip's onValueChange" — :1696:1765 <Tabs onValueChange={onTabChange}>; probe says kept.
  16. ✓ "That gap is open on objectui#7804." — check-handler-key-read-sites.mjs:204 attributes the row to objectui#7804; objectui#7804 reads state: open.

C — packages/types/src/crud.ts TSDoc (8)

  1. ✓ "SPA navigation callback — RUNTIME SLOT (objectui#7804, the objectui#6124 shape): a host-supplied function, NOT authorable metadata".
  2. ✓ "'detail' is registered to DetailView RAW" — index.tsx:414 registers the bare component; :300 registers 'detail-view' through DetailViewRenderer, an ElementDataSourceGate wrapper.
  3. ✓ "reaches schema.onNavigate by identity and handleBack / handleEdit / the post-delete redirect CALL it".
  4. ✓ "Until this card the key was declared on NEITHER face".
  5. ✓ "BaseSchema's index signature typed it any here" — base.ts:499 [key: string]: any.
  6. ✓ "Signature taken from the call site ((url, { replace })) … the same one views.ts#DetailViewSchema.onNavigate declares" — byte-identical, and :773 is inside DetailViewSchema.
  7. onAddComment "reaching the renderer on a DIFFERENT channel … DetailView does not call it, it forwards it as a prop into <RecordComments>, whose submit handler awaits it".
  8. ✓ both declared signatures survive into the built dist/crud.d.ts unchanged.

D — packages/types/src/zod/crud.zod.ts docblocks (10)

  1. ✓ "DECLARED here for the first time; it was never on this arm at all".
  2. ✓ ".passthrough() KEPT an authored value … and handed it to a call site expecting a function".
  3. ✓ "registers DetailView RAW (no wrapper, unlike 'detail-view'), so nothing is interposed".
  4. ✓ "DetailView CALLS it in its own body — handleBack, handleEdit, and the post-delete redirect".
  5. ✓ "Driven through the real SchemaRenderer in plugin-detail/src/__tests__/detail-handler-slots-7804.test.tsx: the authored function runs, with the arguments the call site builds" — suite 1 asserts ('/', { replace: true }) and passes.
  6. ✓ the base reading, with onBack as the lit control.
  7. ✓ "Same spelling as views.ts#DetailViewSchema.onNavigate".
  8. ✓ "DetailView never calls this one. It FORWARDS it … the forward is itself gated by schema.comments, an undeclared key the same passthrough keeps alive".
  9. ✓ "Driven end to end in the same probe: the composer is typed into, the send button clicked, and the authored function runs with the text".
  10. ✓ "⚠️ comments is deliberately NOT declared here" — comments absent from the arm, measured kept.

E — packages/types/src/__tests__/handler-keys-string-any-mirrors-7344.test.ts (12)

  1. ✓ "objectui#7804, added on the batch Redesign examples based on new JSON project specification #69 ruling".
  2. ✓ "neither was ever z.string() or z.any()" — git log -S shows no prior declaration of either spelling on either face.
  3. ✓ "they were declared on NEITHER face, which is why .passthrough() KEPT them".
  4. ✓ "the repo gate check:handler-key-reads carried them as KNOWN_UNDECLARED_READS rows" — present at 55f39ee90f, absent at HEAD.
  5. ✓ "this file owns the crud.zod.ts#DetailSchema pair".
  6. ✓ the onNavigate channel paragraph.
  7. ✓ the onAddComment channel paragraph.
  8. ✓ "the base reading was: an authored { action: 'toast' } parsed GREEN on both keys … while onBack … was refused" — reproduced.
  9. ✓ "The six keys whose function value REACHES a renderer" — RUNTIME_SLOT has 6 entries.
  10. ✓ test name "10 sites are ledgered, 6 runtime slots + 4 retired, with no key filed twice" — green in my run.
  11. ✓ "KeepsFunction<any> is true ([any] extends [never] is false). On this pair the helper CANNOT FAIL" — from the helper's own definition at :768.
  12. ✓ "assertionDetailSlotsAreDECLARED … is the one with a control that fires" — and the control is genuinely enforced: packages/types' type-check runs tsc -p tsconfig.test.json, whose include is src/**/*.test.ts(x); tsc --listFiles confirms this file is in that program; the run is green, so both positive assertions and both synthetic negative controls hold.

F — packages/types/src/__tests__/zod-mirror-parity.test.ts (8)

  1. ✓ "41 entries in KnownDrift, 65 keys across them" — re-derived with my own line-oriented parse of the interface at this head.
  2. ✓ "41 / 63 until objectui#7804's plugin-detail slice … an existing entry (so the entry count did not move)" — at 55f39ee90f: 41 / 63.
  3. ~ [SPOT] "⭐ The first keys this ledger has gained from a pair that declared them on NEITHER face" — an unbounded historical superlative over the ledger's whole life; not exhaustively re-derived. Spot-checked against the four keys already in RUNTIME_SLOT, which all came from string/any. Counted TRUE.
  4. ✓ "It was 40 / 61 until objectui#7804's plugin-kanban slice DECLARED … a new entry carrying TWO of the three keys" — parent of 5a41ce733 reads 40 / 61 with no ObjectKanbanSchema entry.
  5. ✓ "Two slices of objectui#7804 land in this bullet back to back and both are real".
  6. ✓ entry docblock: "onBack was declared on both faces and the mirror was too WIDE".
  7. ✓ "These two were declared on NEITHER".
  8. 'crud.zod.ts#DetailSchema': 'onBack' | 'onNavigate' | 'onAddComment' — three keys, and the parity suite is green.

G — packages/plugin-kanban/src/__tests__/handlerKeyDispositionsMeasured-7804.test.tsx (6)

  1. ✓ "All three handler keys the kanban board consumes are now JUDGED by the object-kanban arm" — main's half of the union, preserved.
  2. ✓ "⛔ its row count is NOT written down here" — perl -0777 census: bare 39 → 0, 39-row → 0.
  3. ✓ "This file reads that ledger live, through the import below" — :151 imports KNOWN_UNDECLARED_READS from the gate of record.
  4. ✓ suite-4 comment: "This leg named detail::DetailSchema.onNavigate until the plugin-detail slice … drained the row".
  5. ✓ the re-derived witness button::ButtonSchema.onSuccess is in the live ledger at this head and belongs to neither slice (it is a packages/components row).
  6. ✓ "the length alone passes on a map holding one stale row" — the assertion keeps both halves.

H — packages/plugin-detail/src/__tests__/detail-handler-slots-7804.test.tsx (16)

  1. ✓ "⛔ that ledger's size is NOT written down here" — no ledger numeral anywhere in the docblock.
  2. ✓ "the hard-coded figure this line used to carry went stale inside the life of this branch".
  3. ✓ "BaseSchema is .passthrough() … the value is KEPT".
  4. ✓ "registers DetailView RAW (no wrapper, ../index.tsx)".
  5. ~ "the authored value arrives … BY IDENTITY and is CALLED" — true for onNavigate; for onAddComment the caller is RecordComments, which the same docblock spells out ten lines later. Shorthand, ⛔ not false. Below the bar; recorded in ③.
  6. ✓ "An authored onNavigate: { action: 'toast' } therefore parses GREEN and is handed to a call site expecting a function".
  7. ~ [SPOT] "the shape AlertDialogSchema.onAction had until objectui#7104 declared it" — a claim about another arm's history; spot-checked only. Counted TRUE.
  8. ✓ "Both keys measure 'runtime-slot', and they do NOT share one channel".
  9. ✓ the onNavigate channel paragraph + "Suite 1 drives the back button and the authored function runs with the arguments the call site builds".
  10. ✓ the onAddComment channel paragraph + "Suite 1 drives the composer, so the leg measures the forward AND the call at the other end".
  11. ✓ "It is also gated by schema.comments, itself an undeclared key kept alive by the same passthrough".
  12. FALSE — "⇒ two keys, two channels, one disposition … the sibling slice on object-kanban got three DIFFERENT dispositions out of three keys sharing one prefix." Defect D1, below.
  13. ✓ "onNeverReadByAnyKnownReader is authored on the SAME document, in the SAME render, and must stay at zero calls" — both suite-1 legs assert 0 and pass.
  14. ✓ the red-first predictions block (suites 1/2 pass on the base tree, 3 and 4 fail on both keys, onBack green before and after) — the ablation reproduces exactly that split.
  15. ✓ "Deliberately .shape, not safeParse: under .passthrough() a DELETED key still parses green" — measured: with the member removed, the document parses green.
  16. ✓ the repaired suite-4 comment, all four halves: (schema as any) is still the spelling and there is exactly ONE such read; the gate unwraps as/!/satisfies/parens so the read IS judged and IS a ledger row attributed to objectui#7804; the disposition is OPEN and decided nowhere; DetailSchema declares neither onTabChange nor onValueChange (which TabsSchema does carry).

I — scripts/check-handler-key-read-sites.mjs (1)

  1. ✓ the diff is exactly the two detail::DetailSchema.* rows removed (0 insertions / 2 deletions); both are gone from the live module and the gate stays exit 0.

J — the PR body, walked top to bottom (82)

  1. ✓ "Part of objectui#7804 — the plugin-detail slice."
  2. ✓ "The parent card stays OPEN and stays the parent" — GET /issues/7804state: open, state_reason: null.
  3. ✓ "PRs land per package (director seat, decision batch Redesign examples based on new JSON project specification #69, 2026-09-07, maintainer verbatim 「其他同意」)" — card comment 5565510864.
  4. ✓ "⛔ No closing keyword anywhere in this body" — regex over the live body: 0 hits.
  5. ✓ "…now declares the two handler keys its registered renderer reads, as objectui#6124 RUNTIME SLOTS on both faces".
  6. ✓ table row onNavigateruntime-slot, read in DetailView's own body and CALLED there.
  7. ✓ table row onAddCommentruntime-slot, never called by DetailView, forwarded as a React prop behind a schema.comments gate.
  8. ✓ "The two share a registration and a document and still reach the renderer by two different routes".
  9. FALSE — "the sibling slice on object-kanban got three different dispositions out of three keys sharing one prefix". Defect D1, below.
  10. ✓ "BaseSchemaCore ends .passthrough() ⇒ … the value is KEPT, then reaches the renderer".
  11. ✓ "registers the component RAW (unlike 'detail-view', which goes through a data-source wrapper)".
  12. ✓ "Both keys were declared on NEITHER face".
  13. ✓ the base-reading parse block, all three rows — reproduced.
  14. ✓ the driven-face TypeError: schema.onNavigate is not a function … (handleBack).
  15. ~ [SPOT] "Red-first run on that tree: 13 tests, 8 passed / 5 failed" — the file does have exactly 13 tests, and the 5/8 split is exactly what removing both declarations produces (my one-key ablation gives 3/10 and names which). The a686403b3 tree was not re-run. Counted TRUE.
  16. ✓ "One prediction MISSED … React 19 does not rethrow a handler error out of the dispatch — it REPORTS it … The miss is written into the test's own docblock".
  17. ✓ "objectui#9338 … merged, taking main to 5a41ce733" — 5a41ce733e is that merge commit.
  18. ✓ "merged into this branch as a merge commit (8421c1f39) — no rebase, no amend, no force-push" — every historical head cited on this PR (a686403b39, 23f839fed8, 6546a15642, 8421c1f396, 980126de0f, ff831dd565, cb5e80cce6, 083a7f1d68, bcbd2793cb, 8a5fdc8f6a, 35146cc663, e3c3589d1f) is an ancestor of this head.
  19. ✓ "One textual conflict, in zod-mirror-parity.test.ts" — git merge-tree --write-tree 6546a15642 5a41ce733 reports exactly one CONFLICT, that file.
  20. ~ [SPOT] the three replay figures (ours 2 failed, theirs 1 failed, merged 32 passed) — internally consistent with the counts I derived (pre-kanban 40/61, branch 40/63, main 41/63, merged 41/65). The three runs were not re-executed. Counted TRUE.
  21. ✓ "The derivation is the file's own AST instruments … not arithmetic on the figures in either branch".
  22. ✓ "The bullet now leads with 41 entries / 65 keys and narrates both slices newest-first".
  23. ✓ "Three figures in that header are machine-read, not one … The restatement tracks the entry count … main's 41 auto-merged and is correct untouched" — :393 reads 41 and :3662 machine-reads it; main also has 41 entries.
  24. ~ [SPOT] "in the ours run above the restatement read 41 and PASSED while the entry count failed" — not re-executed. Counted TRUE.
  25. ✓ "objectui#9338's suite 4 CONTROL … named detail::DetailSchema.onNavigate, exactly the row this branch drains".
  26. ✓ "the witness moves to button::ButtonSchema.onSuccess, a row the gate still exempts at this head and one belonging to neither slice".
  27. ✓ "The assertion keeps both halves it had — a non-empty map AND a named row".
  28. ✓ the gate's printed line OK 105 arm(s), 212 registration(s) (119 with an arm), 60 reachable handler read(s), 60 judged, 0 left unjudged …, 36 exempted by ledgerreproduced byte-for-byte at the pinned head, exit 0.
  29. ✓ "Fork point taken with git merge-base … is now 55f39ee90forigin/main itself".
  30. ✓ "the ledger walks 39 rows at da45e6be7a38 on main (55f39ee90f) → 36 on this tree" — all three re-derived.
  31. ✓ "objectui#9342 took onCardMove on main, and this slice takes onNavigate + onAddComment here. ⛔ The other 36 are untouched by this branch".
  32. ✓ "Three independent instruments agree on that 36" — gate line 36, KNOWN_UNDECLARED_READS.size 36, [UNDECLARED] occurrences in --list 36 (perl -0777, ⛔ not grep -c).
  33. ✓ "the merge at cb5e80cce6 brought objectui#9392 in, which taught the gate to read through a cast and so ADDED two onTabChange rows" — isAsExpression present at da45e6be7a, absent at 5a41ce733; both onTabChange rows exist at :203-204.
  34. ✓ "The revision before this one quoted 61 reachable / 61 judged / 37 exempted … read at head 8a5fdc8f6a" — reproduced exactly in my second worktree.
  35. ✓ "objectui#9342 … retires one read site (61 → 60) and drains one ledger row (37 → 36) without this branch touching either".
  36. ✓ "the accept set moves on a published mirror … ⇒ needs:contract-review on both limbs; the parent card already carries it and this PR carries it too" — label present on PR fix(types): declare onNavigate and onAddComment on the detail arm (objectui#7804, plugin-detail slice) #9343 and on objectui#7804.
  37. ✓ "minor, not major: the 40-package fixed group makes major unavailable".
  38. ✓ Files/crud.zod.ts: "Untouched by the merge (blob identical before and after)" — blob 557c86b6ba… at 980126de0, 8a5fdc8f6a, 35146cc663, e3c3589d1f and HEAD alike.
  39. ✓ Files/crud.ts: "signatures taken from the call sites …, matching what views.ts already declares for the same component under its other registration".
  40. ✓ Files/mirrors ledger: "2 rows added (8 sites to 10, 4 runtime slots to 6)".
  41. ✓ Files/parity: "the KnownDrift entry grows to three keys; the header bullet is the merge's one conflict, resolved to the derived 41 entries / 65 keys".
  42. ✓ Files/detail test: "A follow-up commit (083a7f1d68) repairs NARRATION only … the assertion itself, the declarations and the changeset are untouched".
  43. ✓ Files/kanban test: "two independent changes, both narration … (3) the merge below conflicted here and the resolution is a UNION".
  44. ✓ Files/gate script: "the two ledger rows removed … both drains are present and neither side's is lost".
  45. ✓ Files/schema-reference.md: "one clause invalidated, repaired … Re-measured on the BUILT mirror and re-stated; every other clause in the note was re-probed and none moved" — all 16 clauses measured true here (with row 25's imprecision).
  46. ✓ "A type-level assertion that could not fire, caught and replaced … KeepsFunction answers true for an absent member … A DeclaresExactly assertion … with two synthetic controls".
  47. ✓ Merge: "Merge commit 35146cc663, parents 8a5fdc8f6a and 55f39ee90f; the fork point was da45e6be7a, 223 files back on the main side and 9 on this one" — all four re-derived.
  48. ✓ "Four files changed on both sides. Three auto-mergedOne conflicted" — the both-sides set is exactly those four; git merge-tree --write-tree 8a5fdc8f6a 55f39ee90f reports exactly one CONFLICT, in the kanban test.
  49. ✓ "Resolved as their union … ⛔ The row count this branch removed is NOT reintroduced, and the clause claiming the file reads the ledger live was re-checked against the surviving import".
  50. ✓ "the gate's printed line — moved, 61 / 61 / 37 to 60 / 60 / 36".
  51. ✓ "--listmoved, one row out and none in".
  52. ✓ "the suite-4 narration … — still trueonTabChange is still a live ledger row, still read at DetailView.tsx:1696 behind the cast, and the file writes no count down. ⛔ Not edited." — true as scoped to the merge; see ③ for the fact that cb66ea5a37 did later edit that comment block.
  53. ✓ "the KnownDrift figures 41 entries / 65 keysunmoved … both read 63 keys on main and on the fork point" — re-derived: 41/63 at both da45e6be7a and 55f39ee90f.
  54. ✓ "0 check runs at head 8a5fdc8f6a while the previous head bcbd2793cb carried 36 … 36 check runs at head e3c3589d1f" — all three re-read from the API: 0, 36, 36.
  55. NOT MEASURED — "merge_commit_sha: b18d013b48". That field is recomputed per head; the API now returns 66aa24dece… for cb66ea5a37. ⛔ Neither a pass nor a fail.
  56. ~ [SPOT] "refs/pull/9343/merge was not absent during the conflict — it lingered, frozen" — the mechanism is not re-observable now, but the claim it supports (0 check runs) is measured at row 145; recorded as the body's own correction, not as a figure.
  57. ✓ "24 workflow files carry a push trigger: 18 limited to [main, develop] and 6 to main alone (five spelled [main], one as a block list, changeset-release.yml). None reaches a feature branch" — YAML-parsed census of all 37 workflow files; the five/one spelling split read off the files.
  58. FALSE — "All readings at final head e3c3589d1f". Defect D2, below.
  59. ✓ "Test Files 604 passed | 2 skipped (606) / Tests 13001 passed | 2 skipped (13003)" — reproduced exactly, exit 0, same command, run from the repository ROOT.
  60. ✓ "examples/schema-catalog/ is in the union deliberately … packages/plugin-kanban/ joined the union at the merge".
  61. ✓ "37 workspace manifests name @object-ui/types, and all 37 declare a type-check script (all spelled type-check, hyphenated), @object-ui/site among them" — re-derived over the 47 tracked manifests; 0 without, no alternative spelling.
  62. ✓ "Scope: 42 of 47 workspace projects, 41 of which run a type-check task — all 41 green, 0 errors" — reproduced: Scope: 42 of 47, 41 distinct type-check tasks, exit 0.
  63. ~ [SPOT] "That run was red on its first attempt … examples/schema-catalog is not in the ...@object-ui/types DEPENDENTS closure" — the closure fact is re-derived (that package is not a dependent); the red first attempt was not reproduced, because I built first. Counted TRUE.
  64. ✓ "pnpm build --concurrency=2Tasks: 43 successful, 43 total" — reproduced, exit 0.
  65. ✓ "--listsha256 prefix c6223378723c6bc1 / 62 lines before, 0b586b1c9e568ffc / 61 lines after" — both hashes reproduced bit-for-bit (c6223378723c6bc1db07… at 8a5fdc8f6a, 0b586b1c9e568ffcbcdc… at this head).
  66. ✓ "exactly one row left and none arrived'object-kanban' ObjectKanbanSchema.onCardMove — and every other difference is a line-number shift in a row whose text is unchanged" — the normalised diff shows exactly that one row and nothing else but line shifts.
  67. check:control-bytes — reproduced: OK (scanned 7625 tracked text file(s); skipped 85 binary), exit 0.
  68. check:new-line-citations — reproduced: 0 new citation(s), exit 0.
  69. check:changeset-claims exit 0 · check:spec-symbols exit 0 · check:doc-types exit 0 · check:test-path-roots exit 0 · check:readme-exports exit 0 · check:dist-completeness exit 0 · check:unreferenced-sources exit 0 — every one re-run here after a full build.
  70. check:governed-queue-guardNOT GOVERNED — 9 path(s) checked against 5 governed surface(s); none matched, reproduced against this PR's own 9 paths at the merge-base.
  71. ✓ changeset presence — reproduced verbatim: 6 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s).
  72. check:changeset-no-major exit 0.
  73. ✓ "the remaining check:* families (56 exist in this repo)" — 56 check: scripts in package.json.
  74. check:sdui-registration-pins — exit 2, printed line No console build to weigh at apps/console/dist/assets … This is exit 2, not a pass — reproduced verbatim; correctly classified NOT MEASURED.
  75. ✓ "Two build attempts failed before the green one, and neither was this diff … @object-ui/react-runtime (deps sucrase only) and @object-ui/auth name @object-ui/types nowhere" — both manifests read; neither names it.
  76. ✓ "the root vitest config aliases @object-ui/types/zod to packages/types/src/zod/index.zod.ts, so the mutated file IS the module under test" — vitest.config.mts:501.
  77. ✓ the whole ablation block — reproduced line for line, MUT_BLOB included.
  78. ✓ "All three mutated failures are NAMED on onNavigate; every onAddComment leg stayed green".
  79. ✓ the gate's red printed block — reproduced verbatim.
  80. ✓ objectui#9344 "reads state_reason: completed, dated 2026-09-13T12:55:04Z" and the disposition sits on objectui#7804, "the card check-handler-key-read-sites.mjs itself names at :203-204" — API re-read; lines :203-204 carry exactly those two rows, both 'objectui#7804'.
  81. NOT MEASURED — "Dedup ran over all 5 pages of the repo-scoped open-issue list (453 open issues)". The search endpoint returned no total_count for me and the REST listing pages by opaque cursor with no last rel, so I could not re-derive the denominator. ⛔ Neither a pass nor a fail.
  82. ✓ "the changeset carrier convention claimed in dispatch does not exist mechanically. No script under scripts/ or .github/workflows/ scans for a leading **BREAKING (the only two files mentioning the word use it in unrelated prose), and .changeset/config.json uses the stock @changesets/cli/changelog" — exactly two files hit (scripts/__tests__/dollar-dialect-alias-census.test.ts:15, scripts/vite-ineffective-dynamic-imports.ts:42), both prose; changelog is the stock entry.
  83. ✓ "the all-caps **BREAKING spelling is a minority and the title-case **Breaking spelling — this changeset's — is the commoner of the two" — perl -0777 file census: **BREAKING 36 files, **Breaking 78; this changeset spells **Breaking, at line 12.
  84. ✓ "ls .changeset/*.md counts 1540, one of which is the changesets tool's own README.md, while check-changeset-claims.mjs reports 1538" — 1540 files, README.md present, and the gate's own header prints 1538 pending in total. ⭐ The clause names its instrument for each figure, which is the right shape for a population with no single cardinal.
  85. ✓ "first-occurrence line numbers run from 5 to 51, main's own 7804-object-kanban-handler-keys-judged.md carrying it at line 21" — for **BREAKING: min 5, max 51; that file carries it at line 21.
  86. ✓ "at this head the gate reports 26schema-reference.md 12, zod-mirror-parity.test.ts 8, crud.ts 7, crud.zod.ts 3 (26 distinct changesets, 31 name-resolutions)" — reproduced exactly, all five figures.
  87. ✓ "this PR's only edit to schema-reference.md is purely additive1 file changed, 2 insertions(+), 0 deletions" — git diff --numstat reads 2 0.
  88. ✓ "comments is not declared by this change. DetailView gates the onAddComment forward on schema.comments, kept by the same passthrough".
  89. ✓ the nine kept keys (sections, fields, objectName, backUrl, editUrl, comments, activities, summaryFields, autoTabs) and the data correction — all ten measured on the built arm: the nine are undeclared and KEPT, data IS declared (base.zod.ts:330, z.any().optional()), and DetailView reads all ten off schema.
  90. ✓ "the repo's KeepsFunction type helper cannot fail on a member that was ABSENT … Fixed in place for this pair with a DeclaresExactly assertion and two synthetic controls".
  91. ✓ "Session that produced this change … session_01UzHd6hDYatoDn17BuwKxnZ" — a provenance pointer, no measurable content beyond its own presence.
  92. ✓ no model identifier appears anywhere in the nine changed files or the body — perl -0777 census over all nine plus the body: 0 hits for claude- / opus / sonnet / haiku / gpt- / gemini, with an objectui control that hits in every file.

The two false rows

D1 — "three different dispositions out of three keys" is false, and it ships in a source file.

Two artifacts assert it:

  • packages/plugin-detail/src/__tests__/detail-handler-slots-7804.test.tsx:44-46 (a file this diff adds):
    "⇒ two keys, two channels, one disposition — which is a reading, not an assumption: the sibling
    slice on object-kanban got three DIFFERENT dispositions out of three keys sharing one prefix."
  • the PR body, "Per key, not per prefix": "…got three different dispositions out of three keys sharing one prefix".

Newline-tolerant census (perl -0777, comment prefixes folded) over all nine changed files: exactly
1 occurrence, in that test file; 1 more in the body. Control on the same instrument
(objectui#7804) hits in all nine, so the census reaches every file.

Measured, two independent instruments, with a control that varies only the claim:

INSTRUMENT 1 — handlerKeyRefusal's disposition argument, read off the arms
  object-kanban   onCardClick=runtime-slot  onCardMove=retired  onQuickAdd=runtime-slot   -> 2 distinct
  detail  CONTROL onBack=runtime-slot  onNavigate=runtime-slot  onAddComment=runtime-slot -> 1 distinct

INSTRUMENT 2 — the .description on the BUILT mirror (dist/zod/index.zod.js)
  object-kanban   onCardClick=runtime-slot  onCardMove=retired  onQuickAdd=runtime-slot   -> 2 distinct
  detail  CONTROL onBack / onNavigate / onAddComment  all runtime-slot                    -> 1 distinct

The control returns 1 where the truth is 1 and 2 where the truth is 2, so the instrument tracks the
subject. And the reading "3" is not one either instrument can produce: this repo's own vocabulary,
HandlerKeyDisposition in packages/types/src/zod/tombstone.zod.ts:69-73, has exactly two
members — 'runtime-slot' and 'retired'. Three keys cannot carry three different dispositions in a
two-valued vocabulary.

⛔ This is not a vocabulary quibble imported from outside the diff. The PR body's own table separates
the two columns — | key | disposition | channel, MEASURED | — with runtime-slot under
disposition. The sentence in the test file uses the vocabulary correctly two lines earlier
("two keys, two channels, one disposition") and then misuses it. And the sibling slice's own
shipped artifacts say the opposite in as many words: objectql.zod.ts:1512-1517 reads "⚠️ The three
do NOT share a disposition. Two are RUNTIME SLOTS … the third, onCardMove, is a TOMBSTONE",
and its test's own suite-3 leg is named "the mirror spells RUNTIME SLOT on the two live slots and
RETIRED on the third" (green in my run).

What the kanban slice actually got is three different CHANNELS and two dispositions, which is a
strictly stronger argument for per-key measurement than the false one. The sentence is load-bearing
prose — it is the stated justification for the whole per-key discipline — and it is published into
packages/plugin-detail's test suite, where it will be read as the record of what the sibling slice
measured.

D2 — "All readings at final head e3c3589d1f" is false at this head.

The PR head is cb66ea5a3717eec36ec5744ca8bc0c610b5456d4; e3c3589d1f is its parent. This is not
drift the author could not see: cb66ea5a37 was pushed at 2026-09-14T02:10:53Z and the body was
last edited at 2026-09-14T02:17:17Z — six minutes after the head moved — and the Verification
section still opens by naming the parent as the final head.

⭐ The consequence is nil and I want that on the record: I re-derived every figure in that section at
cb66ea5a37 and all of them hold — the gate line, both --list hashes, the ledger walk, the
vitest totals, the type-check scope, the build, the twelve gates and the ablation. The commit in
between is comment-only (7 insertions, 4 deletions, one comment block, expect(castReads).toEqual(['onTabChange'])
and every other line untouched). ⛔ But a review record is not allowed to repair a false statement by
noticing that it happens not to matter: the body tells a reader the verification covers the head
under review, and it does not.

② semver grading

Changeset of record: .changeset/7804-detail-handler-slots-declared.md, declaring
'@object-ui/types': minor — one package, no others.

What this diff actually is: MAJOR. Both faces narrow, measured on built artifacts:

  • JSON face — dist/zod/index.zod.js refuses onNavigate and onAddComment, each code: 'custom'
    at the key's own path, where the same built arm with those two members removed ACCEPTS and KEEPS
    {"action":"toast"} on both. A published accept set that shrinks is a breaking move.
  • TypeScript face — dist/crud.d.ts types both members as functions where BaseSchema's
    [key: string]: any (base.ts:499) used to type them any, so a host assigning a non-function
    now fails to compile.

Why minor is nonetheless the correct declaration. major is mechanically unavailable in this
repository and I verified the mechanism rather than the claim: .changeset/config.json carries
exactly one fixed group, of 40 packages, and @object-ui/types is in it, so any major would
carry all 40; scripts/check-changeset-no-major.mjs refuses one and is wired into changeset:check
(package.json:121). Both gates exit 0 at this head. The changeset states that reason in its own
body and marks the change **Breaking, and measured.**, matching the sibling slice's precedent
already on main (.changeset/7804-object-kanban-handler-keys-judged.md:21).

Coverage. The other two packages this diff touches (plugin-detail, plugin-kanban) change test
files only. check-changeset-presence reports, reproduced verbatim: 6 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s), exit 0.

⇒ ② is graded correctly and declared correctly, and is ⛔ not a ground for the verdict below.

③ boundary flags

The three FAILs and the four repair rounds — each named defect, at THIS head, measured.

round defect named at cb66ea5a37
FAIL 1 (cb5e80cce6) suite-4's defunct cast-blind narration in the detail test DISCHARGED — the leg now describes the post-objectui#9392 tree; detail::DetailSchema.onTabChange is a live ledger row, read at DetailView.tsx:1696, judged by the gate
FAIL 1 the hard-coded ledger size in that file's docblock DISCHARGED — no ledger numeral anywhere in the docblock
FAIL 1 the body's stale 59 reachable / 35 exempted and walk 39 → 37 → 35 DISCHARGED — body now reads 60 / 60 / 36 and 39 → 38 → 36; both re-derived here
FAIL 2 (bcbd2793cb) the kanban docblock's hard-coded KNOWN_UNDECLARED_READS row count DISCHARGED — census: 39 → 0, 39-row → 0; the live import at :151 replaces it
FAIL 2 the body bullet describing only merge fallout for the kanban file DISCHARGED — the bullet now names all three changes, union included
FAIL 3 (e3c3589d1f) the dead objectui#9344 carrier inside the test comment block DISCHARGED at cb66ea5a37 — the comment now routes to objectui#7804, which is what check-handler-key-read-sites.mjs:203-204 and the published doc note both name; objectui#9344 re-read as closed / completed / 2026-09-13T12:55:04Z
FAIL 3 four stale body figures + one wrong member (data) DISCHARGED61/61/3760/60/36, both --list hashes, 1626 changeset-claims with the four-file breakdown, the 1489/31 cardinals replaced by named instruments, and data correctly moved from "kept" to "declared" (base.zod.ts:330). All re-derived here

Every flag the implementer raised, answered by name.

  • "One prediction MISSED" (React 19 reports a handler error rather than rethrowing) — answered. The leg reads what React reports, carries a silent-document control beside it, the miss is written into the docblock, and both legs are green.
  • noted, not filed: the changeset carrier convention does not exist mechanicallyanswered, and re-measured: no scanner under scripts/ or .github/workflows/, stock @changesets/cli/changelog, **BREAKING 36 files vs **Breaking 78. The clause names an instrument for every figure instead of writing a cardinal for a live population — which is the right shape, since ls .changeset/*.md (1540), that minus the tool's own README.md (1539) and check-changeset-claims.mjs (1538) are three defensible answers to one question.
  • noted, not filed: check:changeset-claims flags 26answered, reproduced to the figure: 26 distinct changesets, 31 name-resolutions, 12 / 8 / 7 / 3.
  • noted, not filed: comments is not declaredanswered. Not a handler key; stays on objectui#7804 as the parent's own accept-set question.
  • noted, not filed: the nine kept keys and the data correctionanswered, all ten measured on the built arm.
  • noted, not filed: KeepsFunction cannot fail on an ABSENT memberanswered, and the repair is real: DeclaresExactly plus two synthetic controls are genuinely enforced, because packages/types' type-check runs tsc -p tsconfig.test.json, whose program contains this file (tsc --listFiles), and the run is green.
  • objectui#9344's item ②escalated by name: no card carries the per-key onTabChange disposition; the ledger rows carry the keys on objectui#7804, which stays OPEN as the parent. ⛔ Not this PR's to take, and ⛔ no duplicate filed.
  • objectui#9447 (the detail / detail-view divergence) — named, ⛔ not reopened, ⛔ not this PR's.
  • objectui#9456 (the gate's own :204 forward-routing to a closed card) — named. Measured pre-existing: this PR's diff on that file is 0 insertions / 2 deletions. ⛔ Not a ground for this verdict and ⛔ no duplicate filed.

Real, but below the bar — recorded, ⛔ not grounds for the verdict.

  1. The published doc note says handleBack, handleEdit and the post-delete redirect "call schema.onNavigate(url, { replace })". All three call it, but handleEdit passes no options object (DetailView.tsx:717, :720). The second parameter is optional in the declared signature, so the sentence is imprecise rather than false.
  2. The new test's docblock says the authored value "arrives at schema.onNavigate / schema.onAddComment BY IDENTITY and is CALLED" — the caller for onAddComment is RecordComments, which the same docblock spells out ten lines later. Shorthand, not a false statement.
  3. The body's "⛔ Not edited" for the suite-4 narration is true as scoped to the merge, but cb66ea5a37 did subsequently edit that comment block, and the Files bullet for that file still describes only 083a7f1d68. An omission, not a false claim — but it is the same root as D2.
  4. The changeset-claims note says "none is falsified by it, and that is now measured rather than asserted", then offers the purely-additive proof for schema-reference.md alone. The proof in fact extends to 22 of the 26 flagged bodies (schema-reference.md 12, crud.ts 7, crud.zod.ts 3 are all +N / -0); the 8 naming zod-mirror-parity.test.ts sit against a +28 / -3 edit and are argued, not measured.
  5. CI at the pinned head: 36 check runs — 32 success, 3 skipped, 1 still in_progress, 0 failures at read time. mergeable: true, mergeable_state: behind (origin/main has moved to 5c75dd68f0 since the merge). Neither is a defect in this diff; both are the PM's.
  6. Instrument hygiene I can confirm rather than assume: the vitest run was issued from the repository root, its log names the four touched test files (verbose re-run: 4 files / 130 tests / exit 0), and apps/console occurs 0 times in the 606-file run's log (apps/ 0 times too) — so the objectui#3378 false green did not happen.

The verdict. Two measurably false statements at this head, one of them published into a source
file this diff adds and used there as the justification for the slice's whole per-key discipline.
The declarations themselves, the changeset, the semver reasoning, the gate of record, the ablation
and all twelve re-run gates are sound; the defect is in the record this PR writes, which is the same
class the three previous rounds were failed for.

Implemented-by: claude/issue-7804-detail-arm (mode:subagent)
Reviewed-by: session_01L5xpA5q533BgTTNADibEFt (domain:spec @ objectui seat — this review ran as an isolated subagent, which has no session of its own)

FAIL


Generated by Claude Code

…e kanban cross-reference

The docblock claimed the sibling kanban slice got "three DIFFERENT
dispositions out of three keys". It cannot have: HandlerKeyDisposition
has exactly two members, so three keys can never carry three distinct
dispositions. What that slice measured is three different CHANNELS
carrying two dispositions - two runtime slots and one tombstone - which
is what its own objectql.zod.ts docblock already says in as many words.

The corrected reading is the stronger argument for per-key measurement,
not a climb-down: it is exactly the case where counting keys and counting
dispositions come apart, so neither can be read off the other.

Comment-only. The gate of record and its --list digest are unmoved.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3061.3 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-DZHlMLHk.js
Status PASS

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 544.93KB 130.50KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 213.54KB 59.33KB
fields (index.js) 247.89KB 62.50KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.33KB 19.90KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 252.44KB 65.58KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.55KB 57.83KB
plugin-kanban (index.js) 46.63KB 14.53KB
plugin-list (index.js) 112.68KB 27.68KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 84.36KB 20.78KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 99.04KB 32.62KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Contract review

Reviewed head: 8da6a093c7d904bd96222487fa4dcfb454647ccd (read at 2026-09-14T03:35Z)

① derived judgments

Scope of this round, stated first. The repair since the fourth sweep is one commit, cb66ea5a37..8da6a093c7. I did not re-run the 183-claim sweep, and I say why with a measurement rather than an assurance. What follows is: the structural argument with its own control, the two repaired statements judged on their own merits, the invalidation set I bounded and re-derived, a named spot-check sample, and the three counts.

The structural argument — measured, and it holds

  • git diff --name-only cb66ea5a37 8da6a093c7 names exactly one file: packages/plugin-detail/src/__tests__/detail-handler-slots-7804.test.tsx (+14/-2).
  • Filtering the diff to lines that are not comment-prefixed leaves 0 lines, and the edit sits at :44-58, inside the docblock that opens at :9 and closes at :77.
  • Stronger, and the form I rely on: strip all comments from that file at both shas and the remainder is byte-identical — sha256 prefix d4b73ee5e69191ca790319a13447eedb, 126 lines, on both sides.
  • LIT CONTROL on the stripper itself, because a zero-difference reading is void without one: the identical stripper run across this branch's own arm commit 23f839fed8 on crud.zod.ts moves the hash 50a02812…8755acde… and the line count 114 → 116. The instrument discriminates, so the equality above is a real absence of change and not a broken filter.

⇒ every claim whose subject is any file other than that one, or any runtime behaviour, has a byte-identical subject at cb66ea5a37 and at this head. The fourth sweep re-derived 174 of its 183 rows at cb66ea5a37 with its own instruments. Those subjects cannot have moved. Exactly two subjects did move: the prose of that one test file, and the PR body, which was re-PATCHed.

Repair ① — the disposition count, judged on what is there NOW

Measured at this head, ⛔ not checked against what the last review said was wrong.

sub-claim instrument verdict
HandlerKeyDisposition has exactly two members union members enumerated with perl -0777 off tombstone.zod.ts:69-73runtime-slot, retired; count 2 TRUE
three keys can never carry three distinct dispositions follows from the above TRUE
the kanban three are two runtime slots and one tombstone objectql.zod.ts:1528-1530onCardClick runtime-slot, onCardMove retired, onQuickAdd runtime-slot2 distinct. CONTROL varying only the arm: crud.zod.ts:177/205/222 all runtime-slot1 distinct, which is what the same sentence claims for detail TRUE
"which is what that slice's own objectql.zod.ts docblock says in as many words" objectql.zod.ts:1512-1517 reads "The three do NOT share a disposition. Two are RUNTIME SLOTS … the third, onCardMove, is a TOMBSTONE" TRUE — literally, not loosely
the routes are bounded by nothing / "three different channels" the kanban file's suite 2 — the channels, per key carries three per-key legs: onQuickAdd reaches BY IDENTITY · onCardClick is REPLACED and the replacement CALLS the authored one · onCardMove reaches NOTHING TRUE on the substance, one imprecision — below

⇒ the repaired statement is true, and its agreement claim is true verbatim. It is not a differently-false replacement: the statement it replaces was false by arithmetic against a closed two-valued type; this one is a claim about routes, which no type bounds, and every countable part of it reproduces.

⚠️ One imprecision, recorded and below the bar. The third of the three "channels" is onCardMove, which the kanban file itself says reaches nothing — "neither channel delivers" (:51). Counting a dead route as a third channel is loose, and the test-file spelling "the routes by which a key reaches a renderer … there were three" is the exposed form of it. It stays below the bar for three measured reasons: the repo binds no closed vocabulary to "channel" (the kanban file never writes "three channels" — 0 occurrences — and uses the word for candidate routes that may or may not deliver, as at objectql.zod.ts:1657 "reads NEITHER content channel"); the three per-key readings genuinely differ, which is the point the sentence is making; and the sentence self-corrects in its own clause by naming the third one a TOMBSTONE, so no reader comes away believing three functions reach the board. Same calibration the fourth sweep applied to the handleEdit options sketch.

Repair ② — the provenance paragraph, judged on its own merits

Does it still assert anything a narration-only push would falsify? — NO. I walked every proposition. The paragraph opens by disclaiming that it asserts a tip; each of its three bullets is a past-tense act at a named sha ("Taken at", "Independently re-derived at", "Re-measured at"), and an act performed at a commit stays true wherever the branch moves next. Census on the stored body: All readings at (final )?head0. ⭐ LIT CONTROL sharing no suspect part of the instrument — the identical regex against an archived copy of the pre-repair wording fires 2, so the zero is a real removal.

Its one falsifiable proposition is the invariant, "Every commit after the last sha named above is narration-only … and moves no figure in this section." A narration-only push keeps it true, which is exactly the property asked for. A figure-moving push would falsify it — and the paragraph says so in-band and makes the staleness mechanically detectable from the commit list. That converts silent staleness into a declared, checkable failure mode; it is the requested shape, not an evasion.

Does it over-claim? — NO, and I checked the disclaimer rather than the claim. Bullet 3 names exactly three things re-measured at 8da6a093c7 and disclaims the rest in terms. I reproduced all three at this head:

  • gate of record, exit 0, its own printed line byte-for-byte — OK 105 arm(s), 212 registration(s) (119 with an arm), 60 reachable handler read(s), 60 judged, 0 left unjudged on an arm with an unresolved spread, 36 exempted by ledger60 / 60 / 36;
  • --list exit 0, 61 lines, sha256 prefix 0b586b1c9e568ffc — unchanged;
  • the four touched test files from the repository ROOT (objectui#3378), through the verify lock, VERDICT command-exit 0: Test Files 4 passed (4) / Tests 130 passed (130). ⭐ Run with --reporter=verbose so vitest itself names them, ⛔ not the echoed command line: handler-keys-string-any-mirrors-7344.test.ts 71 |unit| · zod-mirror-parity.test.ts 32 |unit| · detail-handler-slots-7804.test.tsx 13 |dom| · handlerKeyDispositionsMeasured-7804.test.tsx 14 |dom|, summing to 130; apps/console occurs 0 times; the RUN banner names the worktree root; a dark control filename returns 0.

And the disclaimer really does cover what was not re-run: the section's remaining figures (the 604/13001 union, type-check Scope: 42 of 47, build 43/43, the other gates, the ablation) are not claimed at that commit.

⚠️ I also tested the one load-bearing attribution in the paragraph, since a false one would break the chain: bullet 2 says the fourth review "reports that every figure in this section holds there". Checked against that review's own bookkeeping — its 3 false rows are 86 and 100 (the disposition sentence, in the test file and in What lands) and 149 (the provenance line itself, which is not a figure), and its 2 NOT MEASURED rows are 146 (merge_commit_sha, in Merge) and 172 (the dedup denominator, in Acceptance notes). I located each in the stored body by section: none of the four falls inside Verification. The attribution is accurate.

The invalidation set — how I bounded it, and what I re-derived

Bounded by the structural proof above: only two subjects moved, so the invalidation set is the 16 group-H rows (the test file whose prose changed) plus the group-J body rows that were rewritten or are head-dependent. Everything else has a byte-identical subject.

Re-derived at this head, with my own instruments:

  • All 16 group-H rows. No ledger numeral survives in the docblock (39/36/37 → 0 occurrences each); both keys measure runtime-slot and do not share a channel — onNavigate is read and CALLED in DetailView's own body at :704 (handleBack), :717/:720 (handleEdit) and :785 (post-delete redirect), while onAddComment appears only as a forwarded prop at :1753/:1910 and is awaited at RecordComments.tsx:80 behind the :76/:152 gates; ComponentRegistry.register('detail', DetailView) at index.tsx:414 is RAW against 'detail-view' at :300 going through DetailViewRenderer; suite 4's narration describes the post-objectui#9392 tree and routes to objectui#7804 (93440 occurrences in the file, objectui#7804 → 6).
  • 22 body rows, including: card state: open; 0 closing keywords (lit control Part of objectui#7804 = 1); 0 model identifiers (lit control objectui = 34); all 15 historical shas cited in the body are ancestors of this head, with origin/main's tip as a dark control that correctly reads NOT-ancestor; the gate line; the ledger walk 39 → 38 → 36 enumerated in the Map literal at da45e6be7a / 55f39ee90f / HEAD and set-differenced — exactly the two detail:: rows leave and nothing arrives; three agreeing instruments on 36 (printed line, --list [UNDECLARED] by perl -0777, KNOWN_UNDECLARED_READS.size off the module); the gate-script diff is 0 insertions / 2 deletions; 24 push triggers split 18/6 by a YAML parse of all 37 workflow files; 56 check:* scripts; the nine passthrough-kept keys against data, which is declared (base.zod.ts:330), with title lit and zzNope dark; schema-reference.md purely additive at 1 file changed, 2 insertions(+).
  • 6 rows from groups A/B/E/F/I that the repair could plausibly have brushed — .passthrough() at base.zod.ts:449; the 40-member single fixed group; the corpus migration census over 2512 tracked .json/.md/.mdx ("onNavigate": → 1 file, this PR's own changeset quoting it; "onAddComment": → 0; lit control "showBack": fires); the gate-script row deletions; the mirrors ledger's 10 sites are ledgered, 6 runtime slots + 4 retired; the parity header's 41 entries / 65 keys.

⭐ These six double as a lit control on the structural argument itself: if it were unsound, one of them would have moved. None did.

The accept set and the public surface, re-derived rather than inherited. On the built JSON mirror packages/types/dist/zod/index.zod.js, both keys now read accepted=false, code: 'custom', at the key's own path, carrying the objectui#6124 guidance. Base state reconstructed by .omit()ing exactly the two new members: both accepted=true, kept={"action":"toast"}. LIT CONTROL onBack refused the same way on the same document; SAME-ARM ACCEPT CONTROLS onTabChange, onValueChange, comments and an invented zzNotAHandler all still accepted and kept ⇒ the refusal is key-specific, not arm-wide, and the probe is not returning a blanket rejection. The bare { type: 'detail' } document parses green. Public surface on the built declaration: dist/crud.d.ts:329 onNavigate?: (url, options?) => void and :340 onAddComment?: (text) => void | Promise(void), with onBack?: () => void at :312 as the control, re-exported at dist/index.d.ts:73. ⇒ the accept set moves on a published mirror, in the narrowing direction, on both faces — the carrier applies, and neither limb is mine to clear.

Clause-② — verified, ⛔ not accepted. The declaration is on the card's claim comments (5650183110, restated with a standalone Branch: line at 5652400741), ⛔ not in the PR body. I then checked it against the tree rather than stopping at the declaration: the diff moves an authorable surface in packages/PKG/src (crud.zod.ts, crud.ts) and the accept set moves as measured above ⇒ yes is correct. The governing rule adds that a Clause-②: yes PR takes at least minor, which the changeset satisfies.

The counts

rows how
re-derived 44 16 group-H + 22 group-J + 6 carried-set probes, all measured at this head with my own instruments
spot-checked (named) 7 rows 76, 81, 106, 111, 115, 147, 154 — the red-first run at a686403b3, the two one-sided conflict replays, the frozen merge ref, the first type-check attempt, the AlertDialogSchema/objectui#7104 history, and the "went stale inside the life of this branch" superlative. Each is a statement about a tree or an API state that no longer exists; I confirmed each is consistent with what I measured and did not reconstruct the vanished tree.
NOT MEASURED 2 rows 146, 172, unchanged from the previous sweep and both outside the Verification section. ⛔ Neither a pass nor a fail.
carried on the structural argument 130 subjects proved byte-identical to what the fourth sweep re-derived at cb66ea5a37

44 + 7 + 2 + 130 = 183. ⭐ I judge the structural argument sound, and the six probes above are the measurement that says so rather than an assurance that it must be.

② semver grading

Changeset of record: .changeset/7804-detail-handler-slots-declared.md, frontmatter declaring '@object-ui/types': minor and nothing else.

What this diff actually is: MAJOR. Both published faces narrow, measured on built artifacts above — the JSON mirror moves a document from accepted-and-kept to refused-by-name, and the TypeScript face moves both members from BaseSchema's any-valued index signature to specific callable signatures, so a host assigning a non-function is now a type error.

Why minor is nonetheless the correct declaration. major is mechanically unavailable: .changeset/config.json carries exactly one fixed group of 40 packages containing @object-ui/types, and scripts/check-changeset-no-major.mjs is wired into changeset:check (node scripts/check-changeset-fixed.mjs && node scripts/check-changeset-no-major.mjs — ⛔ there is no standalone no-major npm script). The changeset states that reason in its own body and carries its migration, which is what a breaking changeset owes. Clause-②: yes independently floors it at minor. ⇒ correct for this repo, ⛔ not overstated and ⛔ not understated.

③ boundary flags

The nine defects named across the four FAILs — at THIS head, measured.

round defect at 8da6a093c7
FAIL 1 suite-4's defunct cast-blind narration DISCHARGED — the leg now states the gate DOES judge the read and carries it as a ledger row on objectui#7804; detail::DetailSchema.onTabChange is live in the module and --list prints it
FAIL 1 the hard-coded ledger size in that docblock DISCHARGED39/36/37 all 0 occurrences in the file
FAIL 1 the body's stale 59 reachable / 35 exempted, walk 39 → 37 → 35 DISCHARGED — body reads 60 / 60 / 36 and 39 → 38 → 36; both re-derived and set-differenced here
FAIL 2 the missing ### DetailSchema author-facing note DISCHARGED — present, purely additive (2 insertions(+), 0 deletions); its clauses reproduce against my built-mirror probe, including the three named refusals and onTabChange still kept
FAIL 2 the kanban docblock's hard-coded row count / the merge-fallout-only bullet DISCHARGED — no numeral in that docblock; the bullet names all three changes
FAIL 3 the dead objectui#9344 carrier in the test comment and the body DISCHARGED — 0 occurrences of 9344 in the test file; the body's 2 remaining mentions are the historical "Filed as" record and the correction that the card is closed
FAIL 3 four stale body figures + the wrong data member DISCHARGED — instruments named in place of cardinals; data correctly stated DECLARED at base.zod.ts:330, re-measured with lit and dark controls
FAIL 4 — D1 "three different dispositions out of three keys", shipping in a source file DISCHARGED — replaced in both artifacts; the replacement is true and its agreement claim verbatim (① above)
FAIL 4 — D2 "All readings at final head e3c3589d1f" DISCHARGED — 0 occurrences, lit control fires on the pre-repair copy; the replacement asserts no tip (① above)

Every flag the implementer raised, answered by name. The final report carries open_questions: [] — none is outstanding. Its three out-of-scope entries:

  1. the Files bullet the ② shape does not reach — the implementer reports rather than claims this, and it is right to. Body line 86 still describes only 083a7f1d68 as the follow-up on the detail test file, which cb66ea5a37 and 8da6a093c7 have since also edited. Measured: the sentence is act-bound about 083a7f1d68 and true of it, so it is INCOMPLETE, ⛔ not false. ⇒ recorded, ⛔ not a ground. Successor: whoever next edits the Files list.
  2. scripts/check-handler-key-read-sites.mjs's onTabChange routing prose → objectui#9456. ⚠️ Verified rather than accepted, because a dead carrier introduced by this diff would be live: the text is present at the merge-base 55f39ee90f (1 occurrence) and at HEAD (1), and this PR's diff on that file is 0 insertions / 2 deletions. Pre-existing on main, ⛔ not this PR's, ⛔ not duplicated.
  3. the detail / detail-view accept-set divergence → objectui#9447. Not touched, not widened. The passing observation checks out: views.zod.ts:155 declares onBack as a runtime-slot alongside crud.zod.ts:177.

Real, but below the bar — ⛔ not grounds for the verdict.

  • The "three channels" imprecision in ① — the dead route counted as a third channel, exposed in the test file's "routes by which a key reaches a renderer … there were three".
  • Body line 105's "⛔ Not edited" for the suite-4 narration is true as scoped to the merge section it sits in; the block was later edited at cb66ea5a37. Same family as the Files bullet.
  • The doc note's "handleEdit … call schema.onNavigate(url, { replace })"handleEdit passes no options object at :717/:720. The parameter is optional ⇒ imprecise, ⛔ not false. Unchanged since the fourth sweep recorded it.
  • Bookkeeping in the fourth review, not in this PR: its group-J heading reads (82) where the rows run 92-183, i.e. 92. The stated total of 183 reconciles with 92, so the heading is the typo and the arithmetic is sound.
  • CI has fully converged at the pinned head: commits/{sha}/check-runs reads 36 — 33 success, 3 skipped, 0 failure, 0 cancelled. This settles the fourth sweep's 1 still in_progress note. mergeable_state: behind is the seat's, ⛔ not the diff's.
  • Carriers observed hung on both limbs (PR objectui#9343 and card objectui#7804), as the seat placed them. ⛔ Neither cleared here.

Implemented-by: claude/issue-7804-detail-arm (mode:subagent)
Reviewed-by: session_01L5xpA5q533BgTTNADibEFt (domain:spec @ objectui seat — this review ran as an isolated subagent, which has no session of its own)

PASS


Generated by Claude Code

@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

ADOPTED — the PASS at 5658634767 is adopted verbatim. Both carriers cleared; landing.

Five contract reviews, four FAILs, nine named defects — all nine discharged at this head, measured.

check reading
① same-form PASS at tier record 5658634767, head 8da6a093c7d904bd96222487fa4dcfb454647ccd, heading · sha in a code span · ①②③ itemized · independence pair · exactly one unhedged **PASS**, zero **FAIL**. Tier claude-opus-5 on all 167 harness-stamped messages, no fallback.
② both carriers, machine-read --pair 9343exit 0, run live: "both carriers agree".
③ every check 36 runs, all complete: 33 success / 3 skipped / 0 failure / 0 cancelled. Settles the previous round's 1 in_progress note.

⭐ Why this round was scoped down, and why that is not a shortcut

The fourth review swept 183 claims (178 true, 3 false, 2 NOT MEASURED). The repair after it was one commit, one file, +14/−2, comment-only — so this round was asked for a bounded review rather than a re-sweep, on the explicit condition that it prove the bound and sweep if the proof failed.

It proved it. git diff --name-only cb66ea5a37 8da6a093c7 names one file; stripping all comments leaves content byte-identical on both sides (sha256 prefix d4b73ee5e6…, 126 lines). ⭐ And it put a lit control on the stripper itself — the same filter across the branch's arm commit 23f839fed8 moves the hash 50a02812…8755acde… and 114 → 116 lines. ⇒ the zero is a real absence of change and not a blind instrument.

rows
re-derived 44
spot-checked (named: 76, 81, 106, 111, 115, 147, 154) 7
NOT MEASURED (146, 172, carried) 2
carried on the structural argument 130
183

⭐ It also re-derived 6 probes inside the carried set — passthrough, the 40-member fixed group, the 2,512-file migration census, the gate-script deletions, the mirrors ledger, the parity header — as a lit control on the structural argument itself. None moved.

The two repairs, judged on their own merits

① The disposition count is TRUE. HandlerKeyDisposition enumerates to exactly 2 members; the kanban arm reads runtime-slot / retired / runtime-slot = 2 distinct, against the detail arm's 1 as control; and the claimed agreement is literally true — objectql.zod.ts:1512-1517 says "Two are RUNTIME SLOTS … the third, onCardMove, is a TOMBSTONE". ⭐ Not a differently-false replacement, and the reviewer said why: the old claim was impossible by arithmetic against a closed two-valued type, while the new one is about routes, which nothing bounds.

② The provenance paragraph is TRUE and does not over-claim. No proposition of the form "the tip is X" survives: All readings at (final )?head0, with the pre-repair copy firing 2 as a control that shares no suspect part of the instrument. A narration-only push falsifies nothing; the single falsifiable proposition — the invariant — is falsifiable only by a figure-moving push, is declared in-band, and is mechanically detectable from the PR's own commit list. Its three re-measured items all reproduced here (gate 60/60/36; --list 61 lines / 0b586b1c9e568ffc; four files 4 passed / 130 passed under --reporter=verbose with apps/console 0 times).

⭐ And the reviewer tested the paragraph's attribution, not just its shape: the fourth sweep's 3 false and 2 NOT MEASURED rows all fall outside the Verification section, so "every figure in this section holds there" is accurate rather than lucky.

② semver: the diff is substantively major — the published accept set narrows on both faces — and major is mechanically unavailable (one 40-package fixed group containing @object-ui/types, enforced by check-changeset-no-major.mjs inside changeset:check). .changeset/7804-detail-handler-slots-declared.md declares minor and states that reason. Correct for this repo.

Clause-②: yes verified against the tree, ⛔ not accepted from the declaration.

Recorded, ⛔ not blocking

  • "three different channels" counts the dead route (onCardMove, which reaches nothing) as a third channel. Imprecise, not false: the repo binds no closed vocabulary to "channel", the three per-key readings genuinely differ, and the sentence self-corrects by naming the third a TOMBSTONE.
  • The Files bullet (body line 86) still names only 083a7f1d68 for the detail test file — act-bound about that commit and true of it ⇒ incomplete, not false. The repair said outright that its ② shape does not reach that bullet rather than claiming it did.
  • Body line 105's "⛔ Not edited" is true as scoped to its Merge section; same family.
  • The doc note's handleEdit … onNavigate(url, { replace })handleEdit passes no options at :717 / :720; the parameter is optional ⇒ imprecise, unchanged since the fourth sweep recorded it.
  • A bookkeeping slip in the fourth review, ⛔ not in this PR: its group-J heading reads (82) where the rows run 92-183, i.e. 92. The stated 183 reconciles with 92, so the heading is the typo — recorded so a later reader does not try to make 82 add up.
  • The gate script's objectui#9344 routing prose was verified pre-existing rather than accepted as such (1 occurrence at the merge-base and at HEAD; this PR's diff there is 0 insertions / 2 deletions) ⇒ genuinely objectui#9456's.

Carriers cleared from both this PR and card objectui#7804 in one pass. Landing next: ready → auto-merge → merge queue. ⛔ Card objectui#7804 stays open — this PR carries no closing keyword, by the batch #69 ruling.


Generated by Claude Code

@os-sam
os-sam marked this pull request as ready for review September 14, 2026 03:40
@claude
claude Bot added this pull request to the merge queue Sep 14, 2026
Merged via the queue into main with commit 7ca6ddd Sep 14, 2026
38 checks passed
@claude
claude Bot deleted the claude/issue-7804-detail-arm branch September 14, 2026 03:59
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.

3 participants