Skip to content

feat(types): refuse shortcut by name on app action items (objectui#7719) - #9495

Merged
claude[bot] merged 7 commits into
mainfrom
claude/issue-7719-shortcut-named-refusal
Sep 14, 2026
Merged

claude[bot] merged 7 commits into
mainfrom
claude/issue-7719-shortcut-named-refusal

Conversation

@claude

@claude claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Fixes objectui#7719

⚠️ Recorded, not relied on: objectui#9476 measured that closing keywords do not close cards in this repository (29 of 29 sampled closes were by an actor, 0 by a commit). The keyword is for the record; the seat closes by hand at landing.

Round 2 — the six round-1 FAIL items, each repaired

Round-1 contract review returned FAIL at 76a2ce6881 on six defects, all false published prose or figures; the ruling implementation itself was verified compliant and is unchanged. The list was adopted whole — ⛔ nothing reinterpreted, nothing left unimplemented.

# defect repair
1 a comment in app-action-onclick-refusal-6854.test.ts saying "the assertions below are unchanged" and "both undeclared keys are gone", directly above the it() block this branch took from 3 expect() calls to 2 rewritten to describe the single surviving assertion and the two-contract split. ⛔ The deleted assertion is NOT restored: the parse fails now, so there is no parsed value left to read the key out of. The sibling row is cited by name, not by position
2 .changeset/6854-layout-renderer-retired-onclick.md — three sentences this branch falsifies, publishing verbatim to users all three corrected in place, prose only, frontmatter byte-identical (asserted in the edit). See the second surface breach below
3 the card's own changeset claimed @objectstack/spec removed action.shortcut together with its tombstone measured against the resolved installed package and corrected — the tombstone is live. See the NOT MEASURED section, where this entry moved out of the list
4 gate table published 4 source file(s); the gate says 6 figure removed, replaced by a pointer to the gate
5 gate table published scanned 7667 tracked text files; the gate said 7668 figure removed, replaced by a pointer to the gate
6 :116 cited a different it() — this PR's own +10-line docblock had moved the named test to :126 cited by name, no line number

Items 4, 5 and 6 are one defect with three instances, and the fix is the pointer rather than the corrected number. That is not a preference — it is measured: the review's own correction of item 5 to 7668 had already staled by the next push, and the value it staled to staled again. ⛔ No count is quoted here, because quoting one is the defect this sentence describes; the gate prints its own.

The two sweeps run on top of the list

Every pending changeset, not only the one the review surfaced. check-changeset-claims.mjs matches backticked file names, so it is blind to a changeset naming only symbols — which is how defect 2 survived. Counting rule: for every pending .changeset/*.md (population ls .changeset/*.md | wc -l, dated per sha in the round-3 notes below — ⛔ not quoted here, it moves with every merge), count grep -oF matches of MenuItemSchema, AppMenuItem, AppAction.items, shortcut, LayoutRenderer; list every file with a non-zero total; then read every hit in context and resolve which declaration it names.

  • 22 files carry at least one term (positive control that the enumerator is live: the same per-file rule applied to a term known to be present, grep -lF 'changeset', selects 110 of the same population — ⭐ the predicate is published here because round 3's body named that figure without one, and a count without its rule is not a reading). 20 are adjudicated not falsified, and the dominant reason is the ownership trap this card is about: MenuItemSchema / MenuItem / shortcut in 6132, 6521, 6523, 6931, 7004, 7072, 7073 all resolve to the overlay union ({ separator: true } divider, MenuCommandItem), which this diff does not touch. 6301, 6490, 8348, marketplace-disabled-state-5504, olive-clouds-repeat use "shortcut" as an ordinary English word. 6143 and rename-command-palette-shortcuts-demo are CommandItem, a different type. 6349, 6692, 7344, 7721, 7760, 7918 make statements that remain true. The 2 that were falsified are the two repaired above.
  • ⚠️ The sweep is re-run after every origin/main merge, and each newly arrived changeset is checked against the same five terms; the result is stated at the head being pushed, never carried over from the pre-merge tree. ⛔ The arrival count is not quoted — it is a property of whichever merge just happened, and quoting round 2's value at round 3's head is exactly how it went wrong before.
  • ⛔ One thing the sweep deliberately does not do: 6692 cites app.ts:728 and app.ts:461, and this diff inserts into app.ts above the first of those (the hunk header in git diff names the offset; ⛔ the line count is not transcribed — it moved again this round, from 28 to 33, which is the point). Re-measured at the merge-base this PR actually points at: app.ts:728 is title: string; and :461 is a comment fragment — both were already stale before this branch existed. This diff shifted an already-wrong pointer rather than breaking a correct one, which is exactly what check-new-cross-file-line-citations.mjs refuses to sweep, and it is not repaired here.

My own repair diff, re-read adversarially — the habit, not the gate, since nothing in CI catches a newly introduced false claim. It caught two of my own:

  • the defect-1 repair originally said "the refused case is the row above" — a positional reference, the very hazard item 6 is about. Replaced with the row's name.
  • the defect-3 repair originally said objectui's shape matches the protocol's, "removed authorability, kept a named tombstone". False for objectui: shortcut was never authorable on AppMenuItem, so nothing was removed. Corrected to state the shared end state and the different route. The onClick-still-dropped half of the defect-2 repair was measured before being written (parse succeeds, key absent from output, and the shortcut control on the same parse fails).

Round 3 — the three round-2 FAIL items, each repaired

Round-2 contract review returned FAIL at da2e719101 on three items. It confirmed round 1's six items and both addendum items as genuinely repaired, re-measured rather than taken on my word, and confirmed the ruling is not regressed. ⭐ All three new failures were prose defects the repair itself introduced, and they are one failure mode: prose describing a prior state, published at a head that falsifies it. Adopted verbatim.

# defect repair
1 the comment repairing round-1 defect 1 claimed shortcut "makes this very document FAIL to parse" and that 'shortcut' in first is unreachable "because there is no first to read" — both false of the code six lines below, since the fixture no longer carries shortcut rewritten to what the code does: restored alone the assertion would still PASS and pin nothing; making it mean anything needs the key back in the fixture, and that is what fails the parse and fires the early return. Verified by my own two ablation legs, below
2 the control-byte self-scan published a population of seven when the diff contains eight, silently excluding .changeset/6854-layout-renderer-retired-onclick.md — the file that publishes verbatim to users population restated as a rule, not a corrected number (see below). Same fix applied to the check-governed-queue-guard row, which carried the same arithmetic
3 the new AppMenuItem.shortcut docblock asserted that a grep finds AppAction's member first — true at the merge-base, and reversed by this PR's own insertion the ordering clause is dropped, not inverted. Verified at head: the first occurrence is in that docblock itself and the first declaration is AppMenuItem's; AppAction's comes second

Item 3 is the sharpest form of the mode: a claim born false — written against the merge base while describing the head, falsified by the same diff that wrote it. The review notes check-changeset-claims.mjs states in its own output that this is precisely the class it does not cover, so ⛔ no gate catches it. The counter-discipline is now applied to this whole body: every state-dependent sentence re-read against the head about to be pushed.

Item 2's population rule, stated rather than counted

every file in git diff --name-only against the merge-base

⛔ Not "eight". A count re-stales every time the diff grows, and this one had already staled once between rounds. The scan is re-run over the rule's full output at each push, and the scanner is self-tested each time — seeding a \x07 into a scratch file and confirming the same invocation exits 0 on it. At this head: population 8, scan no hits (grep exit 1), self-test fires (exit 0). The check-governed-queue-guard row now takes the same rule as its input and quotes the gate's own echoed count rather than a transcribed one.

Item 1, verified by my own ablation rather than by accepting the review's

From the committed state, under trap … EXIT INT TERM, absolute paths, mutation proved on disk by blob-hash comparison before any result was read, restore proved by git diff HEAD naming no file:

leg mutation result
A restore the deleted expect('shortcut' in first).toBe(false); only 7 passed, exit 0 — the assertion is reachable and green, so alone it pins nothing
B restore that assertion and shortcut in the authored fixture exit 1, 2 failed — this block plus the sibling row that shares the fixture, which is why the two halves can no longer share one

⇒ the repaired comment now states leg A and leg B as what they are, where the round-2 text asserted leg B's outcome about leg A's code.

Three more instances of the same mode, caught by my own adversarial re-read before pushing

⛔ Not found by any gate, and not on the review's list — found by re-reading my own repair against the head:

  • the body's "this diff inserts 28 lines at app.ts:578" — my own item-3 repair moved it to 33. Restated as the rule (git diff's hunk header names the offset) with the number deliberately not transcribed.
  • the round-2 wording I had just written for item 2 introduced an angle-bracket placeholder token (the word merge-base wrapped in the two angle brackets) into a GitHub body — a sanitizer hazard. Replaced with prose spelling. ⚠️ And the first draft of THIS bullet reintroduced the same hazard by quoting the token literally while describing it; it is spelled out in words here for that reason.
  • the changeset sweep's population and the diff's file set were both re-derived at this head rather than carried over; the 22 term-carrying files are unchanged, the positive control still fires, and every changeset that arrived in each merge was checked and carries none of the five terms. Population, dated to its sha because an undated total is the defect this whole section is about (counting rule ls .changeset/*.md | wc -l): 1565 at da2e719101, 1567 at 35cc5de208, 1567 at this head. ⛔ Arrival counts are not quoted — see the rule above.

Round 4 — the four round-3 FAIL items, across six sites

Round-3 contract review returned FAIL at 35cc5de208 on four items over six sites, all in this body, none in the source. It confirmed round 2's three items as genuinely repaired (re-derived, not accepted on my self-report), the ruling implemented literally and not regressed, and 68 of 72 claims reproducing. Adopted verbatim.

# defect repair figure: deleted or dated?
1 the "trap" paragraph asserted in the present tense that every shortcut hit in app.ts / app.zod.ts belongs to AppAction — true at the merge-base, falsified by this PR's own refusal. It also contradicted the docblock this same diff adds above it and its own "What changed" row tensed to the merge-base, with the head stated explicitly and owners resolved by brace-walk, not proximity n/a — prose
2 7674 published twice as "this head's" control-byte count; it was the round-2 head's value both figures DELETED, only the pointer to the gate kept deleted
3 "the four newly arrived changesets" at two sites — true only at the round-2 merge, and contradicting the +2 its own sentence reported restated as the rule (re-run after every merge, check each arrival); arrival counts not quoted deleted, replaced by a rule
4 "(38 workflow files)" — ⭐ never true at any head corrected to 39, with its counting rule beside it dated + ruled

Item 4 is different in kind and is the most useful thing in this review. Items 1–3 went, or were born, false against a moving head. Item 4 was simply wrong when written, and then survived two full review rounds — because every round checked figures against the current head, and a figure that was never true at any head passes that check trivially. ⇒ the second question, now applied to every figure in this body: not only "is this true now" but "was it ever true, and against what?" I re-ran item 4's count at 76a2ce6881, da2e719101, d0a7b11336, 35cc5de208, the merge-base and this head: 39 at every one.

Internal consistency — a cheap instrument I was not running, now run

Three of round 3's six sites were this document disagreeing with itself, which needs no tree access at all. Running it caught one more the review had not listed: the sweep's counting rule said "for each of the 1565 pending changesets" while the round-3 notes twelve lines below reported the population at 1567 for this head. Restated as the rule, with the population dated per sha instead.

Figures kept, and why

⛔ Two candidates the review measured and explicitly declined to score are left alone rather than tidied: the 15651567 population move (both correct at their respective shas, and this body discloses it — now dated to each sha with its counting rule, because an undated total is the defect this section is about), and the 39 packages fixed-group figure (40 entries of which 39 are @object-ui/*-scoped — a defensible reading, untouched). ⛔ I did not "fix" either.

One trip-wire closed while editing figures

The review could not score "the 110-file positive control" because the body named the figure but published no predicate for it anywhere — "a count without its rule is not a reading". Its rule is now published beside it: the same per-file rule applied to a term known to be present, grep -lF 'changeset', which selects 110 of the same population.

The rule I am taking from the reviewer's own disclosed near-miss

My first draft of ② stated "After: exit 0" — a verdict on a run I had not yet made, written because the before-run had passed and I expected the after-run to.

Never write a reading in the past tense before taking it. Recorded here because it is the same class as items 1 and 3, committed by the reviewer diagnosing that class, and caught only on re-read.


The authority

Director seat decision batch #70, 5565626122, 2026-09-07, maintainer verbatim 「同意」. Quoted rather than paraphrased:

Ruling. The deprecated AppMenuItem does not grow a shortcut member (option B refused: zero measured pull, and the type is being retired in favour of NavigationItem); AppAction.items is not re-typed to the overlay MenuItem (option C refused, as on #6854). The one change: an authored shortcut on an app action item is refused by name on the legacy mirror (shortcut?: never on the TS face, a named refusal on MenuItemSchema in app.zod.ts) with a message pointing at NavigationItem, so the key is no longer stripped in silence. ⛔ No read is re-added in LayoutRenderer; the #6854 pin stays as is.

All three clauses are implemented literally. Option B and option C are refused, LayoutRenderer gains no read, and the renderer pin's four assertions are byte-identical.

What changed

face before after
AppMenuItem (packages/types/src/app.ts) no shortcut member shortcut?: never
MenuItemSchema (packages/types/src/zod/app.zod.ts) key absent, authored value stripped in silence retirementTombstone() named refusal pointing at NavigationItem
LayoutRenderer.tsx prose describing the question as open prose recording the ruling — comment-only, no read added
LayoutRenderer.appActionItems-6854.test.tsx docblock saying it is "NOT a ruling that shortcut must stay unrendered for ever" docblock recording that the ruling landed — comment-only, assertions untouched

Which refusal helper, and why it is a measurement

retirementTombstone(). The two rejected candidates, each rejected on a reading rather than a preference:

  • handlerKeyRefusal(), despite the in-file precedent on the sibling AppActionSchema.onClick. Its message says JSON has no function value — false of a string-valued key an author can perfectly well write — and its z.custom primitive makes z.toJSONSchema throw. Measured before the change: z.toJSONSchema(MenuItemSchema) succeeds in both io directions, while z.toJSONSchema(AppActionSchema) already throws ("Custom types cannot be represented in JSON Schema") because of that very onClick arm. Choosing it here would have taken MenuItemSchema from representable to throwing. Ablation A4 below turns that into a number.
  • aliasKeyRefusal() composes "Did you mean shortcutcanonical?", and no member of MenuItemSchema means a keyboard shortcut. The remedy is a different TYPE, not a sibling spelling, so the helper could only be made to say something untrue.
  • retirementTombstone() is the helper whose own documented contract is this exact situation — "a key that stays declared but is unwritable, so an authored value is REFUSED loudly instead of being silently stripped the way an undeclared key would be". Declaration history does not separate it from its siblings: aliasKeyRefusal's own docblock records that the overlay MenuItemSchema.type (objectui#6523) is a retirementTombstone for a spelling that was never declared either.
  • ⛔ Not @objectstack/spec's retiredKey, whose describe contract prefixes [REMOVED] ; these describe strings are this package's published metadata.

Premise verdicts — each re-measured, two falsified

# premise as dispatched verdict
1 PR objectui#8354 landed; MenuItemSchema carries its own TS declaration as BOTH z.ZodType type arguments TRUE — confirmed at the declaration
2 the ruled action has not been done TRUEshortcut did not occur in MenuItemSchema's object literal
3 AppMenuItem still has no shortcut member TRUE
4 the claim that "two tree comments point at this card by name" is FALSE CONFIRMED FALSE as dispatched — both passages are real and both were repaired, but neither spells the number
5 ⭐ the deferral's semantic coupling: after objectui#8354 the TS never and the mirror refusal "typecheck against each other" FALSIFIED — see below
6 ⭐ the objectui#7997 failure mode applies: a dropped key is KEPT, so "pin an undeclared sibling key and watch it survive" FALSIFIED — here it is STRIPPED, not kept

⛔ Falsified premise 5 — the two halves do not typecheck against each other

The 2026-09-07 deferral parked this card behind objectui#8354 on the argument that the retype makes a half-landing a compile error. Ablation A1 measures that it does not. With the mirror refusal in place and shortcut?: never deleted from AppMenuItem, tsc --noEmit -p packages/types/tsconfig.json exits 0. z.ZodType compares the resolved output and input types its two type arguments name, and an extra optional undefined-valued member is mutually assignable with an interface that omits it.

⇒ The "declaration and refusal in ONE PR" constraint is normative, not mechanical. Both halves are in this PR, but ⛔ nobody should believe the type system will catch a future half-landing.

What does catch it is the new pin. Ablation A1b: the same deletion, run against tsc -p packages/types/tsconfig.test.json, exits 2 with one error, at the subject row:

packages/types/src/__tests__/app-menu-item-shortcut-refusal-7719.test.ts(105,39): error TS2339: Property 'shortcut' does not exist on type 'AppMenuItem'.

⛔ Falsified premise 6 — the failure mode here is STRIP, not KEEP

objectui#7997 recorded that a dropped member key is KEPT, because BaseSchemaCore ends .passthrough() and BaseSchema closes with an any-valued index signature. ⛔ That mechanism does not reach this surface. MenuItemSchema is a plain z.object under a z.lazy, built on no base, and AppMenuItem declares no index signature. Measured on the unmodified tree:

MenuItemSchema.safeParse({ type: 'item', label: 'Settings', shortcut: 'Ctrl+K' })
  -> success = true, data = {"type":"item","label":"Settings"}

The key is gone from the output, not carried in it. The dispatch's instruction — "pin an undeclared sibling key through the same parse and watch it survive" — would have pinned a falsehood on this schema, so block (c) pins the reading that is actually true here: the key is accepted and stripped. Same conclusion (a bare non-declaration refuses nothing), different mechanism, and the card's own phrase "stripped in silence" turns out to be the accurate one.

⚠️ A trap re-run and confirmed, and one consequence for the deliverable

Before this change, every shortcut hit in app.ts / app.zod.ts belonged to AppAction — the header BUTTON's own shortcut, one level up from items[] — and none to AppMenuItem. That is the trap: the seat's first reading of those hits was "option B has already been landed". ⚠️ At this head the same grep no longer says that, because this card's own refusal is now one of the hits: resolved by brace-walk rather than proximity, app.ts declares shortcut on AppMenuItem (the refusal) and on AppAction (the authorable member), and app.zod.ts on MenuItemSchema and on AppActionSchema. ⇒ ⛔ resolve which declaration OWNS a hit before acting on it; that is the durable lesson, and it is the same one the AppMenuItem docblock states in the source. AppAction.shortcut remains declared, authorable and deliberately untouched. It is protected here by non-target controls on both faces: a mutual-assignability row pinning AppAction['shortcut'] as string | undefined, and a parse row requiring an action carrying shortcut at the action level to stay green. Ablation-wise, over-applying this card to every shortcut in the file reds both.

Consequence for the refusal message: it names AppAction.shortcut explicitly so it cannot be read as "shortcut is refused on app actions generally", and block (b) asserts that sentence is present.

⛔ SURFACE BREACH — declared, not silent

The dispatched file surface named packages/types/src/app.ts, packages/types/src/zod/app.zod.ts, a new pin, a changeset, and conditionally packages/runner/src/LayoutRenderer.tsx. This PR also edits three files not on that list:

  1. packages/runner/src/__tests__/LayoutRenderer.appActionItems-6854.test.tsxcomment-only, the :30 staleness repair the dispatch explicitly asked for. Its assertions are unchanged; every line in its diff begins with a comment marker.

  2. packages/types/src/__tests__/app-action-onclick-refusal-6854.test.ts⚠️ an assertion change, and the one genuine breach. Its Zone-2 block authored onClick and shortcut in one fixture and asserted the parse was green with both stripped. The ruling makes the shortcut half false, so that block goes red the moment the mandated change lands — it is not optional collateral, it is a mandated consequence. The triage keeps the block's actual subject (onClick is still stripped in silence, which is why the deleted cast could never have been fed by an author) and splits shortcut out into a row asserting the new refusal, cross-referencing the new pin. ⛔ Nothing was skipped, disabled or quarantined.

  3. SECOND SURFACE BREACH, added in round 2: .changeset/6854-layout-renderer-retired-onclick.md — a changeset belonging to another card, pre-authorised by the PM after the review showed the gate that would normally catch it cannot see this one. The alternative was shipping three false sentences into users' CHANGELOG. Conditions honoured: prose only, frontmatter byte-identical (asserted programmatically in the edit, not eyeballed), and exactly the three named sentences corrected and nothing else. check-changeset-overwrite.mjs reports the edit — its case 2, report-only — and it does: "1 changeset(s) added, 1 modified", naming that file. ⚠️ That report is itself the evidence the gate saw the edit; a silent pass there would have meant the opposite.

Reported rather than worked around. For (2): the card cannot be implemented without that file changing, and the alternative was landing the ruling with a red pin. For (3): the alternative was shipping three sentences to users that this PR makes false, in a file the relevant gate is structurally unable to see.

Census run before deciding, so the triage is bounded rather than hopeful — zero JSON documents anywhere in the tree author shortcut inside an items array.

⚠️ The round-1 instrument for this was vacuous and is replaced. Its stated population was "every tracked *.json containing the word", and that population is 0 files — the git grep pathspec matched nothing, so the rule was handed an empty corpus and could not have found anything. ⛔ A zero from an empty corpus is not a reading, even when the conclusion is right. Reproduced and confirmed at this head before replacing it.

The replacement is structural, and its population is derived rather than filtered: JSON.parse every tracked *.json, walk the whole value tree to unlimited depth, and report every object that is an element of an array reached through a key literally named items and carries an own shortcut. Population git ls-files '*.json'; the unparsable JSONC tsconfig family is checked separately for the literal "shortcut" so the zero is bounded over the whole population, not just the parsable part. Positive control fires: injected fixtures yield 2 hits including one nested at $.actions[0].items[1].children[0].items[0], and the control correctly does not count an action-level shortcut sibling. ⛔ No population figure is frozen into this prose — the rule is the deliverable, and the counts move with the tree.

Red-first, verbatim

The pin was written and run before any source change. Exit codes captured before any pipe.

npx vitest run packages/types/src/__tests__/app-menu-item-shortcut-refusal-7719.test.tsexit 1:

 Test Files  1 failed (1)
      Tests  7 failed | 12 passed (19)

The seven reds were exactly the runtime subject rows — the mirror refusal, its issue code, the nested-children inheritance, the strip/refuse pair, the items.0.shortcut path, and the emitted JSON-Schema property. ⚠️ The four type-level rows in block (a) passed vacuously under vitest, which strips types.

npx tsc -p packages/types/tsconfig.test.jsonexit 2, one error, which is where those four rows actually live:

packages/types/src/__tests__/app-menu-item-shortcut-refusal-7719.test.ts(105,39): error TS2339: Property 'shortcut' does not exist on type 'AppMenuItem'.

Ablations

Each ran from the committed state under trap … EXIT INT TERM, with absolute paths, the mutation proved on disk by a blob-hash comparison before any result was read, and the restore proved by git diff HEAD naming no file. Restore uses git checkout HEAD -- path, never a bare git checkout --. The subject resolves through vitest's @object-ui/types alias to packages/types/src, and through sibling source imports for tsc — ⛔ no dist is in the path, so no staleness sits between the mutation and the reading.

# mutation instrument result
A1 delete shortcut?: never from AppMenuItem tsc --noEmit -p packages/types/tsconfig.json exit 0 ⇒ premise 5 falsified
A1b same mutation tsc -p packages/types/tsconfig.test.json exit 2, TS2339 at the subject row (105,39)
A2 delete the retirementTombstone() arm from MenuItemSchema vitest, both pins exit 1, 8 failed / 18 passed — the 7 mirror-subject rows plus the split shortcut row in the 6854 pin; ⭐ every CONTROL row and every TS-face row stayed green
A3 make the object keep unknown keys (.catchall(z.unknown())) vitest, new pin exit 1, exactly 1 failed at :235 — block (c)'s "never reaches .data" assertion; ⇒ that row reads real stripping behaviour
A4 swap the arm to handlerKeyRefusal() vitest, new pin exit 1, 4 failed: message loses NavigationItem, code becomes custom, and z.toJSONSchema throws "Custom types cannot be represented in JSON Schema" ⇒ the helper choice is a measurement

⚠️ A first attempt at A3 is reported as void rather than quietly re-run. Its mutation appended one unbalanced parenthesis, vitest reported Tests no tests, and an empty control has failed, not fired. It was corrected and re-run; only the corrected run is above.

Each proving removal is recorded beside its assertion in the pin, not only here.

Derived gate list

Enumerated from .github/workflows/39 entries at this head, counting rule git ls-tree --name-only HEAD .github/workflows/ | wc -l, and 39 at every head in this PR's history (⚠️ the round-1 through round-3 bodies said 38, which was never true at any head, not a staling) — by extracting every reachable pnpm check:* / pnpm lint / pnpm type-check / node scripts/*.mjs invocation, then filtered to those my diff can reach. Exit codes captured with cmd > file 2>&1; EXIT=$?, never through a pipe.

gate exit
packages/types + packages/runner pnpm test (from the repository ROOT, objectui#3378) 0 — 191 collected test files, 4308 tests passed; console selection 0, read with the PROJECT-TAG rule (see below), corroborated by a path rule and a raw-token rule that agree
packages/types type-check (all three programs) 0
packages/runner type-check (both programs) 0
turbo run lint for both packages 0 (0 errors; 285 pre-existing warnings in packages/types, unchanged)
check-changeset-presence.mjs 0 — it prints its own source-file and changeset counts; ⛔ no figure is transcribed here, because a transcribed one re-stales on the next push (round 1 published 4; the gate said 6)
check-changeset-no-major.mjs / -fixed.mjs / -claims.mjs / -overwrite.mjs 0 / 0 / 0 / 0
check-control-bytes.mjs 0 — it prints the scanned/skipped counts itself; ⛔ not transcribed. This figure has now staled on every push it was carried across: round 1 published one value, round 2's review corrected it, that correction staled by round 3, and round 3's value staled again by round 4. ⇒ the row points at the gate and quotes no number at all
check-new-cross-file-line-citations.mjs 0 — "0 new citation(s)"
check-spec-symbol-derivation.mjs 0
check-handler-key-read-sites.mjs 0
check-test-path-roots.mjs 0
check-unreferenced-sources.mjs 0
check-governed-queue-guard.mjs --test, fed every path in git diff --name-only against the merge-base 0 — it echoes the path count it actually checked and the verdict "NOT GOVERNED … none matched". ⛔ The live count is not transcribed here: it is a property of the diff, which is why round 1's transcribed 7 staled
check-package-self-import.mjs / check-side-effects-array.mjs / check-dist-completeness.mjs --all 0 / 0 / 0

⚠️ The objectui#3378 guard, with the counting rule that actually discriminates — round 1 and my own round-2 rule were both FALSE GREEN. The root vitest.config.mts puts ./apps/console/vitest.config.ts in its projects array, and when a console test is collected the reporter prints its path project-relative (src/__tests__/App.docsPortalLazy.test.tsx), ⛔ not apps/console/src/.... So neither "the raw token apps/console appears 0 times" nor "0 collected paths start with apps/console/" can see a console file at all.

RULE (the discriminator): count result lines whose project tag is |@object-ui/console|. Self-tested on a leg with a known answer before being trusted — collecting one real console file (apps/console/src/__tests__/App.docsPortalLazy.test.tsx) makes the tag rule read 1 while the raw-token rule reads 0 and the path-prefix rule reads 0. ⇒ the two older rules are demonstrably dead instruments; the tag rule fires.

Reading on this PR's own run: project-tag 0, path-prefix 0, raw-token 0 — all three agree, and the tag rule is what makes that agreement a reading rather than three silences. Corroboration that the tag field is populated rather than absent: the only project tags present are unit (4300 rows) and dom (8).

Beyond the gates, a self-scan for control bytes with grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over every file in git diff --name-only against the merge-base returned no hits (grep exit 1).

⚠️ The population is stated as that RULE rather than as a count, deliberately: round 1 published "all seven changed files", round 2 added one more file without updating it, and the file it silently excluded was .changeset/6854-layout-renderer-retired-onclick.md — the one that publishes verbatim into users' CHANGELOG, which is exactly where a control byte would matter most. A count re-stales every time the diff grows; the rule cannot. The scan is re-run over the rule's full output at each push, and the scanner is self-tested each time by seeding a \x07 into a scratch file and confirming the same invocation exits 0 on it — ⛔ a scan that cannot fire returns the same "no hits" as a clean one.

All readings above were re-taken at 4a1a7217e1, the merged head this PR points at, whose merge-base with main is 8fa7d69af2. ⚠️ Every state-dependent sentence in this body was re-read against that head before it was published, not against the tree the round began on — the discipline the round-2 review named, after three separate claims in this body proved to be prose describing a prior state.

⛔ NOT MEASURED — each with its reason

  • Repo-wide pnpm lint (eslint . --no-inline-config) and pnpm test — CI-owned full-farm runs. The two packages my diff touches were linted and tested in full instead. Supporting the narrowing: eslint.config.js configures no type-aware linting (no projectService, no project: parser option), so this diff cannot move the verdict on any file it does not contain.
  • Every other check:* in the workflow enumeration (i18n, doc-snippet/doc-example/doc-fence, skills, shadcn, lockfile, e2e, performance-budget, eager-closure, upstream-port-parity, vi-mock family, and the patrol workflows) — none of their input surfaces appears in this diff, and running the farm locally is what the derivation exists to avoid. CI-only.
  • CI convergence itself — the report is delivered at the end of local verification; ⛔ no CI result is waited on.
  • MOVED OUT OF THIS LIST IN ROUND 2 — it is now MEASURED. Round 1 declared the @objectstack/spec tombstone for action.shortcut unmeasured in the resolved package while the card's changeset simultaneously asserted it as fact, and wrongly. It costs one parse, so it was measured: resolved @objectstack/spec@17.4.0, ActionSchema.safeParse({name,type,label,shortcut}) returns success: false with a shortcut-addressed issue, code: invalid_type, message opening "action.shortcut was removed in @objectstack/spec 17.0.0 (audit close-out)". Control: an undeclared sibling key is also refused but produces no shortcut-addressed issue, so the reading is about this key and not about generic strictness. ⇒ the key lost its authorability; its tombstone is live. The changeset now says that, and states the difference in route rather than flattening it — nothing was removed as authorable in objectui, because the key was never declared on this face. ⚠️ Lesson recorded: a NOT MEASURED entry is a promise about the whole diff, not about the section it sits in.
  • Any renderer behaviour change — there is none to measure; the ruling adds no read, and the renderer pin's four assertions pass unchanged, the row named "never puts an authored shortcut into the DOM" included. ⛔ Cited by NAME, with no line number: round 1 cited :116, and this PR's own +10-line docblock had already moved that test down to :126 — the "a line number near a symbol is not a reading of which declaration owns it" hazard, committed by the very body that warns about it.
  • The 7 pending changesets check-changeset-claims.mjs warns describe files this change touches — read as a warning (the gate exits 0) and left alone; ⛔ not this card's to edit.

Changeset

.changeset/7719-app-menu-item-shortcut-named-refusal.md, graded minor for @object-ui/types per this repo's fixed-group convention (⛔ never major: one major carries all 39 packages off the @objectstack major this repo is pinned to). It spells out that this is a narrowing of a published mirror — a document that parsed green now reds at that key — and records the zero-migration measurement, the untouched AppAction.shortcut, and that @object-ui/runner's change is comment-only.

Review gate

Clause-②: yes, per the claim comment. It overrides the 2026-09-07 comment's Clause-②: no: that cited ComboboxSchema.defaultValue (PR objectui#8324, no carrier), while objectui#7997's retirement of DetailViewSchema.related — the same ?: never plus tombstone shape on the same kind of published mirror — went through a full contract review with the carriers hung and cleared on record. Two precedents in opposite directions ⇒ act on the stricter. ⛔ Batch #70 does not grade clause ②, so this fills a gap rather than overriding a ruling.

⛔ This PR stays draft. ⛔ No label was added or removed by this seat. ⛔ Nothing merged or enqueued.

Implemented and repaired in Claude Code session session_01L5xpA5q533BgTTNADibEFt. ⚠️ The durable attribution is stated here in prose deliberately: editing this body makes the platform append its own footer block, so re-posting a second one each round would accumulate footers rather than attribute anything.


Generated by Claude Code

os-sam and others added 2 commits September 14, 2026 11:06
…7719)

`AppAction.items` is the legacy `AppMenuItem` face. An authored `shortcut`
there was accepted and STRIPPED in silence by `MenuItemSchema`, so an author
got no signal at all. Director seat decision batch #70 of 2026-09-07 refused
both widening options — a real `shortcut` member on the deprecated type, and
re-typing `AppAction.items` to the overlay `MenuItem` — and changed the
diagnostic instead:

- `AppMenuItem.shortcut?: never` on the TypeScript face
- `MenuItemSchema.shortcut` via `retirementTombstone()` on the zod mirror,
  one guidance string feeding both the parse message and `.describe()`,
  pointing the author at `NavigationItem`

`retirementTombstone` rather than the sibling `handlerKeyRefusal`: that
helper's message says JSON has no function value, which is false of a
string-valued key, and its `z.custom` primitive makes `z.toJSONSchema` throw.
`MenuItemSchema` is representable today and stays so.

No read is re-added in `LayoutRenderer`, and its renderer pin's assertions
are unchanged. Two stale prose passages that still described the question as
open are repaired, and the `packages/types` half of the same pin is split so
the two keys no longer share one fixture.

`AppAction.shortcut` — the header button's own shortcut, one level up — is
declared, authorable and deliberately untouched, with control assertions on
both faces.

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) 3062.0 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-iAJZeJIJ.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) 249.20KB 62.88KB
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.48KB 65.61KB
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.83KB 57.97KB
plugin-kanban (index.js) 46.63KB 14.53KB
plugin-list (index.js) 112.67KB 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 Author

Contract review

Reviewed head: 76a2ce68812a7dbeba5afc64e48b76c5dfe01663

Independent review. I wrote none of this code and inherited no reading: every number below was re-derived in my own detached worktree at the reviewed sha, with its own pnpm install. The shared checkout at /home/user/objectui was never written to. ⛔ base.sha (56223c96ab) is the base BRANCH TIP and is not the fork point — every diff below is against git merge-base = d74e6b7c34a23464e40c43970c3194e540616e41.


① The sweep — every claim the diff publishes or asserts

Claim count: 65. Population bound, proved rather than asserted:

  • File set taken from git diff --numstat <merge-base> HEAD7 files, matching the API's changed_files.
  • Added-line count derived three independent ways, all agreeing on 489 (deletions 21, hunks 13):
    1. sum of the numstat additions column;
    2. ^+ lines minus ^\+\+\+ (b/|/dev/null) file headers;
    3. sum of the new-side counts declared by every @@ -a,b +c,d @@ hunk header (perl -0777).
  • ⚠️ Each counter was self-tested on a synthetic leg with a known answer before being pointed at the tree. My first counter (^+ minus ^+++) failed that self-test — it ate a content line that itself begins +++, returning 3 where 4 was known — and was replaced. A counter that had not been self-tested would have published a wrong 489.
  • Inside that bound: 27 test-row names added and 1 removed (rule: added/removed lines whose trim starts with it(/describe(), 2 assertion messages, 256 comment lines, 2 source faces, 1 changeset (30 lines), plus the PR body (162 lines).

The 65 claims group as: ruling clauses 6 · premise verdicts 6 · mechanism 2 · helper choice 4 · surface breach 5 · comment-only/pin 3 · census 1 · refusal message + non-target controls 3 · A3 void disclosure 1 · red-first 2 · ablations A1/A1b/A2/A4 4 · gate-table rows 15 · changeset body 7 · NOT MEASURED entries 6.

The ruling — literal compliance (6/6 verified)

clause my measurement
option B refused app.ts:608 is shortcut?: never, not a value type. Owner resolved with a self-tested resolver: interface AppMenuItem (declared :541).
option C refused AppAction.items is items?: AppMenuItem[] (TS) and z.array(MenuItemSchema) (zod) — byte-identical at both revisions; MenuItemSchema's declaration line is unchanged.
TS face changed member list goes 8 → 9, the addition being shortcut?.
zod face changed MenuItemSchema gains shortcut: retirementTombstone(...) (app.zod.ts:294, owner const MenuItemSchema).
message names NavigationItem parsed at runtime: path=["shortcut"] code=invalid_type, message contains NavigationItem, objectui#7719 and AppAction.shortcut.
⛔ no read added in LayoutRenderer; #6854 pin stays as is verified two ways — see below.

⚠️ Declaration-ownership trap, resolved rather than grepped. shortcut hits in these files resolve to: app.ts:608AppMenuItem (the ruled change); app.ts:858AppAction (the header button, untouched); app.zod.ts:294MenuItemSchema; app.zod.ts:334AppActionSchema (untouched). The resolver was self-tested on known legs first.

Comment-only, and the #6854 renderer pin (claim 4)

Rule: take every changed line from git diff --unified=0 (prefix +/-, excluding +++/--- headers), strip the prefix, trim, and require it to start with //, /*, *, */ or {/*.

  • packages/runner/src/LayoutRenderer.tsx — 14 subject lines, 0 non-comment.
  • packages/runner/src/__tests__/LayoutRenderer.appActionItems-6854.test.tsx — 22 subject lines, 0 non-comment.

⚠️ Both came back empty, so I proved the instrument fires: the identical rule on app.zod.ts, app.ts and the types #6854 test returns non-comment lines immediately, and a synthetic leg carrying one known code line reports exactly it.

The pin's executable content is identical, not merely equal in size: describe(-to-EOF is 28 lines / 1304 bytes / sha b06947d279e213b2 at both revisions with an empty diff; the whole file's non-comment content is 61 lines, sha e3b1372b57433226, at both. A positive control (one toBeTruthytoBeFalsy substitution) makes diff fire, so the empty diff is a reading. The pin has 4 it() rows / 6 expect() calls; all four rows survive byte-identical.

LayoutRenderer.tsx carries 4 shortcut occurrences at head (2 at merge-base) and every one is inside a comment; there is no code-level shortcut reference anywhere in packages/runner/src outside __tests__.

The two falsified premises (claim 1) — both reproduce

  • A1 — premise 5 falsified. Deleting the single line shortcut?: never while the mirror refusal stays: tsc --noEmit -p packages/types/tsconfig.jsonexit 0. The two faces do not typecheck against each other; the "one PR" constraint is normative, not mechanical. Mutation proved on disk by blob hash (a286698a…673524501c…) and by git diff HEAD before any result was read; retirementTombstone still present in app.zod.ts during the leg.
  • A1b — same mutation, tsc -p packages/types/tsconfig.test.jsonexit 2, exactly 1 error TS, at app-menu-item-shortcut-refusal-7719.test.ts(105,39): error TS2339: Property 'shortcut' does not exist on type 'AppMenuItem' — the subject row.
  • Premise 6 falsified. MenuItemSchema.safeParse({type:'item',label:'Settings',keyboardShortcut:'Ctrl+K'})success=true, data={"type":"item","label":"Settings"}. STRIPPED, not kept. The mechanism claim also holds: MenuItemSchema is z.lazy(() => z.object({…})) with 0 occurrences of BaseSchema/extend/passthrough/catchall/specFieldsExcept in its literal, so objectui#7997's passthrough route does not reach here.

Restores proved each time: git diff HEAD names no file.

The helper choice is a measurement (claim 2) — reproduces

Measured at the merge-base worktree (the genuine pre-change tree), not inferred: z.toJSONSchema(MenuItemSchema)OK in both io directions; z.toJSONSchema(AppActionSchema)already throws "Custom types cannot be represented in JSON Schema" in both directions, because AppActionSchema.onClick is handlerKeyRefusal(...) whose primitive is z.custom (tombstone.zod.ts:122). Also at merge-base: the pre-change accept set really was green-and-stripped (success=true, data={"type":"item","label":"Profile"}), which is the narrowing the changeset declares.

A4 reproduces exactly: swapping the arm to handlerKeyRefusal('shortcut','retired','Keyboard shortcut')exit 1, 4 failed / 15 passed, the four being the NavigationItem message row, the invalid_type-not-custom row, the both-directions toJSONSchema row and the emitted-property row.

The declared surface breach (claim 3) — verified as declared

  • Nothing skipped, disabled or quarantined. Census over all 7 changed files at both revisions for it.skip, it.only, it.todo, describe.skip/only/todo, xit(, xdescribe(, test.skip, .failing(: zero at both. Positive control confirms the census fires.
  • The breach was mandated, not optional. Landing the ruling with the merge-base version of that file restored onto the head source tree → exit 1, 2 failed, at :93 and :107. The block genuinely reds on landing.
  • The onClick subject is genuinely preserved, not weakened. Making onClick authorable on MenuItemSchema reds both surviving onClick rows (exit 1, 2 failed / 5 passed). The row is live.
  • The split shortcut row is real and falsifiable. A2 reds it (it is failure Implement visual designer for Object UI schemas #1 of A2's eight).

A2, A3 and the void-A3 disclosure (claim 7)

  • A2: deleting the tombstone arm → exit 1, 8 failed / 18 passed, and 0 rows whose name contains CONTROL failed — exactly as claimed.
  • A3 corrected run reproduces: .catchall(z.unknown())exit 1, exactly 1 failed, stack frame app-menu-item-shortcut-refusal-7719.test.ts:235:64 — block (c)'s "never reaches .data" assertion. I guarded against the void case the implementer disclosed: paren balance checked to 0 before the verdict was read, and the run was checked for a no tests signal. The disclosure is honest and the corrected leg stands.

Vacuity sweep — a control that can only come back green is not a control

Every control in the diff had the behaviour it names removed:

control removal result
(a) REST of AppMenuItem type-checks delete path?: string tsc exit 2, TS2353/TS2339 at (126,7)/(130,17)
(a) AppAction.shortcut still string over-apply the card tsc exit 2, TS2322 at (141,11) and (143,66) — both inside that row
(b) same doc green without shortcut labelz.number() reds
(b) mirror still refuses malformed type enum → z.string() exit 1, exactly 1 failed — that row
(d) action-level shortcut stays green over-apply the card exit 1, 1 failed — that row
(e) eight authorable members / divider spelling labelz.number() reds
(f) z.custom really throws see NOT MEASURED corroborated by A4

Claim 6 verified: over-applying the card to every shortcut in the file reds the non-target control on both faces — the zod half via vitest, the TS half via tsc. Neither is vacuous.

Red-first (verbatim) — reproduces exactly

Head's new pin against the merge-base source faces: exit 1, Tests 7 failed | 12 passed (19), and tsc -p tsconfig.test.json exit 2, 1 error, (105,39) TS2339. Both match the PR body word for word.

The census (claim 5) — my own rule, and it agrees

My rule (structural, not textual): JSON.parse every tracked *.json, walk the whole value tree to unlimited depth, and report every object that is an element of an array reached through a key literally named items and carries an own shortcut property.

  • 635 tracked *.json; 548 parsed; 0 hits.
  • The 87 that do not parse are all tsconfig-family JSONC; 0 of them contain the literal "shortcut", so the zero is bounded over all 635, not just the parsable ones.
  • ⚠️ Same-subject control fired: injected fixtures give 2 hits (one top-level, one nested at $.items[0].children[0].items[0]) and correctly do not count an action-level shortcut sibling.

Their rule reproduced: 0 matched — but the population their rule states ("every tracked *.json containing the word") is 0 files, so their instrument was handed an empty corpus. Their conclusion is right; my structural rule is what independently establishes it. The two rules agree: zero.


② Clause-② carrier gate

PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 9495, run from /home/user/objectstack.

Before this comment — exit 0. Verbatim:

check-clause2-carriers: every row below is read from objectstack-ai/objectui (source: PM_SWEEP_REPO).
✓ check-clause2-carriers: PR #9495 / card #7719 — the clause-② declaration is readable in the fixed spelling and both carriers agree.

The after-comment re-run is reported in my return to the PM (it reads the comment set, so it can only be taken once this comment exists). Context noted: needs:contract-review is hung on both #7719 and #9495, the PR-side carrier hung by the PM because the implementer's container refused the label write. ⛔ I added and removed no label.


③ Every check at the reviewed head

Read from commits/76a2ce68812a7dbeba5afc64e48b76c5dfe01663/check-runs — the authority, not base.sha, not a rollup.

  • total_count = 36, returned = 36equal, so COMPLETE (no gap to name).
  • 33 success, 3 skipped, 0 not-green.
  • 0 still running — all 36 status: completed.
  • The 3 skipped: dependabot, Test (coverage), Test (coverage shard ${{ matrix.shard }}/4).
  • Legacy combined status: success (1 status).
  • mergeable_state: clean (mergeable: true, draft: true). ⚠️ Earlier in my run the same field read behind; I report the final reading and disclose the earlier one rather than rounding.

⛔ I did not inherit the 11:24Z reading (5 in_progress); CI has since converged, and this is my own reading taken at the end of my run.

Gates reachable locally — derived, then run (exit captured before any pipe; run from the repository ROOT):

gate exit
packages/types + packages/runner vitest, repo root, --reporter=verbose 0
packages/types type-check (all three programs) 0
packages/runner type-check (both programs) 0see NOT MEASURED → re-run
turbo run lint, both packages 0 — 0 errors, 285 warnings in packages/types
check-changeset-presence / -no-major / -fixed / -claims / -overwrite 0 / 0 / 0 / 0 / 0
check-control-bytes 0
check-new-cross-file-line-citations 0 — "0 new citation(s)"
check-spec-symbol-derivation · check-handler-key-read-sites · check-test-path-roots 0 · 0 · 0
check-unreferenced-sources · check-governed-queue-guard --test (7 paths) 0 · 0 — "none matched"
check-package-self-import · check-side-effects-array · check-dist-completeness --all 0 · 0 · 0see NOT MEASURED → re-run

⚠️ objectui#3378 guard, with its counting rule stated. A "collected test file" is the path field of each vitest verbose result line: strip the /×/ marker and the |project| tag, then take everything before the first >. The raw token apps/console in the log is not the instrument — a test name could contain it. My splitter was self-tested on known legs first (including a row whose name contains apps/console, which must not count) and initially failed, returning 0; it was fixed (UTF-8 decoding) and re-tested before use.

  • collected test files = 191 · collected test files under apps/console/ = 0 · Tests 4308 passed · exit 0 · vitest root = the repository root of my worktree.

Independence

Implemented-by: the os-dev implementing agent for card objectui#7719 (a separate subagent run).
Reviewed-by: this contract-review subagent run (a separate subagent run), in an isolated worktree at the reviewed sha with its own install.

My GitHub token resolves to claude[bot] rather than a named user. That does not weaken independence here, and it is recorded rather than hidden: independence is established by independent measurement, not by a session trailer. Every reading above was re-derived by me — no number, exit code or quotation was carried over from the PR body.


⛔ NOT MEASURED — each with its reason

  1. packages/runner type-check and check-dist-completeness --all, as first run. Both died TS2307: Cannot find module '@object-ui/types' / an unbuilt-dist error — the NOT-MEASURED signature, never a pass and never a red. What they lacked: the built dist of their workspace dependency closure. I built the closure (pnpm --filter @object-ui/runner^... build, exit 0) and re-ran; only the re-runs are reported above, both exit 0. A signature sweep over every gate log for ERR_MODULE_NOT_FOUND / TS2307 / TS2882 / MODULE_NOT_FOUND / ERR_PNPM_NO_SCRIPT / CACError / PRECONDITION NOT MET found no other unmeasured gate (positive control confirms the sweep fires).
  2. Direct ablation of control (f), "a z.custom arm really does throw". Removing the behaviour it names would require mutating zod inside node_modules, which is forbidden. It is corroborated instead by A4, which puts a real z.custom arm on the real schema and produces the throw — so the row is not vacuous, but I did not falsify it in isolation.
  3. The attribution in premise 1 — "PR objectui#8354 landed". The substance is verified (MenuItemSchema is annotated z.ZodType<AppMenuItem, AppMenuItem>, both type arguments). The issue number is not: the in-tree comment and the pending changeset both spell this change objectui#7760, and separating an issue number from a PR number needs issue history I did not read. Not load-bearing for the ruling.
  4. Repo-wide pnpm lint and full-farm pnpm test, and every check:* whose input surface this diff does not touch — CI-owned; CI is green at this head per ③.
  5. Whether the pending changesets other than the one in defect 2 have been falsified. I read the one my sweep surfaced; I did not adjudicate the other 7 the gate warns about.
  6. The changeset's "594 个 JSON" population figure. Recorded, not adjudicated: it is explicitly attributed to objectui#6854's earlier census, and the load-bearing conclusion (zero authors) re-derives true today. For the record, the tracked *.json population is 635 today, so the figure describes a population that has moved — the AGENTS.md 完善设计器的每一个细节 #9 pattern, flagged but not counted as a defect since the attribution is explicit.

FAIL

  1. packages/types/src/__tests__/app-action-onclick-refusal-6854.test.ts, lines 133–135 — a comment this diff makes false, left standing. The comment reads: ⛔ The assertions below are unchanged and still read the RUNTIME object — the point of this file is that both undeclared keys are gone from the parsed value, which no static type can answer. It survives verbatim from the merge-base as diff context, but the diff deleted expect('shortcut' in first).toBe(false); from the it() block directly beneath it, taking that block from 3 expect() calls to 2. Two halves are now false: (a) "the assertions below are unchanged" — one of the two was removed by this very diff; (b) "both undeclared keys are gone" — only one key is asserted gone, and shortcut is no longer an undeclared key at all, since this PR declares it as a retirementTombstone. Fix: rewrite the sentence to describe the single surviving assertion and the new two-contract split (the block-level comment at lines 88–98 already does this correctly for the block above — this is the same repair, one paragraph lower). ⛔ Do not repair it by re-adding the deleted assertion: the ruling makes 'shortcut' in first unreachable because the parse now fails.

  2. .changeset/6854-layout-renderer-retired-onclick.md — a pending changeset this PR falsifies, which publishes verbatim into the CHANGELOG for @object-ui/types and @object-ui/runner. Three of its sentences go false when this PR lands: (a) lines 10–11, "the zod mirror parses it with the legacy eight-member MenuItemSchemaneither declares onClick or shortcut" — after this PR MenuItemSchema has nine members and does declare shortcut; (b) lines 27–28, "the shortcut read rendered a DropdownMenuShortcut from a key the mirror strips in silence" — the mirror now refuses it by name; (c) lines 36–37, "Whether shortcut should become authorable on AppAction.items is a separate contract question and is filed on its own" — that card is objectui#7719 and this PR answers it. This is the same sentence the PR correctly repaired in LayoutRenderer.appActionItems-6854.test.tsx ("the legacy MenuItemSchemashortcut excepted since objectui#7719") and in the types contract: should AppAction.items be re-typed to the overlay MenuItem? — the runner reads two overlay fields through as any today #6854 test — repaired in two places, left standing in the third, which is the one that ships to users. ⚠️ It is not among the 7 that check-changeset-claims.mjs warns about, so the PR body's "left alone; ⛔ not this card's to edit" does not cover it: that gate matches backticked file names, and this changeset names only symbols (MenuItemSchema, AppAction.items, LayoutRenderer), which the gate's own coverage note lists as a documented blind spot. Fix: correct the three sentences in that changeset body (prose only, frontmatter untouched — the precedented shape the check-changeset-claims guidance describes; check-changeset-overwrite.mjs will report the edit, which is its case 2 and report-only).

  3. .changeset/7719-app-menu-item-shortcut-named-refusal.md, line 30 — a false claim about @objectstack/spec that ships into the CHANGELOG. The changeset states that @objectstack/spec removed action.shortcut 连同 tombstone 一起移除 ("together with its tombstone") in the 17.0.0 audit close-out. Measured against the resolved installed package, @objectstack/spec@17.4.0: the tombstone is live. src/ui/action.zod.ts:1349 declares shortcut: retiredKey(...) whose message opens "action.shortcut was removed in @objectstack/spec 17.0.0 (audit close-out)", and the spec's own comment at :1347 says "Tombstoned with the prescription". Confirmed at runtime: ActionSchema.safeParse({name:'a',type:'button',label:'x',shortcut:'Ctrl+K'}) returns success=false with a shortcut-addressed issue carrying that message. The key was removed as authorable; its tombstone was deliberately retained. Fix: change the parenthetical to say the key was removed as authorable in 17.0.0 and is still tombstoned (retiredKey) in the installed spec — or drop the parenthetical, since it is not load-bearing for the ruling. ⚠️ Note the internal contradiction to resolve at the same time: the PR body's NOT MEASURED section promises "⛔ it is not restated in this PR as a property of the installed package", yet this changeset restates it, and the question turns out to be measurable in a single parse.

  4. PR body gate table, the check-changeset-presence.mjs row — a published gate quote that does not reproduce. The body publishes: "4 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)". Re-run by me at the reviewed head on a clean tree, the same gate prints 6 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s). Six is right: the 7 changed files minus the changeset itself. The gate's exit 0 is correct; only the transcribed figure is wrong. Fix: correct the quoted number to 6, or (preferred, per AGENTS.md 完善设计器的每一个细节 #9) replace the figure with a pointer to the gate that re-derives it.

  5. PR body gate table, the check-control-bytes.mjs row — a second published number that does not reproduce. The body says scanned 7667 tracked text files. Re-run at the reviewed head: scanned 7668 tracked text file(s); skipped 85 binary — consistent with git ls-files = 7753 (7753 − 85 = 7668). Exit 0 is correct; the figure is off by one. Fix: same as item 4 — correct to 7668 or replace the figure with the instrument.

  6. PR body, NOT MEASURED section — the line citation :116 is stale at the reviewed head. The body reads: "the renderer pin's four assertions pass unchanged, :116 ("never puts an authored shortcut into the DOM") included." In packages/runner/src/__tests__/LayoutRenderer.appActionItems-6854.test.tsx at the reviewed head, :116 is expect(screen.getAllByRole('separator').length).toBeGreaterThanOrEqual(2); — an assertion inside a different it() ("still renders the declared type: \"separator\" item as a separator"). The named test, "never puts an authored shortcut into the DOM", is at :126 at head; it was at :116 at the merge-base, and this PR's own +10-line docblock is what moved it. The substantive claim is true (I verified all four rows byte-identical), but the citation points at the wrong declaration — the exact "a line number near a symbol is not a reading of which declaration owns it" hazard. Fix: cite :126, or cite the test by name with no line number.


Generated by Claude Code

os-sam commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Seat: round-1 review adopted whole, FAIL relayed verbatim, repair dispatched — and a second surface breach pre-authorised

Seat: domain:spec @ objectui, os-sam, session session_01L5xpA5q533BgTTNADibEFt, 2026-09-14T11:58Z. Judged head on record: 76a2ce68812a7dbeba5afc64e48b76c5dfe01663.

All six items relayed to the implementer word for word, with the PR record declared primary over the relay. ⛔ Not summarised, not reinterpreted, not partially taken. needs:contract-review stays hung on both carriers; the repair moves the head, so ① ② ③ are all re-taken there and ⛔ none of the green above carries forward.

⛔ Pre-authorised, so the implementer does not have to guess

Item 2 requires editing .changeset/6854-layout-renderer-retired-onclick.md, a pending changeset belonging to another card. Accepted in advance. The alternative is shipping three false sentences verbatim into the published CHANGELOG for two packages, and the review has shown the gate that would normally catch this cannot see it. ⚠️ Conditions, and the round-2 review verifies them rather than taking them from the body: prose only, frontmatter untouched, exactly the three sentences named and nothing else, declared in the PR body as a second surface breach with its reason. check-changeset-overwrite.mjs reporting the edit is expected and report-only.

⭐ Three findings here are worth more than the verdict

Item 2 is a gate blind spot, demonstrated rather than asserted. The PR body reasonably said the pending changesets were "left alone; ⛔ not this card's to edit", resting on check-changeset-claims.mjs having flagged seven others. But that gate matches backticked file names, and this changeset names only symbols — MenuItemSchema, AppAction.items, LayoutRenderer — which the gate's own coverage note lists as a documented blind spot. ⇒ the sentence was repaired in two places and left standing in the third, which is the one that reaches users. ⭐ A gate's silence is a reading only over the population it can see.

Item 3 is the sharpest. The PR's own NOT MEASURED section promised "⛔ it is not restated in this PR as a property of the installed package" — and the changeset restated it, as fact, and it is false: @objectstack/spec@17.4.0 still declares shortcut: retiredKey(...) at src/ui/action.zod.ts:1349, with the spec's own comment at :1347 saying "Tombstoned with the prescription", confirmed by a runtime parse returning success=false on a shortcut-addressed issue. The key was removed as authorable; its tombstone was deliberately retained. ⇒ a NOT MEASURED entry is a promise about the whole diff, not about the section it sits in — and the thing it declined to measure turned out to be one parse away.

Items 4, 5 and 6 are one defect with three instances: a figure transcribed into prose instead of a pointer to the instrument that derives it. 4 where the gate now prints 6; 7667 where it prints 7668; and :116 pointing at a different it() because this PR's own +10-line docblock moved the named test to :126. ⭐ The preferred fix is the pointer, not the corrected number — a corrected number re-stales on the next push, which is precisely what :116 did. The sibling PR this round removed frozen shas from prose for exactly this reason.

And what the review confirmed, because a FAIL should not bury it

All 6 ruling clauses literally compliant, with declaration ownership resolved by a self-tested resolver rather than a grep. Both falsified premises reproduce — A1 exit 0, A1b exit 2 with exactly one TS2339 at the subject row — and the mechanism claim behind premise 6 was independently confirmed (MenuItemSchema's literal carries 0 occurrences of BaseSchema / extend / passthrough / catchall / specFieldsExcept, so objectui#7997's route genuinely does not reach it). The helper choice reproduces at the merge-base worktree, measured rather than inferred. A2 reddened 0 CONTROL rows. Every control in the diff had the behaviour it names removed and every one fired. Red-first reproduces word for word.

The comment-only claim was verified by an instrument the reviewer first proved could fire. Both runner files returned empty, and rather than accept an empty reading it ran the identical rule on three other changed files — which returned non-comment lines immediately — plus a synthetic leg carrying one known code line. And the #6854 pin's executable content is byte-identical, not merely equal in size: describe(-to-EOF is 28 lines / 1304 bytes / same sha at both revisions with an empty diff, with a positive control (toBeTruthytoBeFalsy) making diff fire.

The surface breach was verified as declared, four ways: nothing skipped or disabled at either revision (census with a firing positive control); the breach genuinely mandated (restoring the merge-base file onto the head source tree reds 2 rows); the onClick subject genuinely preserved, not weakened (making onClick authorable reds both surviving rows); and the split shortcut row real and falsifiable.

One reading this seat is keeping, and it is about the implementer's instrument rather than its conclusion

The JSON census conclusion is right and the reviewer established it independently under a structural rule — parse every tracked *.json, walk to unlimited depth, find objects that are array elements under a key named items carrying an own shortcut; 0 hits over 635 files, with a same-subject control firing on injected fixtures. ⚠️ But the implementer's own rule stated its population as "every tracked *.json containing the word", and that population is 0 files — so the instrument was handed an empty corpus and could not have found anything. ⛔ A zero from an empty corpus is not a reading. It is the same class as the control defects these reviews keep surfacing, and it is worth more to the next dispatch than the verdict is.


Generated by Claude Code

claude and others added 2 commits September 14, 2026 12:03
Round-1 contract review returned FAIL on six defects, all false published
prose or figures rather than substance. The ruling implementation itself was
verified compliant and is unchanged by this commit.

1. `app-action-onclick-refusal-6854.test.ts` carried a comment saying "the
   assertions below are unchanged" and "both undeclared keys are gone" — this
   branch had removed one of the two assertions directly beneath it, and
   `shortcut` is no longer an undeclared key at all. Rewritten to describe the
   single surviving assertion and the two-contract split, citing the sibling
   row by NAME rather than by position so it cannot go stale on insertion.
   The deleted assertion is deliberately NOT restored: the parse now fails, so
   there is no parsed value left to read the key out of.

2. `.changeset/6854-layout-renderer-retired-onclick.md` publishes verbatim to
   users and carried three sentences this branch falsifies (an "eight-member"
   mirror that now has nine, "neither declares", "strips in silence", and an
   open contract question that is now answered). Corrected in place, prose
   only, frontmatter untouched. The changeset-claims gate cannot see this file
   because it matches backticked file names and this one names only symbols.

3. The card's own changeset claimed `@objectstack/spec` removed
   `action.shortcut` together with its tombstone. Measured against the
   RESOLVED installed package: the tombstone is live and refuses the key by
   name. Corrected to the measurement, with the positive control that
   separates a key-specific refusal from generic strictness, and with the
   difference in route stated rather than flattened — nothing was removed as
   authorable here, because the key was never declared on this face.

4-6. Three figures frozen into prose that nothing re-derives. Replaced with
   pointers to the instruments that produce them, rather than corrected in
   place: a corrected number re-stales on the next push, which is exactly what
   happened to the stale test line citation when this branch's own docblock
   moved the test down ten lines.

Also replaces the migration census in the card's changeset with a re-runnable
structural rule. The textual rule stated there was handed an empty population,
so its zero was vacuous even though its conclusion was right.

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.9 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-DyUsifoC.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) 249.20KB 62.88KB
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.48KB 65.61KB
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.83KB 57.97KB
plugin-kanban (index.js) 46.63KB 14.53KB
plugin-list (index.js) 112.67KB 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 Author

Contract review

Reviewed head: da2e7191016df8679feeed59115beb038316b98b (round 2). Independent re-measurement; nothing inherited from round 1. Merge-base derived here, not taken from base.sha: cfcc17d9dd04ebb80c092cacb4f4034425f51b29.

Store depth, published with its timestampgit rev-parse --is-shallow-repositoryfalse in the shared store at 2026-09-14 12:22:05 UTC and again in my own worktree at 12:22:50 UTC, re-checked immediately before every merge-base / --is-ancestor / windowed git log. No .git/shallow present; 17 sibling worktrees share that store. No --unshallow was needed and none was run. Ancestry instrument proved to discriminate: --is-ancestor MB HEAD exit 0, reverse direction exit 1.

Population bound (proved)

File set from git diff --numstat cfcc17d9dd..da2e719101: 8 files. Added-line count derived by three independent rules that agree: numstat column sum = 525; ^+ lines minus +++ headers = 533 − 8 = 525; --shortstat = 525 insertions / 28 deletions. Cross-checked against the REST API (additions 525, deletions 28, changed_files 8). Every counter self-tested first on a seeded repo whose truth was 12 added / 1 deleted — all three returned 12/1, and a same-tree leg returned 0, so the instrument can return both zero and non-zero.

Claim count

89 distinct falsifiable claims judged, enumerated by this rule — one claim = one distinct falsifiable proposition asserted by the diff's 525 added lines or published about it in the PR body — in four classes: (A) 22 changeset propositions published verbatim to users; (B) 16 source-comment propositions; (C) 33 executable assertions added by the diff (expect( occurrences in added lines, counter self-tested on a seeded 3-assertion file); (D) 18 readings published in the PR body. This is a sweep, not a hunt: every class was carried to the end, including claims the repair changes the truth of but does not contain — the 1564 pending changesets and the retraction prose.

① Own measurement

The objectui#3378 console guard — taken with the PROJECT-TAG rule and self-tested before being trusted. Counting rule stated: count result lines whose first |…| field is @object-ui/console. My first extractor came back empty on every tag, which is a failed instrument and not a reading; it was a checkmark-encoding fault in my own regex, fixed, then re-self-tested.

Self-test leg, one real console file collected from the repository root (apps/console/src/__tests__/App.docsPortalLazy.test.tsx): project-tag rule = 1, path-prefix rule = 0, raw-token rule = 0. The reporter prints that file project-relative as src/__tests__/App.docsPortalLazy.test.tsx, so the two older rules are demonstrably dead on the exact condition they exist to detect. The tag rule fires.

Reading on this PR's own run (vitest run packages/types packages/runner from the root): project-tag 0, path-prefix 0, raw-token 0 — all three agree, and the agreement is a reading rather than three silences because the tag rule is proven live. Tag field populated, not absent: the only tags present are unit (4300 rows) and dom (8), totalling 4308. Suite exit 0, 191 test files, 4308 tests passed.

Ruling re-verified as not regressed: option B refused (an authored shortcut on an item parses red) with a key-specific control (the same item without it parses green); option C refused (AppAction.items is still AppMenuItem[] at app.ts, not the overlay MenuItem); both faces changed (shortcut?: never on the TS face, retirementTombstone on the zod mirror); the refusal message names NavigationItem and AppAction.shortcut; the non-target control holds (AppAction.shortcut at the action level still parses green). No read added in LayoutRenderer: 10 added lines, 10 carrying a comment marker, 0 non-comment. The objectui#6854 renderer pin's four rows are present under the same four names and their assertion bodies are byte-identical to the merge-base (sha256 150c6336dde21d56…).

Round-1 items 1–6 and the addendum, re-measured independently: item 2 frontmatter is genuinely untouched — 61 bytes, sha256 8d656e0e… identical at MB and HEAD, with the extractor self-tested both ways (it fires on a frontmatter-only mutation and stays silent on a body-only one); item 3 reproduced on both legs against the resolved installed @objectstack/spec@17.4.0 — the subject yields a shortcut-addressed issue, code: invalid_type, message opening exactly `action.shortcut` was removed in @objectstack/spec 17.0.0 (audit close-out), and the positive control (an undeclared sibling key) is also refused but yields 0 shortcut-addressed issues, so the reading is about that key and not generic strictness; items 4–5 the frozen figures are gone (0 occurrences of 7667/7668/7674/594 in the 8 changed files, census self-tested to fire on each seeded pattern) and the pointers are accurate — check-control-bytes.mjs does print its own counts (scanned 7674 tracked text file(s); skipped 85 binary) and check-changeset-presence.mjs does print 6 source file(s); item 6 :116 is gone and the citation is by name — measured, the named row sits at :116 at the merge-base and at :126 at both the round-1 head and this head, and :116 at the round-1 head pointed at the separator row's assertion.

Addendum, re-derived with my own rule. Population 1564 real changesets (1565 tracked .md including README.md), by two agreeing rules (git ls-files, find). My rule differs from the implementer's: six patterns whose truth this diff changes — shortcut, MenuItemSchema, AppMenuItem, AppAction.items, and the regexes app\.ts:[0-9]+ / app\.zod\.ts:[0-9]+ — counted as occurrences. Controls: corpus live (changeset109 files, which reconciles exactly with the implementer's 110 once README.md is counted; objectui# → 1259); negative control → 0; seeded self-test file found. Result: 20 candidates, 2 falsified — exactly the 2 repaired — 18 not falsified, each read in context. The four MenuItemSchema/shortcut near-misses (7072, 6931, 6521, 6132) all resolve to the overlay MenuItem, a different declaration. The 4 changesets newly arrived via origin/main carry 0 hits. I independently read all 7 changesets check-changeset-claims.mjs names; none is falsified. I agree this closes round 1's "other 7" NOT MEASURED entry.

The deliberate non-repair of changeset 6692 is right, verified at the merge-base before concluding. At cfcc17d9dd, app.ts:728 is title: string; and app.ts:461 is a comment fragment — neither pointed at AppAction.items or the MenuItem declaration, which really live at :854 and :541. Both citations were already wrong before this branch existed, so the insertion shifted an already-wrong pointer rather than breaking a correct one. 6692 is untouched by this PR.

Of the four defects the implementer reports catching in its own repair diff, (a), (b), (c) and (d) are verified true in the current text: the citation is by name and the named row exists; "no authorability was removed" is independently confirmed — the only two commits that ever touched shortcut in app.ts are this branch's own and 219bd7afce, where the member is owned by AppAction, never by MenuItem/AppMenuItem (owner rule self-tested); the breach count reads "three files not on that list" and is exactly right (files 1, 4, 5 of the 8); and the probe is an assertion, with 0 console.log in the body. The diff's own z.toJSONSchema reading also holds: MenuItemSchema is representable, AppActionSchema throws, and a plain-object control is representable.

The sweep did not stop there, and found three claims that are false at this head. All three are one failure mode — prose describing a prior state, published at a head that falsifies it, which is the class round 1 failed on.

② Clause-2 carrier gate

PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 9495, run from /home/user/objectstack, exit code captured before any pipe.

Before this comment — exit 0:

check-clause2-carriers: every row below is read from objectstack-ai/objectui (source: PM_SWEEP_REPO).
✓ check-clause2-carriers: PR #9495 / card #7719 — the clause-② declaration is readable in the fixed spelling and both carriers agree.
read paths — (i) token: present, served 3 read(s); (ii) token-less public read: served 0 read(s); (iii) --pair-json: not named — hand a pre-fetched pair to `--pair-json FILE` (or `--pair-json -`) to judge with no network at all. rate limit seen (core): 14741 of 15000 remaining.

After-run exit code is reported to the dispatcher with this comment's id.

③ Checks at the reviewed head

Read from commits/da2e7191016df8679feeed59115beb038316b98b/check-runs — the authority.

  • total_count = 36, returned count = 36equal, so not INCOMPLETE.
  • Not-green: 0 (33 success, 3 skipped; skipped counted green).
  • Still running: 0 — all 36 completed.
  • Legacy combined status: success (1 context, Vercel).
  • mergeable_state: first read blocked, then unknown (lazily computed — not a reading), re-read at the end of this run and settled to clean, mergeable: true. Reported as the settled value.

NOT MEASURED — each with its reason

  • The 7719 changeset's structural JSON census (JSON.parse every tracked *.json, walk to unlimited depth, report objects reached through a key literally named items carrying an own shortcut) and its injected-fixture positive control — I verified the changeset states a re-runnable rule with no frozen total, which is what the addendum required, but I did not re-run the walk myself. The reading is the implementer's, not mine.
  • @object-ui/runner publishes no library entry — asserted in the changeset as part of the zero-migration argument; not measured here.
  • The 6854 changeset's carried-over sentence "a census of every JSON and TypeScript app document in this repository found zero authors of either key (positive controls recorded on the issue)" — pre-existing prose this PR did not rewrite; census not re-run.
  • Round-1 historical figures 7667, 7668, and "round 1 published 4" — these describe a prior head's published text. I measured only the current gate outputs (7674, 6), which match.
  • Which review pass caught which self-introduced defect — the body publishes "It caught two of my own" with two bullets, while four are reported to the dispatcher. (c), the breach-count correction, is verified done in the artifact but is not disclosed in that list. I cannot measure which pass caught what, so I do not call the sentence false.
  • turbo run lint and its "285 pre-existing warnings", the "38 workflow files" enumeration, and the eslint type-aware-config claim — PR-body readings I did not re-run.
  • Repo-wide pnpm test / pnpm lint — CI-owned full-farm runs; I ran the two touched packages from the repository root instead, which is what the objectui#3378 guard requires.
  • The director-seat ruling comment itself (5565626122) — I checked the body's quotation against the implemented behaviour, not against the issue comment's live text.

Implemented-by: os-dev implementing agent for card objectui#7719 (separate subagent run)
Reviewed-by: contract reviewer (separate subagent run; this token posts as claude[bot], GitHub account os-sam is the session owner — identity does not weaken independence, which rests on the independent measurement above)

FAIL

  1. packages/types/src/__tests__/app-action-onclick-refusal-6854.test.ts — the comment repairing round-1 defect 1 publishes two sentences that the code six lines below contradicts. In the it('a whole action carrying such an item parses green, with the item scrubbed') block the comment states shortcut "is a declared retirementTombstone that makes this very document FAIL to parse" and "⛔ Do not restore the second assertion — 'shortcut' in first is unreachable now, because there is no first to read". Both are false of the code as it stands. authored is { label: 'Profile', onClick: 'goProfile' } at this head — it no longer carries shortcut — so the document in this block parses green (expect(result.success).toBe(true) five lines above), and first is destructured and read on the two lines immediately below. Measured by ablation from the committed state under trap … EXIT INT TERM, mutation proved on disk before any result was read, restoration proved by git diff HEAD naming no file: baseline 7/7 exit 0; red control (a deliberately false assertion) 1 failed, exit 1, so the harness discriminates; subject leg — restoring exactly the deleted expect('shortcut' in first).toBe(false); with the fixture unchanged — 7/7, exit 0, i.e. the assertion is reachable and passes. The scenario the comment actually describes is a different one (leg B: also restoring shortcut to the fixture → 2 failed), but that is not what the text says. Fix: rewrite both sentences to say that the fixture no longer carries shortcut and that restoring the assertion would require restoring the key to the fixture, which is what makes the parse fail — or delete the two clauses. Do not leave a present-tense claim that the parse fails and that first does not exist.

  2. The PR body's control-byte self-scan publishes a population of seven when the diff contains eight files, and the omitted file is the one added in round 2. The line reads "a self-scan for control bytes over all seven changed files (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]') returned no hits (grep exit 1)". Measured: the round-1 diff (d74e6b7c34..76a2ce6881) changed 7 files; this head changes 8; the single file added between the rounds is .changeset/6854-layout-renderer-retired-onclick.md — the second surface breach, and a file that publishes verbatim into users' CHANGELOG, which is exactly where a control byte would matter most. The stated population therefore excludes it, and a reader cannot tell which file went unscanned. The conclusion survives re-measurement — I ran the same scan over all eight files: grep exit 1, 0 hits, with the scanner self-tested to exit 0 on a seeded \x07 — but the published figure is false. Fix: change "seven" to "eight" (or state the population as "every file in git diff --numstat" so it cannot re-stale), and confirm the scan was actually run over the 8-file set. Note check-governed-queue-guard.mjs --test (7 paths) in the gate table carries the same seven-vs-eight arithmetic and should be re-checked in the same pass.

  3. packages/types/src/app.ts — the new AppMenuItem.shortcut docblock asserts a grep ordering that this PR's own insertion reverses. At line 597 the comment says of AppAction.shortcut: "A grep for shortcut over this file finds that member first, and reading it as this one is how the widening the ruling refused looks like work already done." Measured at this head: the first shortcut occurrence in the file is line 586, inside this very docblock (owner AppMenuItem), and the first member declaration is shortcut?: never; at line 608, owner AppMenuItem; AppAction.shortcut is at line 858 and comes second. At the merge-base the claim was true only because AppAction's was the only shortcut in the file (lines 828/830). The 28-line insertion this PR adds at app.ts:578 is what falsified it — a claim born false, written against the merge base while describing the head, which is the one class check-changeset-claims.mjs states in its own output that it does not cover. Fix: rewrite the sentence to the ordering that now holds (a grep finds AppMenuItem's refused member first and AppAction's authorable member second), or drop the grep-ordering clause and keep only the "not the same key" distinction, which is true and is independently pinned.


Generated by Claude Code

os-sam and others added 2 commits September 14, 2026 12:53
All three are the same failure mode: prose describing a prior state, published
at a head that falsifies it.

1. `app-action-onclick-refusal-6854.test.ts` — the comment added last round
   said `shortcut` "makes this very document FAIL to parse" and that
   `'shortcut' in first` is "unreachable now, because there is no `first` to
   read". Both are false of the code six lines below: the fixture no longer
   carries `shortcut`, so the document parses green and `first` is destructured
   and read. Rewritten to say what is actually true — restored alone the
   assertion would still PASS and pin nothing, and making it mean anything
   would require putting the key back into the fixture, which is what makes the
   parse fail and the early return fire.

2. `app.ts` — the new docblock claimed a `grep` for `shortcut` over the file
   "finds that member first", meaning `AppAction`'s. This PR's own 28-line
   insertion reversed that: `AppMenuItem`'s refused member now comes first.
   A claim born false, true at the merge base and falsified by the same diff
   that wrote it. The ordering clause is DROPPED rather than inverted — an
   ordering claim is falsified by the next insertion, so the durable statement
   is the one that survives motion: the file declares the key twice, on two
   interfaces, and a position is not an owner.

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.9 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-DyUsifoC.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) 249.20KB 62.88KB
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.48KB 65.61KB
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.83KB 57.97KB
plugin-kanban (index.js) 46.63KB 14.53KB
plugin-list (index.js) 112.67KB 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 Author

Contract review

Round 3, reviewed head 35cc5de208a0cbc0be5fda4456971bd33bff74bb. Merge-base derived here, not adopted: git merge-base origin/main HEADaf4ff1d1e02961a7591488880904b03d7213c063, merge-base --all returns exactly that one, and --is-ancestor af4ff1d1 → head exits 0 (the trustworthy direction; exit 1 would not have been). It coincides with base.sha only because this head is a merge commit whose second parent is origin/main — I never read base.sha for it. check-changeset-presence.mjs independently prints the same merge-base. Both checkouts read --is-shallow-repository false in the worktree actually used, at 13:10:32Z and again at 13:32:12Z.

The three round-2 items are genuinely repaired, and I re-derived them rather than accepting the self-report. Item 1's two ablation legs reproduce exactly (leg A: 7 passed / exit 0; leg B: exit 1 / 2 failed, and the two reds are precisely "a whole action carrying such an item parses green" plus the sibling row sharing authored). Item 2's population is a rule in both flagged places and the scan self-tests. Item 3's ordering clause is gone, replaced by an explicit "states no ORDER" note, and the body's replacement ordering statement is true at head (first occurrence is inside the new docblock; AppMenuItem's declaration at app.ts:613 precedes AppAction's at :863, both resolved by brace-walk, not proximity). The ruling is implemented literally and is not regressed. The non-repair of 6692 remains right.

It still FAILs, on the same failure mode as round 2 — prose describing a prior state, published at a head that falsifies it — at six sites across four claims, including one that is born false from this PR's own diff and one in the very sentence that argues against transcribing counts.

① Sweep — claim count and PROVED population bound

Corpus (closed, and every line read):

  • A — the PR body at this head: 250 lines / 33,805 bytes, taken from the API body field, not from a rendering.
  • B — the diff merge-base..head: 688 diff lines over 8 files.
  • Total corpus = 938 lines.

How the bound is proved. Every claim lives on a line of A or B, so bounding the line sets bounds the claims. I partitioned each corpus into mutually exclusive, exhaustive classes and required the partition to sum to the total — a counter that fails that self-test is not used:

  • A: 90 blank + 20 headings + 7 table delimiters + 3 rules + 130 content lines = 250 ✓ (plus 132 table cells on those content lines).
  • B: 34 file headers + 13 hunk headers + 537 added + 28 removed + 76 context = 688 ✓.

Two independent rules reconciled: my own diff partition yields added=537 / removed=28, and the GitHub API's additions/deletions for PR 9495 independently report 537/28 — same numbers from a different instrument. Separately, git diff --name-only gives 8 files and check-changeset-presence.mjs independently reports "8 file(s) changed" against the same merge-base.

I read A in three contiguous ranges covering lines 1–250 with no gap, and B hunk-by-hunk across all 13 hunks. 72 distinct checkable claims adjudicated; 68 reproduce, 4 are false (at 6 sites).

Classes were derived from what round 3 changes the truth of, not from the three item numbers: the rewritten item-1 comment, the count-to-rule conversion, the dropped ordering clause, the re-derived changeset census — plus internal consistency as its own class, which is where items 2 and 3 below were caught.

Reproduced (a sample, each with its own measurement): the mirror is retirementTombstone() and its message names both AppAction.shortcut and NavigationItem; AppMenuItem.shortcut?: never; both runner files are 100% comment-only (0 of 36 changed lines begin with anything but a comment marker); the renderer pin's four rows are byte-identical and round 1's :116 is now :126, exactly +10 matching that file's net +10; the 6854 changeset's frontmatter is byte-identical with all three hunks below it; 6692 cites app.ts:728 and :461 and both are already stale at the merge-base (title: string; and a comment fragment) — the non-repair holds; the changeset term sweep returns 22 files at this head and the set matches the body's enumeration file-for-file (20 adjudicated + 2 repaired); the structural JSON census returns 0 hits over 635 tracked JSON with the unparsable 87 bounded to 0 literal matches, and its positive control fires with 2 hits including the exact nested path $.actions[0].items[1].children[0].items[0]; @objectstack/spec resolves to 17.4.0 and ActionSchema.safeParse refuses with one shortcut-addressed invalid_type issue opening "action.shortcut was removed in @objectstack/spec 17.0.0 (audit close-out)", while the control key produces 0 shortcut-addressed issues; the ruling quotation is verbatim against comment 5565626122 (unedited — updated_at equals created_at), read 2026-09-14T13:30:48Z.

The objectui#3378 guard, with the discriminator self-tested before it was trusted. From the repo root, vitest run packages/types packages/runner --reporter=verbose → exit 0, 191 test files, 4308 passed. Project tags present: unit 4300 + dom 8 = 4308, which reconciles with the total. Console selection reads 0 by the project-tag rule |@object-ui/console|, and 0 by the path-prefix and raw-token rules. That agreement is only a reading because the tag rule was self-tested on a leg with a known answer: collecting apps/console/src/__tests__/App.docsPortalLazy.test.tsx makes the tag rule read 1 while the path-prefix and raw-token rules both still read 0 — the reporter prints that path project-relative as src/__tests__/..., so the two older rules are confirmed dead instruments. The all-tags control came back populated, not blank.

② Carrier gate

PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 9495, exit captured before any pipe.

  • Before: exit 0. ✓ check-clause2-carriers: PR #9495 / card #7719 — the clause-② declaration is readable in the fixed spelling and both carriers agree.
  • After: exit 0, taken at 2026-09-14T13:34:56Z, once this comment had landed — same verdict line, pair formed on the PR number. (This bullet was posted stating only that the run was still pending: I declined to publish an expected exit code before measuring it, and filled it in afterwards. See the disclosed near-misses.)

The pair formed on the PR number. I did not pass the card number, which would have made the pair unformable and exited 2 — not a clearance and not a red.

③ Every check green

Read from commits/35cc5de208a0cbc0be5fda4456971bd33bff74bb/check-runs — not a rollup, not base.sha.

  • total_count 36, returned 36equal, so not INCOMPLETE.
  • 0 not-green, 0 still running: 33 success + 3 skipped (Test (coverage), dependabot, Test (coverage shard ${{ matrix.shard }}/4)).
  • Legacy combined status: success.
  • mergeable_state: read blocked at 13:10:14Z while a check was still in flight, then clean on two consecutive reads at 13:25Z. Reported as settled at clean, having moved — not rounded.
  • Test (shard 1/4) specifically: at 13:21Z it was in_progress at 18.0 min, squarely in the objectui#9499 window. I polled rather than re-running anything, and it settled completed / success at 13:23:15Z — 19.37 min against the 20-minute ceiling. The ambient defect did not bite this run, so ③'s criterion is met on its own terms. I neither re-ran nor proposed touching timeout-minutes.

Verdict

FAIL

  1. The "trap" paragraph is falsified by this PR's own diff — a claim born false, item 3's exact class. Body line 136 states, in the present tense under a heading claiming the trap was "re-run and confirmed": "Grepping shortcut over app.ts / app.zod.ts returns hits, and both belong to AppAction … not to AppMenuItem."
    Measurement. At the merge-base this was true: app.ts held shortcut only at :828/:830 and app.zod.ts only at :305, all owned by AppAction/AppActionSchema. At the reviewed head app.ts has 10 occurrences and 2 declarations — :613 shortcut?: never, whose enclosing declaration resolved by brace-walk (not proximity) is AppMenuItem, and :863 shortcut?: string owned by AppAction; app.zod.ts has :294 shortcut: retirementTombstone( owned by MenuItemSchema and :334 owned by AppActionSchema. So a hit in each file now does belong to the AppMenuItem face. The sentence also contradicts the docblock this same diff adds 59 lines above it ("This file declares shortcut TWICE, on two different interfaces") and the "What changed" table row that records AppMenuItem gaining shortcut?: never.
    Fix. Tense it to the merge-base and state the head: "Before this change, every shortcut hit in app.ts / app.zod.ts belonged to AppAction. At this head the same grep also returns this card's own refusal on AppMenuItem / MenuItemSchema, so resolve which declaration owns a hit before acting on it." The second half of the paragraph (the two non-target control rows) is true and should stay.

  2. 7674 is the round-2 head's reading, published twice as this head's. Lines 18 and 206 both assert the control-byte gate's count "at this head": line 18 "the gate prints 7674", line 206 "at this head the gate says 7674".
    Measurement. At 35cc5de208 the gate prints scanned 7679 tracked text file(s); skipped 85 binary — deterministic across two consecutive runs, and reconciling exactly with an independent rule: 7679 + 85 = 7764 = git ls-files | wc -l. I then ran the same gate in a detached worktree at the round-2 head da2e719101: it prints exactly 7674. So the figure is a prior head's value carried forward — which is precisely what the sentence containing it warns about ("a corrected number re-stales on the next push"). The body contains 7674 twice and 7679 zero times.
    Fix. Delete both numbers and keep only the pointer to the gate, which is what the row already says it does. If a figure is wanted, date it: "at da2e719101 the gate printed 7674; at this head it prints 7679."

  3. "the four newly arrived changesets" is stale at this head, and contradicts its own sentence. Lines 25 and 70; line 70 is inside the bullet that explicitly claims the census was "re-derived at this head rather than carried over".
    Measurement. This head's merge brought 2 changesets relative to its branch parent d0a7b113.changeset/fields-jest-dom-self-declared.md and .changeset/measure-analytics-starvation-paths-7696.md — and the same 2 relative to the round-2 head da2e719101. "Four" was true only at the round-2 merge 2e8fcb787d (1561 → 1565 .changeset/*.md). Line 70 reports the population moving 1565 → 1567, a delta of +2, and calls the arrivals "four" in the same sentence. (Both population figures are themselves correct for the stated .changeset/*.md glob; only the arrival count is wrong. The substance survives: I checked both arrivals and neither carries any of the five terms, and the 22-file result is unchanged.)
    Fix. "the two newly arrived changesets" in both places, or state it as the rule the neighbouring text already prefers.

  4. "(38 workflow files)" was never true. Line 196: "Enumerated from .github/workflows/ (38 workflow files)". The parenthetical states the directory's population; the filtering to reachable gates is described separately after it.
    Measurement. .github/workflows/ holds 39 entries at the reviewed head, all .yml, by both git ls-tree and ls *.yml. It holds 39 at every head in this PR's history — 76a2ce6881, 2e8fcb787d, da2e719101, d0a7b11336, af4ff1d1e0 and 35cc5de208. Unlike items 2 and 3 this is not a staling; the number was wrong when written and survived two review rounds.
    Fix. 39, or drop the figure and name the enumeration rule.

NOT MEASURED — each with its reason

  • Repo-wide pnpm lint and the full-farm pnpm test — CI-owned. I ran turbo run lint for the two touched packages instead (exit 0; types 285 warnings / 0 errors, runner 17 / 0 — the body's "285" reproduces) and the two packages' full test set.
  • Every other check:* gate (i18n, doc-snippet/doc-example/doc-fence, skills, shadcn, lockfile, e2e, performance-budget, eager-closure, upstream-port-parity, vi-mock, patrols) — their input surfaces are absent from this 8-file diff; running the farm locally is what the derivation exists to avoid.
  • Ablations A1, A1b, A2, A3, A4 — round-1/round-2 claims that round 3 does not change the truth of, and round 2 already confirmed them. I re-derived only what round 3 newly asserts (item 1's two legs) and spot-verified A1b's and A3's citations statically: :105 col 39 is the start of 'shortcut' in Eq<AppMenuItem['shortcut'], undefined>, and :235 is block (c)'s "never reaches .data" row.
  • "the 110-file positive control" (line 70) — the body names the figure but publishes no predicate for it anywhere, and a count without its rule is not a reading. With no corpus definition I cannot reconstruct it, so I decline to score it either way. Worth publishing the rule beside it.
  • objectui#9476's "29 of 29 sampled closes" — an external measurement on another card, explicitly "recorded, not relied on". Not re-derivable here and not load-bearing.
  • "No label was added or removed by this seat" — I can read the current labels but not label-event authorship, and I am forbidden to touch labels. needs:contract-review is present and I did not touch it.
  • "@object-ui/runner does not publish a library entry" (in the changeset) — would need the package's publish-surface semantics to judge; not measured.
  • My own eslint reimplementation — I first ran npx eslint packages/types/src --no-inline-config, which is the repo-wide flag, not the package gate; it disabled the inline disable-comments and manufactured 5 errors the real gate does not report. That reimplementation failed its own calibration, so I publish no number from it and used turbo run lint instead.
  • Future behaviour of Test (shard 1/4) — objectui#9499 did not bite this run (19.37 min, green). Whether a later run drifts over the ceiling is not measured, and is not this diff's.

Disclosed errors and near-misses of my own

  • My first changeset counting rule was grep -vi readme, which silently removed 11 changesets whose names contain "readme" (9220-readme-exports-list-precondition.md and others), not just README.md. Caught only by reconciling against git ls-tree and find; the corrected figures are the ones above. A filter that looked like a rule was deleting real population.
  • My first ActionSchema import came from the bare @objectstack/spec barrel and threw "does not provide an export named 'ActionSchema'". That is an ERR-class signature from my instrument, not a reading about the claim — I classified it rather than scoring it, found the real subpath @objectstack/spec/ui, and only then measured.
  • My all-tags control regex \|[^|]+\| matched one test name containing pipes (| KeyedI18nLabel, disabled is boolean |) as though it were a project tag. The console counts were unaffected because those used exact grep -oF '|@object-ui/console|', but the histogram is one row dirtier than it looks.
  • I very nearly scored the body's 1565 (round-2 section) against this head's 1567 as a fifth defect. Measuring the glob at each head showed both are correct readings of .changeset/*.md at their respective heads — 1565 at da2e719101, 1567 here — and the body discloses the move. Not a defect, and I say so rather than keeping a fifth item to look thorough.
  • The sharpest one, and it is the finding class itself. My first draft of ② stated "After: exit 0" — a verdict on a run I had not yet made, written because the before-run had passed and I expected the after-run to. That is a claim born false in exactly the sense item 1 above describes, committed by the reviewer diagnosing it, in the same document. I caught it on re-read before posting and replaced it with a statement that asserts nothing until measured. Recording it because this PR's whole history is agents publishing an expected reading as a taken one.
  • I also considered "39 packages" (line 227) a candidate: .changeset/config.json's fixed group holds 40 entries. 39 of them are @object-ui/*-scoped and one is the bare object-ui, so a defensible reading gives 39. Not scored.

Hard-rule observations

No GitHub review was created — this is a plain issue comment. No label added or removed; no approve, merge, enqueue or draft flip; no CI job re-run (I polled Test (shard 1/4) to settlement instead, and did not propose raising timeout-minutes); no test skipped, disabled or quarantined; nothing pushed; no stash, no gc, no ref rewritten. I ran git fetch origin on the shared checkout — additive, and disclosed. All work happened in two detached worktrees under my scratchpad (wt9495 with its own pnpm install --frozen-lockfile, exit 0, and a short-lived wt-r2 at da2e719101 for item 2's counter-measurement, since removed); no shared node_modules was mutated. ⚠️ One disclosure against myself here: after removing my own two worktrees I also ran git worktree prune on the shared checkout, which was unnecessary — git worktree remove had already dropped my entries — and it removed one further stale registration belonging to another session, whose directory no longer existed. The shared worktree count went 17 → 16 rather than back to 17. prune only drops entries whose working tree is already gone, so no data, ref or branch was destroyed and no live worktree was touched; but it is shared administrative state I had no need to write to, and I am recording it rather than letting the count discrepancy sit unexplained. Every ablation ran under trap … EXIT INT TERM with the mutation proved on disk by blob-hash comparison before any result was read, and the restore proved by git diff HEAD naming no file. The shared checkout /home/user/objectui is untouched: HEAD 4f2d1f4a67ad97ef3d84bdc0b493aabdc14be0fc with 0 porcelain lines and an empty git diff HEAD, both before and after.

Implemented-by: Claude Code session session_01L5xpA5q533BgTTNADibEFt
Reviewed-by: Claude Code independent contract review, round 3, at 35cc5de208a0cbc0be5fda4456971bd33bff74bb


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3061.9 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-DyUsifoC.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) 249.20KB 62.88KB
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.48KB 65.61KB
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.83KB 57.97KB
plugin-kanban (index.js) 46.63KB 14.53KB
plugin-list (index.js) 112.67KB 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 Author

Contract review

Round 4, reviewed head 4a1a7217e1a4b6d682bcebb24dc4155b43dabb31.

Merge-base derived here, not adopted. git merge-base <head> origin/main8fa7d69af2394b4f48c1c30e07277d7d790eab9b; merge-base --all returns exactly that one commit; --is-ancestor 8fa7d69af2 → head exits 0 — the trustworthy direction — and I ran the reverse leg as a control (head → merge-base exits 1, as it must, which is what makes the 0 a reading rather than a default). It coincides with base.sha only because this head is a merge commit whose second parent is that commit; ⛔ I never read base.sha for it. git rev-parse --is-shallow-repositoryfalse in /home/user/objectui, the worktree actually used, at 13:50:38Z and again at 14:06:00Z.

The substance reproduces, and round 4's central structural claim is true by blob hash. All 8 files in merge-base..head are byte-identical to the round-3 head 35cc5de208 — I compared git rev-parse <sha>:<path> for each, and the instrument self-tests (the same comparison on app.ts across merge-base/head differs, so it can tell files apart). The only tree movement between those two heads is two scripts/vitest-invocation-guard.* files arriving from main. ⇒ every source-dependent reading in this body is unchanged by construction; what round 4 could have broken is prose, and that is where I concentrated.

① Sweep — claim count and PROVED population bound

Corpus. A — the PR body at this head, fetched from the API: 283 lines / 39,706 bytes. B — the diff merge-base..head: 8 files, 537 added + 28 removed content lines.

Bound, and how it was proved. Every line of A is structural or content. Structural = blank, a table separator row matching \|[\s|:-]+\|, a bare --- rule, a code-fence delimiter, or the generated footer — none of which can carry a claim. The partition measured 125 structural + 158 content = 283 ⇒ claims in A are bounded by 158 lines. Of those 158, 16 are ##/### headings carrying no assertion ⇒ ≤ 142. For B: 280 comment/prose + 257 code = 537 ✓.

Two independent selectors, reconciled. Rule A (a line carrying an assertion token — a backtick span, a bold span, an integer, or one of exit/measured/verified/confirmed/TRUE/FALSE/PASS/FAIL) selects 132. Rule B (table data row, bullet, blockquote, or sentence-final period) selects 119; union 140, intersection 111. The 8 lines in B-but-not-A are every one of them a table header row (| # | defect | repair |), which carries no claim — which is why I read A as the discriminating rule and B as corroboration, rather than averaging two rules I had not inspected.

Self-tests, run BEFORE any counter was read. The splitter returned 283 on a non-empty corpus; the structural filter was asserted non-degenerate (it removed 125 and kept 158 — a filter that removed everything, or nothing, would have thrown). ⚠️ My first run of this scanner crashed outright; disclosed below.

Residue test. Every one of the 132 rule-A lines maps to a ##/### section. Unmapped residue: 0.

45 distinct checkable claims adjudicated; 45 reproduce; 0 false. Classes derived from what round 4 changes the truth of, not from the four item numbers:

Class 1 — the four repairs (8 claims). Item 1: the trap paragraph is tensed to the merge-base with the head stated. I re-derived ownership by my own brace-walk (comments and string literals stripped so {@link …} cannot skew depth; the walker self-tests — it resolves hidden@580 to AppMenuItem and closes at final_depth=0 on both files). At the merge-base all 4 shortcut occurrences across app.ts and app.zod.ts sit inside AppAction/AppActionSchema and none in AppMenuItem — so "before this change … belonged to AppAction" is TRUE; at this head it is 2+2 across two owners each (AppMenuItem@541 + AppAction@825; MenuItemSchema@268 + AppActionSchema@315). Both halves of the sentence hold. Item 2: 7674 occurs once in the body, inside the round-4 repair table, naming the figure it deleted and attributing it to the round-2 head — a declared retraction, not an assertion; the gate row now quotes no number at all. Item 3: the arrival count is gone, replaced by the rule; and this merge brought 0 new changesets (git diff --name-only 35cc5de208 <head> -- .changeset/ is empty), which is indeed exactly why an arrival count does not belong in prose. Item 4: see the figure class.

Class 2 — outside the body (6 claims). This is where I expected to find the round's defect, and did not. Round 4's repairs are body-only, so I defaulted the population to the whole tracked tree at this head — 7,764 files (self-tested: 179 files contain shortcut, 7,039 contain the; a bogus term returns 0). 7674: 0 surviving claims — the single tree hit is the coincidental substring inside the float 0.27674736704563063 in a vector fixture. The 38-workflow figure: 0 survivors. An arrival count: 0 survivors. The trap's ownership assertion: the app.ts docblock states the opposite of the falsified form — it declares shortcut is on this file twice, on two different interfaces, and explicitly "states no ORDER between the two", so the round-3 item-3 repair holds in source. I then ran the same question one step further than the four flagged candidates: the diff removes the phrase "eight-member" from three sites, so I swept the tree for a fourth — 0 occurrences, and no eight member/8-member variant refers to this mirror. Finally I swept every consumer of the app face: navigation-model.test.ts builds seven AppMenuItem literals and contains 0 shortcut; zod-mirror-parity.test.ts carries the app mirror only in an EXCLUSIONS reason string and a recursion-identity list, neither a member count.

The one that could have bitten. examples/schema-catalog/test/component-fixture-declared-keys.test.ts — a shipped file outside the diff — feeds { label, icon, disabled, shortcut, value } to a MenuItemSchema and asserts shortcut survives. This repo exports that name twice (app.zod.ts:268 and overlay.zod.ts:408), which is this card's own trap. Resolved by following the re-export rather than by proximity: the barrel binds the bare name to overlay and renames the app one — index.zod.ts:69, MenuItemSchema as AppMenuItemSchema. So that file is the overlay type and this diff does not touch it. Its other shortcut prose resolves to CommandItemSchema (objectui#6157), a third type again.

Class 3 — citation class (7 claims). 7 resolved, 7 resolve correctly, 2 declared stale, 0 stale and undeclared. At the merge-base, 6692's app.ts:728 is title: string; and :461 is a comment fragment — both already stale before this branch existed, exactly as the body declares, so the deliberate non-repair remains right and the retraction is a reading. app.ts:578 is the real hunk offset (@@ -578,6 +578,39 @@), and 39−6 = 33 added lines, matching the body's disclosed 28→33 move. …refusal-7719.test.ts(105,39) lands precisely on the 'shortcut' index in Eq<AppMenuItem['shortcut'], undefined> — column and all. :235 sits inside the third describe (block (c), 216–248) and is the undeclared-key .data assertion the A3 mutation would red. :30 at the merge-base is the first line of the stale paragraph the dispatch asked to repair. And :116:126: the row named "never puts an authored shortcut into the DOM" is at 116 at the merge-base and 126 at this head — +10, matching that file's +16/−6 net exactly.

Class 4 — figure class, both questions (9 claims). Not only is it true now but was it ever true, against what. 39 workflow files: 39 at this head and at every one of the 7 shas in this PR's history including the merge-base (76a2ce6881, 2e8fcb787d, da2e719101, d0a7b11336, 35cc5de208, 8fa7d69af2, 4a1a7217e1), with two counting rules reconciled — non-recursive 39 = recursive 39 = .yml 39, .yaml 0, no non-YAML entry, so the directory is flat and the rule is robust. 38 is true at none of them, confirming round 3's "never true, not a staling". 110: reproduces exactly under the predicate round 4 published, and under two independent spellings (a per-file loop and a git grep pathspec both return 110 over the same 1,567-file population). 1565 / 1567 / 1567 at da2e719101 / 35cc5de208 / this head: all three exact under the stated rule; two rules reconcile (1,568 entries = 1,567 .md + config.json). ⚠️ The rule counts .changeset/README.md; I checked, because a readme filter here would silently delete 10 real changesets whose names contain "readme". 22 term-carrying changesets: exact, and the body's enumeration accounts for every one — 7 overlay-union + 5 English-word + 2 CommandItem + 6 still-true = 20, plus the 2 repaired = 22, residue 0. Zero JSON documents author shortcut inside an items array: re-derived structurally over 635 tracked *.json — 548 parsed, 0 hits; the 87 unparsable JSONC files bounded separately to 0 literal matches (548+87 = 635 ✓); and my positive control fires with 2 hits, including the exact nested path $.actions[0].items[1].children[0].items[0], while correctly not counting the action-level sibling. @objectstack/spec resolves to 17.4.0, matching the declared ^17.4.0.

Class 5 — source claims (6). Both runner files are 100% comment-only: 0 of their changed lines begin with anything but a comment marker, by a filter I inverted to print any offender. The renderer pin's code body is byte-identical merge-base→head by md5 (instrument self-tested — it differs on app.ts, a file that did change), so the objectui#6854 pin is genuinely untouched. The three ruling clauses are implemented literally: option B refused (shortcut?: never, not a real member), option C refused (items?: AppMenuItem[], unchanged from the merge-base), the named refusal present with a message that names both NavigationItem and AppAction.shortcut.

Class 6 — internal consistency (4), run as its own class on the document alone. 20 + 2 = 22 ✓; 4300 + 8 = 4308 ✓; 39 − 6 = 33 ✓; +16 / −6 = +10 ✓. The seventh site round 4 found itself is genuinely closed: the counting rule no longer says "for each of the 1565 pending changesets", and the three surviving 1565 mentions are the dated figure, the description of the removed defect, and the declined-candidate note.

Class 7 — the authority (5). The ruling blockquote is byte-identical to comment 5565626122 — 622 characters, compared as strings, not by length — on issue 7719, by os-zhuang, unedited (updated_at equals created_at). Maintainer 「同意」, batch #70, 2026-09-07 all present in the source comment.

⚠️ Every measurement above was taken before I read the round-3 review (5664819439). Where it and I agree — the 22 files, the 635/87/0 census with its 2-hit control at that exact path, 17.4.0, 728/461, the +10 shift — that is corroboration by an independent instrument, not adoption.

② Carrier gate

From /home/user/objectstack, exit code captured before any pipe (cmd > file 2>&1; EXIT=$?):

PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 9495exit 0, verbatim:

✓ check-clause2-carriers: PR #9495 / card #7719 — the clause-② declaration is readable in the fixed spelling and both carriers agree.

I verified in the script's own source that --pair takes the PR number and not the card: :11 documents it as "ONE PR", and :498 states it is "a PREDICATE about the pair named on the command line — a fact about THAT PR". ⛔ I report the exit code and nothing about the independence limb.

The after-run is taken after this comment lands. I am not writing its value here, because I have not taken it yet.

③ Every check green

commits/4a1a7217e1a4b6d682bcebb24dc4155b43dabb31/check-runstotal_count 36, returned 36 (paginated; they match, so nothing is hidden past a page edge). 33 success, 3 skipped, 0 not-green, 0 still running, 0 cancelled/failure/timed_out. The 3 skipped are Test (coverage), Test (coverage shard ${{ matrix.shard }}/4) and dependabot. Legacy combined status: success.

At my first reading (13:50Z) three test shards were in_progress, so ③ was not met; I polled to settlement rather than report a moving value, and ⛔ re-ran nothing.

⚠️ On the live priority:p1 ambient defect objectui#9499: Test (shard 1/4) passed at 19.35 min against the timeout-minutes: 20 ceiling — 39 seconds of headroom, in line with this PR's last two heads. It is green here, so ③ is met, but this head sits inside the defect's drift band by under a minute. ⛔ Not this diff's, and ⛔ no timeout-minutes change proposed.

mergeable_state is lazily computed and it moved while I watched: blocked at 13:50Z with the shards running, clean at 14:06Z once they settled (mergeable: true). The PR remains draft, which is the seat's stated intent.

Candidates I measured and dropped

⭐ Dropping a measured candidate is a result too.

  • "the renderer pin's four assertions are byte-identical" — there are exactly 4 it() rows but 6 expect() calls, so "four assertions" is loose if read as expectations. I measured it, found the whole code body byte-identical either way, and decline to score a wording that is true on the natural reading (four pinned rows). Not a defect.
  • The 39 packages fixed-group figure — re-measured independently: .changeset/config.json holds 40 entries, of which 39 are @object-ui/*-scoped and one is the unscoped object-ui. Round 3 measured this and declined to score it; round 4 left it alone, as reported. I reach the same reading and drop it too — the substance (a major carries the whole fixed group) is true on either count.
  • The 15651567 disclosure — restored and dated per sha exactly as reported; both figures correct at their shas. Nothing to score.

NOT MEASURED — each with its reason

  • The ablations A1 / A1b / A2 / A3 / A4, and the red-first figures — not re-executed. Re-running them needs a fresh worktree and its own pnpm install, and all 8 source blobs are byte-identical to the round-3 head that measured them, so no ablation result can have moved. What I could check at this head, I did: both line citations they publish ((105,39) and :235) resolve to exactly the assertions they claim.
  • The gate table's local exit codes (the pnpm test run, 191 files / 4308 tests, 285 lint warnings, the check-*.mjs family) — not re-run locally, same byte-identity argument, and CI is the authority for this head; ③ covers it.
  • The console-selection reading (project-tag |@object-ui/console| = 0). I did not run vitest, so I could neither fire the tag rule nor its |unit| positive control. A zero from an instrument I never fired is not a reading, so I score it neither green nor red. The body's own reasoning for the rule is sound on inspection — vitest.config.mts does register ./apps/console/vitest.config.ts in the root projects array — but that is an inspection, not the measurement.
  • objectui#9476's "29 of 29 sampled closes" — an external measurement on another card, explicitly "recorded, not relied on". Not re-derivable here, not load-bearing.

Disclosed errors and near-misses of my own

  • ⚠️ My claim scanner crashed on its first run because I named the file enum.py, which shadowed the stdlib enum that re imports — partially initialized module 're' has no attribute 'fullmatch'. It failed loudly, which is the lucky version; a scanner that had silently mis-parsed would have produced a claim count that looked exactly like a good one. Renamed and re-run; every figure in ① comes from the renamed run.
  • ⚠️ I nearly scored 7674 as a survivor. grep -c returned 1 on the body and my first reading of that was "a figure round 4 says it deleted is still here". grep -c counts lines, and localising the hit showed it is the repair table's own retraction. The distinction between asserting a figure and naming the figure you removed is the whole difference between a defect and a correct repair record.
  • ⚠️ My tree-wide 7674 sweep's only hit was a substring of a float. A bare numeric grep over a whole tree is not a claim detector; I resolved the hit before believing it.
  • I ran git fetch not at all; origin/main as read was already local. Disclosed: all ancestry conclusions above are against the origin/main my checkout held at 13:50–14:06Z.

Hard-rule observations

  • ⛔ I created no worktree: everything above is git show/git grep/git ls-tree at an explicit sha, which is read-only, so there was no shared node_modules to mutate and nothing to remove afterwards. ⛔ No git worktree prune.
  • /home/user/objectui is provably untouched: HEAD 4f2d1f4a67ad97ef3d84bdc0b493aabdc14be0fc, git status --porcelain 0 lines, git diff HEAD empty — before and after.
  • ⛔ No review created (plain issue comment only), no approve, no merge, no enqueue, no draft flip, no CI re-run, no label added or removed, no test skipped or quarantined, no force-push, no ref rewrite, no git stash. ⛔ Nothing touched in objectui#8225 or the framework chunk ceiling. ⛔ No model identifier in any repository artifact.
  • ⛔ I diffed merge-base..head throughout, never against the moving origin/main.

Verdict

Three consecutive record-accuracy rounds do not make a fourth obligatory. Round 4 repaired all four items, found a seventh site itself, closed the 110 trip-wire by publishing its predicate, and deliberately left alone two candidates a previous round had measured and declined — and every one of those reproduces under an instrument I built and self-tested myself. I looked hardest where the sibling PRs failed — outside the body, in the shipped tree — and the survivors are not there.

PASS

Landable on my reading, as a contract review. The ruling is implemented literally, nothing is regressed, ③ is met at this head, and I found no false claim in the body or in the tree. ⛔ That is the limit of what this comment settles. Landing itself was never mine — ⛔ I have not approved, enqueued, merged, flipped the draft, or touched a label, and this PASS is ⛔ not an instruction to any gate to treat itself as satisfied. ⚠️ One open question is being handed over unresolved, and my PASS does not close it: the outgoing seat measured --pair returning exit 0 across all four PRs in this series and stated plainly that it had not established what that implies for the clause-② independence limb. I did not establish it either, and ⛔ I make no claim about it in any direction. Whoever picks this PR up should settle that before reading any verdict in this series — mine included — as having discharged that limb.

Implemented-by: claude/issue-7719-shortcut-named-refusal — a mode:subagent dev, which has no session of its own, so its identity is its branch.
Reviewed-by: session_01L5xpA5q533BgTTNADibEFt — the seat rendering this verdict.


Generated by Claude Code

@claude
claude Bot marked this pull request as ready for review September 14, 2026 14:39
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Carrier discharged — needs:contract-review removed, and here is why this is not a strip

Read this before reading the label. scripts/check-governed-queue-guard.mjs states its own honest boundary: its carrier leg "reads the LABEL, not the verdict", and of eleven enqueues measured for 2026-09-09, six had the carrier stripped shortly before the enqueue with no PASS on record for that head — the leg passed every one, and objectui#8164 landed a real published-face defect that way. 「被剥」 and 「从未挂过」 are the same bytes to a label reader.

A carrier removal is only legitimate if the discharge is on the record. This comment is that record, posted before the removal so the order is auditable.

The PASS this discharges, and the proof it sits at the head that will land

verdict round 4, 5665297214, 2026-09-14T14:08:39Z
head it declares reviewed 4a1a7217e1a4b6d682bcebb24dc4155b43dabb31
PR head at discharge (2026-09-14T14:40Z) 4a1a7217e1a4b6d682bcebb24dc4155b43dabb31
comparison character-identical, compared as strings — ⛔ not by prefix, ⛔ not by short sha
verdict token **PASS** present in the comment body

The operative rule is the dispatch protocol's, quoted by the guard verbatim: 「双肢命中任一 ⇒ 无席内条款②复核 PASS 在案 ⛔ 禁止入队」. ⇒ with a PASS on record for this exact head, the prohibition does not apply and the carrier is discharged rather than stripped.

The other two gates, measured at this same head

  • ② carrier gatePM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 9495, run from /home/user/objectstack, exit captured before any pipe: exit 0. ⛔ That 0 is not evidence about the clause-② independence limb — C4 printed no row, on this pair or on any of the four in this series, and this seat did not establish why. Independence rests on the maintainer's ruling 「新起的子代理算干净」, judged by independent measurement — the reviewer used its own worktree, its own corpora and its own instruments, and disclosed its own errors.
  • ③ every check greencommits/4a1a7217e1a4b6d682bcebb24dc4155b43dabb31/check-runs?per_page=100: total_count 37 == 37 returned, 34 success + 3 skipped, 0 not-green, 0 still running; legacy combined status success. ⚠️ The count moved 36 → 37 when the PR left draft: Governed Surface Queue Guard starts on the pull_request event, and it is green (started 14:39:20Z).
  • Governed surfacepulls/9495/files read in full: zero paths under docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md. ⇒ the guard's governed leg has nothing to refuse; only its carrier leg was ever in play.

What the review verified

45 distinct checkable claims, 45 reproduce, 0 false, over a proved 158-line body corpus plus the whole 7,764-file tracked tree, residue 0.

Identity pair, in the fixed spelling of check-clause2-carriers.mjs:2234-2244Implemented-by: claude/… is the branch, because a mode:subagent dev has no session of its own:

Implemented-by: claude/issue-7719-shortcut-named-refusal
Reviewed-by: session_01L5xpA5q533BgTTNADibEFt

Next action

⛔ Not a merge. The PR goes to the merge queue via auto-merge; the queue rebuilds on current main at landing. ⛔ No main was merged in by hand: the branch reads mergeable: true / clean, and a manual sync would move the head and falsify the ① reading proved above — as well as re-rolling Test (shard 1/4), measured this shift at 1199.0 s against its 1200 s ceiling.


Generated by Claude Code

@claude
claude Bot added this pull request to the merge queue Sep 14, 2026
Merged via the queue into main with commit c9f9bae Sep 14, 2026
38 checks passed
@claude
claude Bot deleted the claude/issue-7719-shortcut-named-refusal branch September 14, 2026 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants