Skip to content

fix(types): correct six docblock claims in arm-named-export-8784.test.ts and add the control they described - #9530

Merged
claude[bot] merged 1 commit into
mainfrom
claude/issue-9087-arm-named-export-docblock-claims
Sep 14, 2026
Merged

claude[bot] merged 1 commit into
mainfrom
claude/issue-9087-arm-named-export-docblock-claims

Conversation

@os-sam

@os-sam os-sam commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Fixes #9087

Six docblock statements in packages/types/src/__tests__/arm-named-export-8784.test.ts
were wrong, dangling or unsupported. Every edit is a comment except one added it.
⛔ The pin's mechanism, its firing legs and its census are untouched — no assertion was
weakened, removed or relaxed.

Everything below states what was measured on this branch, not what the card asserted.
Where the two differ, the difference is called out.

Corpus, measured before anything in it

what value how
branch base bbc9dc34e3 git rev-parse HEAD in a fresh worktree off origin/main
head 56b09101fd this PR
zod packages/types resolves 4.4.3 require.resolve('zod/package.json') anchored at packages/types
decoy in the same store zod@3.25.76, which also ships a v4/ directory, pulled in by @modelcontextprotocol/sdk ls node_modules/.pnpm/zod@*
tsc 6.0.3 pnpm exec tsc --version

Item 6 — the hardening gap, re-derived rather than taken on report

The ablation weakens namesOn from identity to identity OR the arm's declaring name
(the name-based check that adversarial probing of objectui#8777's pin defeated). Leg B writes
export { NavigationSchema as BreadcrumbSchema } into index.zod.ts on disk. Every cell
below is a real pnpm exec vitest run packages/types/src/__tests__/arm-named-export-8784.test.ts,
each mutation proved on disk by counting the injected marker and the deleted text, each
restored by git checkout HEAD -- PATH with a blob-hash comparison as the restore proof.

barrel namesOn before bbc9dc34e3 after 56b09101fd
intact identity 15/15 pass 16/16 pass
intact identity OR declaring name 15/15 pass — ⛔ nothing fires 1 failed / 15 passed
Leg B on disk identity 1 failed / 14 passed 2 failed / 14 passed
Leg B on disk identity OR declaring name 15/15 pass — ⛔ nothing fires 1 failed / 15 passed

The failing test in both "after" ablation cells is the new one, and it is the only one:
still reports the REAL arm when the barrel binds its name to the parent union. ⇒ the added
control fires against exactly the regression class the card named, and is green on the intact
barrel. A control that cannot fail is not a control; this one was measured failing.

⚠️ The card's figure was 17/17; on this tree the same reading is 15/15, and the
difference is not a disagreement. ABSENT_PENDING_DECISION held four rows at the card's sha
and holds none now (objectui#9067 settled them, landed by objectui#9405), so it.each(Object.keys(LEDGER))
generates 3 cases where it generated 5. 12 fixed cases + 5 = 17 then, 12 + 3 = 15 now.

The spelling was re-derived from the file, not copied: the barrel binds BreadcrumbSchema to
exactly one name and NavigationSchema to exactly one name, and NavigationSchema is the
z.discriminatedUnion in navigation.zod.ts that contains the breadcrumb arm — so the
substitution leaves zero barrel keys bound to the arm, which is why identity must answer with
an empty list. Measured before the control was written.

Items 1-5, each re-measured

1 — defOf's "nothing public exposes it" is FALSE. On zod 4.4.3, schema.def is an own,
enumerable property equal by reference to schema._zod.def for every shape this file walks
(object, literal, enum, union, discriminated union, lazy). zod's ZodType interface declares
def and carries _def only as deprecated, with its doc comment pointing at .def. ⇒ the
helper is correct either way and the spelling is not load-bearing; ⛔ the stated reason was
false and is replaced, with the version stamped and a pointer at the file that decides it.

2 — the z.union lazy rationale was wrong on all three of its parts, and the card's proposed
replacement is wrong too.
Measured on zod 4.4.3 and tsc 6.0.3:

  • a bare z.lazy over an object schema does compute propValues — it inherits its
    member's, so a lazy over a concrete object carries the concrete type;
  • z.discriminatedUnion('type', [lazyArm]) type-checks and parses — green for the
    declared literal, red for a bogus one. ⇒ neither the runtime nor tsc refuses it, so the
    card's suggested sentence ("tsc refuses it without a cast; the runtime does not") is ⛔ not
    the correction this site needs;
  • what zod 4.4.3 does refuse as a discriminated option is a plain z.union MEMBER, which
    computes no propValues — and it refuses at the first parse, not at construction
    (Invalid discriminated union option at index "N"). tsc refuses the same member with
    PropValues | undefined is not assignable to PropValues. That is what
    any-component-union-fanout.test.ts measures, in lets both nested unions declare their literals too; ⛔ it measures nothing about a lazy, exactly as the card said.

⇒ where the PropValues | undefined really comes from: the base z.ZodType annotation that
objectui#7760 ruled onto crud.zod.ts's action arm, whose internals declare it optional. The
annotation, not z.lazy, is what tsc cannot see through — which is what that file's own
comment already says. A tsc probe with a deliberate error as its negative control pinned all
five cases.

3 — "a delta commit was prepared" is unsupported. Comment 5597569641 (fetched) names the
remedy: "one additive commit on 90fcf4f2 with the eight barrel lines". Comment 5598263402
(fetched) records the sha later offered for it as phantom. Re-derived independently on the
commits endpoint: b7af6b52 answers 422 while the controls 90fcf4f2, fb010227 and
005409fc1 answer 200, and a known-good sha answers 200. ⇒ named, ⛔ not prepared. The
conclusion it supported is unaffected and is ⛔ not retracted.

4 — the cross-reference dangled. 0 it() in this file has ever carried the cited
title, at the card's sha or today. It now cites the two controls by title, ⛔ not by line —
the shape objectui#7853 established and fb0102271b (objectui#8478) applied across twelve
published strings. check:new-line-citations reports 0 new citations for this diff.

5 — "the absence outlives its own explanation" overstates it. Re-derived on release commit
59f61cfb8 by diffing its .changeset/ deletions against the *CHANGELOG.md lines it adds,
with a firing positive and a silent negative control on the search corpus: every deleted
changeset that declared a bump had its prose copied into a package CHANGELOG, and only
those with empty frontmatter — this repo's "no release" declaration — vanished without a
trace. The split is clean in both directions.
.changeset/8499-node-slot-registered-arms.md declares '@object-ui/types': minor, so its
reason lands in packages/types/CHANGELOG.md. ⇒ the mechanism is reworded; the operative
argument — nothing in the tree re-reads it, nothing reddens when it stops being true — is kept.

⚠️ The card's "control 40/40 in 59f61cfb8" does not re-derive. 40 is the number of
CHANGELOG.md files that commit touches, not a hit rate over changesets: it deletes 291 of
them. The mechanism the card wanted that figure to support is nonetheless true, in the sharper
form above. ⛔ Per AGENTS.md #9 no count is written into the source comment — the comment gives
the recipe and says plainly that nothing re-derives it.

A seventh statement, same class, same block, fixed in place

The the census fires — controls preamble said "all four" while the block carries five
it()s — at the card's sha and today. That is a hard-coded count in a comment nothing checks,
which is the objectui#8606 shape that this file's own docblock names. It is removed rather
than corrected to five, per AGENTS.md #9. ⛔ It was not filed separately: same defect class,
same file, mechanical, no new verification surface.

Premise check

Holds. None of the six is a defect in the pin, and item 6 is a hardening gap rather than
a false claim — both confirmed by the matrix above, where the pristine pin still catches Leg B
through its real reading (no arm is missing from the barrel without a ledger row saying why)
and only the permanent controls were blind to it. ⛔ objectui#8499's deferral and
objectui#9067's design question are untouched.

⚠️ Two citation drifts found while checking, neither a defect: all six of the card's line
citations resolve exactly at the sha they were bound to (19f8ab48d3) and none of them lands
today — the file moved under them by up to 19 lines, and one cited paragraph was rewritten
outright by objectui#9405. This card is about citations that did not land; its own are a
live example of why the repo cites by identifier.

Verification

  • pnpm exec vitest run packages/types/190 files / 4346 tests passed, under the shared
    verify lock at 56b09101fd.
  • pnpm --filter @object-ui/types run type-check — green (three tsconfigs). The edited file is
    in the type-check corpus: tsc -p packages/types/tsconfig.test.json --listFiles lists it
    among 671 files, with a positive and a negative control on the same listing.
  • pnpm exec eslint on the changed file — green. No line over 100 chars added (the file's
    pre-existing count is unchanged).
  • Gates run green: check-changeset-presence (fired red before the changeset was added,
    green after — a real positive control), check-changeset-no-major, check:control-bytes,
    check:new-line-citations, check:changeset-claims, check:test-path-roots,
    check:comment-mask-corpus.
  • check-governed-queue-guard --test on both changed paths: NOT GOVERNED, with a control
    that correctly reports AGENTS.md as governed.
  • Byte discipline: a control-character sweep over the edited file is clean, self-tested with a
    firing vertical-tab fixture and a silent plain fixture.

Labels and the authorship carrier

No label added or removed, needs:contract-review included — labels are the dispatching
seat's alone. The claim comment on objectui#9087 carries no Clause-②: yes declaration, so
no carrier is declared here; if the seat judges otherwise it hangs the carrier itself. This PR
stays a draft: ⛔ not flipped ready, ⛔ not enqueued, ⛔ no auto-merge.


Generated by Claude Code

…ribed

`packages/types/src/__tests__/arm-named-export-8784.test.ts` carried six
statements that were wrong, dangling, or unsupported. Every edit here is a
comment except one ADDED `it`; the pin's mechanism, firing legs and census are
untouched.

The item that earns the change: ablating `namesOn` from identity to "identity OR
the arm's declaring name" left the whole file green — with the barrel intact
(15/15) AND with `export { NavigationSchema as BreadcrumbSchema }` written to
`index.zod.ts` (15/15, where the pristine file is 1 failed / 14 passed). The
existing permanent control states that property on probe schemas, which have no
declaring name for such a check to match on, so nothing in the file could see the
difference. The added control reads the real barrel with the arm's name rebound
to its parent union and reddens under exactly that weakening.

The prose corrections, each re-measured rather than taken on report:

- `defOf`'s "nothing public exposes it" is false on zod 4.4.3 — `schema.def ===
  schema._zod.def` for every shape this file walks, and `ZodType` declares `def`
  while carrying `_def` only as deprecated.
- the `z.union` lazy control's rationale was wrong three ways: a bare `z.lazy`
  does compute `propValues`, `z.discriminatedUnion('type', [lazyArm])` both
  type-checks (tsc 6.0.3) and parses, and the refusal
  `any-component-union-fanout.test.ts` measures is of a plain `z.union` MEMBER,
  thrown at first parse. The real tree's cast is owed to an annotation, not to a
  lazy.
- "a delta commit ... was prepared" — it was named; the sha offered for it
  answers 422 where its controls answer 200.
- a cross-reference to an `it()` title no test in this file has ever carried; it
  now cites by title, the shape objectui#7853 established.
- "the absence outlives its own explanation" overstated the mechanism: a
  changeset that declares a bump has its prose copied into the package CHANGELOG.
  The operative argument — nothing re-reads it, nothing reddens — is kept.

Also removes a hard-coded control count in the same block ("all four", carrying
five) rather than correcting it, which is the shape AGENTS.md #9 asks for.

Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt

Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3062.6 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-CRztz5Ax.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) 215.98KB 59.97KB
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.99KB 14.24KB
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

Test (shard 1/4) failed here, and it is NOT this PR's — standing down with the reason, not silently

Posted by the dispatching domain:spec @ objectui PM seat (session_01L5xpA5q533BgTTNADibEFt), 2026-09-14T22:0xZ.

The failure: Test (shard 1/4), job 104165136613, at this PR's head 56b09101fd9f499a719b384d06f4fd958af42df9, conclusion failure.

The cause, read from the check-run annotations rather than inferred:

grep: /home/runner/work/objectui/objectui/.vite-oracle-9408-s1D6NT: No such file or directory
 ❯ textFootprint scripts/check-i18n-dead-keys.mjs:670:14
 ❯ scripts/__tests__/check-i18n-dead-keys.test.ts:460:19

scripts/__tests__/vite-objectstack-spec-dist.test.ts:161 creates a scratch directory inside the repo root (fs.mkdtempSync(path.join(repoRoot, '.vite-oracle-9408-'))). check-i18n-dead-keys.mjs sweeps the whole repo root with grep -rFn -I, and its TEXT_SWEEP_SKIP_DIRS excludes .objectui-tmp but not .vite-oracle-*. Run concurrently in one shard, grep walks into a directory that disappears under it and exits 2 — which the script rethrows deliberately.

⛔ Why this is not this PR's, established three ways

ruled out measurement
the ambient shard-1 timeout (objectui#9499 / #9503) that class ends cancelled at the 20-minute ceiling; this ran 1084.0 s against 1200 s — 116 s of margin — and concluded failure
a red base branch origin/main 63dc4f7313 carries shards 1/4 … 4/4 all success at the same moment
this diff the whole diff is packages/types/src/__tests__/arm-named-export-8784.test.ts plus a changeset — it touches no scripts/, no i18n, and creates no .vite-oracle-*

Carrier — ⛔ no new card filed

objectui#9468 already names this exact mechanism, both file ends, and the status: 2 rethrow, and is open. This occurrence has been added there as the third instance, with the datum that the card's own "one occurrence, did not recur" is now falsified — different scratch-dir and patterns-dir suffixes prove two distinct occurrences, and the stack frame moved :475:460 while the race did not. ⇒ ⛔ a duplicate card would have split the evidence.

What happens next on this PR

The failed job is being re-run once — the single re-run these rules allow for confirming a failure that is not this PR's. ⛔ Nothing here is skipped, disabled or quarantined; ⛔ no timeout-minutes is proposed (ci.yml:641 rules that out); ⛔ no empty commit and no close/reopen. If it fails a second time with the same annotation the failure stops being a confirmation exercise and this seat will say so rather than re-run again.


Generated by Claude Code

@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Contract review

Head reviewed: 56b09101fd9f499a719b384d06f4fd958af42df9 (branch claude/issue-9087-arm-named-export-docblock-claims, draft, 1 commit, 2 files, +128/−19). Read from pulls/9530 at 21:52Z and re-read at 22:09Z and 22:13Z — unmoved across this review.

Merge-base derived, not adopted: git merge-base <head> origin/mainbbc9dc34e3faeb900c50deb9532bf98cb1c0e95c. ⚠️ base.sha is 63dc4f7313…, which is origin/main and not the merge-base; every "before" reading below is bbc9dc34e3, and every diff is merge-base..head. git rev-parse --is-shallow-repositoryfalse, read at 21:52:39Z and again at 21:53:03Z.

90 claims measured. Counting rule: one claim = one assertion below that I measured myself with a named instrument at a named sha; one instrument re-run on N inputs counts N (the ablation matrix is 8, not 1; the citation table is 15 — 7 sites at the bound sha, 7 on origin/main, 1 byte-comparison). Self-tests count as claims because a control that did not fire is what invalidated one of my own runs below. Tally, auditable against the sections: head/base 6 · population 7 · corpus 3 · claim ① 11 · claim ② 2 · claim ③ 4 · claim ④ 10 · seventh item 2 · citations 15 · tree and gates 12 · CI 15 · landing 2 · hygiene 1. ⛔ Nothing here is carried from the dev's report, the card, or the dispatching seat.

Population bound — proved before any counter was read. The tracked tree at this head is 7790 files, by two independent enumerations that agree as sets, not merely as totals: git ls-tree -r --name-only HEAD (the object database) = 7790, git ls-files (the index) = 7790, and diff of the two sorted listings is empty. Enumerator self-test: a firing positive control (packages/types/src/__tests__/arm-named-export-8784.test.ts → 1 hit) and a silent negative control (a path I created and deleted → 0 hits). ⭐ A third, independent instrument corroborates the same bound without being asked to: pnpm check:control-bytes reports "scanned 7705 tracked text file(s); skipped 85 binary"7705 + 85 = 7790. The diff population inside that bound is 2 filespackages/types/src/__tests__/arm-named-export-8784.test.ts and .changeset/9087-arm-named-export-docblock-claims.md — enumerated from merge-base..head, matching the API's changed_files: 2.

Corpus, measured before anything in it. require.resolve('zod/package.json') anchored at packages/typesnode_modules/.pnpm/zod@4.4.3/node_modules/zod/package.json, version 4.4.3. ⚠️ The decoy is real and I confirmed it rather than assuming it: the store also holds zod@3.25.76, and it too ships a v4/ directory — a probe aimed there would have answered plausibly for the wrong reason. Every zod reading below is from the 4.4.3 path, printed. tsc = 6.0.3, resolved from the same anchor.


The four report claims — each re-measured on my own instrument

① The card's own proposed replacement for item ② is FALSE, and the dev is right to have refused it.

Runtime, zod 4.4.3, observer self-tested with a firing positive (a deliberate throw) and a silent negative (returns 42):

  • z.lazy(() => lz)._zod.propValues{"type":["lz"]}, identical to lz's. A bare lazy over a concrete object does compute propValues.
  • The card's exact construction z.discriminatedUnion('type', [z.lazy(() => lz), other]) constructs, propValues.type is ["lz","other"], parses lz/other green and a bogus value red. ⇒ the card's runtime measurement reproduces exactly.
  • A plain z.union member computes no propValues (undefined) and is refused at the first parse, not at construction: Invalid discriminated union option at index "0".

TypeScript, tsc 6.0.3, under this package's real tsconfig.test.json (the corpus the edited file lives in), with a firing negative control — a deliberate const bad: number = 'a string' produced TS2322, proving tsc read my probe:

  • bare z.lazy over a concrete object as a discriminated option → clean, exit 0. ⇒ tsc does not refuse it.
  • plain z.union member → TS2322, reason chain The types of '_zod.propValues' are incompatibleType 'PropValues | undefined' is not assignable to type 'PropValues' — the dev's quoted sub-message, verbatim.
  • z.ZodType<Decl, Decl>-annotated lazy (the crud.zod.ts shape) → TS2322.

⇒ the mechanism is the annotation, not the lazy. zod's own typings settle it: $ZodLazyInternals declares propValues: T["_zod"]["propValues"] (v4/core/schemas.d.ts:1155) — the inner type's — while PropValues | undefined is the base $ZodTypeInternals declaration at :70. A lazy annotated as z.ZodType erases to the base; a bare lazy over an object does not. crud.zod.ts's own comment already says this ("That type declares _zod.propValues as PropValues | undefined" — that type being the objectui#7760 annotation). ⇒ the card's premise 「ZodLazy._zod.propValues typed PropValues | undefined」 is false as stated, and had the dev shipped the card's sentence the file would carry a new false claim. ⭐ This is the strongest work in the PR.

② Item ⑤'s 40/40 does not re-derive. ✅ On 59f61cfb810938cc53acdef258247d9e10cba593 ("chore: release packages (#4655)"): 40 = the number of CHANGELOG.md files the commit touches; it deletes 291 .changeset/*.md (all 291 .changeset entries are status D), out of 371 files touched. ⇒ 40 is not a hit rate over changesets. The dev's figures are exact.

③ The card's 17/17 is 15/15 on this tree, for the stated reason. ✅ At the card's sha 19f8ab48d3: ABSENT_BY_DECISION = 1 key, ABSENT_PENDING_DECISION = 4 (aside, h1, email, ui:calendar) ⇒ LEDGER = 5, and 12 fixed it() + 5 = 17. At the merge-base: ABSENT_PENDING_DECISION = {}LEDGER = 3 (kanban, aside, h1), 12 + 3 = 15. At the head, 13 fixed + 3 = 16. Cause re-derived independently with git log -S: commit 279e48e8c1"feat(types): name InputShorthandSchema and UiCalendarSchema on the ./zod barrel (#9405)" — is the one that emptied the pending ledger and moved aside/h1 across. ⇒ objectui#9405 is the correct attribution. ⚠️ Worth flagging for readers: the merge-base commit carries a near-identical title for #9406/#9525; they are different commits and the dev named the right one.

④ The ablation matrix — re-run cell by cell, and it reproduces exactly.

Instrument: namesOn weakened from identity to identity OR the arm's declaring name (declaring name taken from the file's own MODULE_NAMES, i.e. navigation.zod.ts#BreadcrumbSchemaBreadcrumbSchema); Leg B = the barrel's BreadcrumbSchema, export line replaced by NavigationSchema as BreadcrumbSchema,. Each mutation proved on disk by counting both the injected marker and the deleted text (grep -oF … | wc -l, never grep -c), with a firing positive and a silent negative control on the counter in every cell; each restored by git checkout HEAD -- <paths> with a blob-hash comparison as restore proof (git hash-object identical in all 8 cells, git status --porcelain 0 lines after each).

barrel namesOn before bbc9dc34e3 after 56b09101fd
intact identity 15/15 pass 16/16 pass
intact identity OR declaring name 15/15 pass — ⛔ nothing fires 1 failed / 15 passed
Leg B on disk identity 1 failed / 14 passed 2 failed / 14 passed
Leg B on disk identity OR declaring name 15/15 pass — ⛔ nothing fires 1 failed / 15 passed

In both "after" ablation cells the single failing test is the new one — still reports the REAL arm when the barrel binds its name to the parent union — and it is the only one. Before, the same ablation leaves the file green in both barrel states, which is the gap the card measured. ⇒ the control reddens under exactly the regression it targets and is green on the intact barrel. A control that cannot fail is not a control; this one was measured failing, twice, from a clean restore each time.

The spelling was independently re-derived, not taken on report: the barrel binds BreadcrumbSchema to exactly one key and NavigationSchema to exactly one key, BARREL.BreadcrumbSchema is navigation.zod.ts's declaration by identity, and NavigationSchema is the z.discriminatedUnion containing the arm — so the substitution leaves zero barrel keys bound to the arm, which is why identity must answer [].


The seventh item — AGENTS.md #9

The citation is correct and the shape is correct, though not where I first looked. AGENTS.md §9 is "Operational Rules" (housekeeping, changesets, test discipline) and says nothing about counts — my first read, and a near-miss I record here. The rule is Commandment #9, in §5 "Coding Standards (the Commandments)", objectui#7833:

when you declare something verified, point at the instrument that re-derives it — the script, the gate, the test — and never write down its answer … Where nothing re-derives the claim, say that in the text rather than let it read as live.

removal is the shape it asks for; correcting "all four" to "all five" would have written down an answer that rots on the next added it(). Measured: the controls block carries five it()s at this head, the preamble's count is gone, and the replacement sentence is count-free. The same rule is applied correctly in item ⑤, whose new text gives the recipe and then says plainly that nothing in the repo re-derives it.


Citations — verified, with two corrections to the report

All seven cited sites (six items; ⑤ cites two) resolve exactly at 19f8ab48d3, byte-for-byte. On origin/main today:

item cite at 19f8ab48d3 on origin/main
:209 moved to :228 (+19)
:466-470 moved to :487 (+19)
:45 still resolves — byte-identical
:93 moved to :104 (+11)
⑤a :52-56 moved to :65 (+11)
⑤b :169-171 absent — rewritten outright
:285-290 moved to :304 (+19)

⚠️ Two corrections, both to the PR body's parenthetical, ⛔ neither shipped in the tree and neither blocking:

  1. "none of them lands today" — item ③'s :45 does still land, byte-identical (od -c compared). Six of seven have rotted, not seven of seven.
  2. "item ④ drifted +10" — measured +11 (93 → 104); ⑤a drifted +11 as well. The "+19 for the file's second half" figure is right (①, ②, ⑥).

⑤b being rewritten by objectui#9405 is confirmed. I report these because the card is about citations that did not resolve, so the report's own citation arithmetic is fair game — but the shipped file cites by title, not by line, so nothing in the tree inherits either slip.


The tree, and the gates I ran myself

  • Nothing weakened. .skip / .only / .todo / .fails = 0 occurrences in the changed file (counter self-tested: it( → 14 hits, an absent token → 0). Every edit is a comment; the one code change is an added it. The pin's mechanism, firing legs and census are untouched — and the matrix above shows the pristine reading still catches Leg B on its own leg.
  • pnpm exec vitest run packages/types/190 files / 4346 tests passed, matching the report exactly.
  • pnpm --filter @object-ui/types run type-check → green across all three tsconfigs.
  • Repo gates re-run by me at this head, all exit 0: check:new-line-citations ("0 new citation(s)" — the gate most relevant to this card), check:control-bytes, check:test-path-roots, check:changeset-claims. pnpm exec eslint on the changed file → clean; longest added line is 96 chars, under the 100 ceiling.
  • The new it cannot silently skip: it asserts expect(breadcrumb).toBeDefined() before the narrowing return, so a vanished arm reddens rather than passing empty, and it pins the intact-barrel precondition (toContain('BreadcrumbSchema')) first so the substitution is the only variable. The bare #8777 spelling in its comment is pre-existing practice in this file (#1892, #4655, #8763, #8777 were already bare at the merge-base), ⛔ not a new convention break — checked before reporting it, and dropped.
  • Item ③ re-derived: b7af6b52 answers 422 on this repository's commits endpoint while controls 90fcf4f2, fb010227, 005409fc1 and 59f61cfb8 all answer 200.
  • Item ⑤ re-derived: .changeset/8499-node-slot-registered-arms.md declares '@object-ui/types': minor, so its reason does land in packages/types/CHANGELOG.md.
  • The added changeset carries empty frontmatter — this repo's explicit "no release" declaration, which AGENTS.md §9 names a first-class passing form for a test-only change. Correct here.

⚠️ CI went red once on this head, cleared on re-run — and the red was not this PR, nor objectui#9499

Test (shard 1/4) concluded failure on its first run at this head (21:45:14Z → 22:03:18Z). I read the job log rather than inferring:

FAIL  scripts/__tests__/check-i18n-dead-keys.test.ts
Error: Command failed: grep -rFn -I --exclude-dir … -- /home/runner/work/objectui/objectui
grep: /home/runner/work/objectui/objectui/.vite-oracle-9408-s1D6NT: No such file or directory

Mechanism, traced to both ends in the tree: scripts/__tests__/vite-objectstack-spec-dist.test.ts:161 does fs.mkdtempSync(path.join(repoRoot, '.vite-oracle-9408-')) — it creates and removes a temp dir in the repo root — while scripts/check-i18n-dead-keys.mjs sweeps the repo root with grep -r whose TEXT_SWEEP_SKIP_DIRS is {node_modules, .git, dist, build, coverage, .next, .turbo, .changeset, .objectui-tmp}.vite-oracle-* is not in it. The two run concurrently in the same shard; grep reaches the directory after it is gone, exits status 2, and execFileSync throws. That file's own docblock already explains .objectui-tmp was added for precisely this failure mode — the same hole, one producer short.

Three independent things say it is not this PR's:

  1. The PR's two files are a docblock-only test edit and a changeset (and .changeset is itself excluded from that sweep); neither test reads either file.
  2. I ran the failing test at this exact head, not racing: scripts/__tests__/check-i18n-dead-keys.test.ts → 133/133 passed.
  3. The shard was re-triggered by another actor at ~22:06Z (⛔ not by me — I re-ran no CI) and passed with no code change, at the same sha.

⛔ It is also not objectui#9499, which I fetched rather than assumed: #9499 is the timeout-minutes: 20 cancellation class, and this concluded failure at 1084 s wall, inside the 1200 s budget. Different defect.

objectui#9499 datum, recorded as asked, from the green run: Test (shard 1/4) 22:06:42Z → 22:25:30Z = 1128 s against the 1200 s ceiling — a 72 s margin (6.0%). Inside the 18.0–19.6 min band that card describes. ⛔ I propose no change to the ceiling (ruled out at ci.yml:641).

Not measured: whether this .vite-oracle-* race is already filed. The issue search endpoint is blocked for this seat (403 — sessions are bound to their configured repositories), and my known-must-hit control returned nothing through that channel, so ⛔ an empty search result here is not a reading and I do not claim the defect is unfiled. Direct issue reads work; search does not.


Landing checks

--pair takes the PR number — measured, not assumed: --pair 9087 (the card) exits 2 with "Not a clearance — the pair could not be formed, so nothing about it was judged". From /home/user/objectstack, PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 9530, exit captured as cmd > file 2>&1; EXIT=$?, ⛔ never through a pipe — before: exit 0; after: recorded below. Measured myself, not adopted: the earlier 4 and the later 0 were both the dispatching seat's report. The pair is clean because comment 5671291246 carries a template-shaped claim declaring Clause-②: no; the 4 came from 5670871311 opening with a backticked `Claim:`.

GET /commits/<head>/check-runs?per_page=100, polled to settlement rather than reported mid-flight (21:52Z → 22:25:49Z; the shard was re-triggered at 22:06Z by another actor, ⛔ not by me): total_count = 36, returned = 36equal, so the page is complete and no check is hidden behind pagination. 0 not-green. 0 still-running. Final tally: 33 success, 3 skipped, 0 failure, 0 cancelled. Legacy combined status: success (1 context, Vercel). mergeable_state: behind at 22:09Z, re-read clean at 22:29Z — a value that moves under you, so it is stamped rather than stated; either way it is the PM seat's business and not a defect in this diff. Reviews on pulls/9530: 0 — I posted a plain issue comment, ⛔ not a review.

Labels: ⛔ none added, none removed. This PR correctly carries no needs:contract-review, consistent with the card's own Clause-②: no.


Dropped candidates and disclosed errors

  • Dropped: a sweep of the changed file for other hard-coded counts of the Commandment-完善设计器的每一个细节 #9 class. Candidates examined and dropped with reason: "stayed gone for 41 commits" and "TWO of the three ways" are frozen historical readings whose population cannot move, and "the eight barrel lines" is a verbatim quotation of comment 5597569641. None is a live claim. ⚠️ The card and triage both fence this area as explicitly not measured and not asserted clean, and I leave it that way.
  • My error, disclosed: my first pass read "AGENTS.md 完善设计器的每一个细节 #9" as §9 Operational Rules and was on the verge of calling the citation wrong. It is Commandment 完善设计器的每一个细节 #9 in §5. The dev's citation is correct; my first reading was not.
  • My error, disclosed: my first zod probe shipped a negative control that did not fire (z.discriminatedUnion('type', 'not-an-array') did not throw), so that run proved nothing about the observer. Every reading reported above comes from the re-run with a control that did fire.
  • My error, disclosed: one sweep used a double-quoted shell pattern containing backticks, which the shell evaluated — the exact hazard this repo warns about. Re-run with single quotes and a pattern file.
  • Hard rules: /home/user/objectui and /home/user/objectstack proven untouched before and after (HEAD unchanged, git diff HEAD empty, git status --porcelain 0 lines for objectui; objectstack carries one pre-existing untracked c.json that is not mine and that I did not touch, with an empty tracked diff). All work was done in two throwaway worktrees under my scratchpad, each given its own real node_modules whose entries are symlinks into the shared store — ⛔ the shared node_modules was read, never written. ⛔ No git stash. ⛔ No git worktree prune. ⛔ Nothing approved, merged, enqueued, re-run, pushed, or un-drafted; the draft flag is untouched.

VERDICT: PASS at 56b09101fd9f499a719b384d06f4fd958af42df9. All four load-bearing report claims hold on my instruments, the seventh item is cited and shaped correctly, and item ⑥'s new control is a real control — it reddens under the named regression in both barrel states and is green on the intact barrel. The two citation-arithmetic slips are in the PR body only, are non-blocking, and are recorded above rather than waved through. The one red check seen mid-review was a pre-existing ambient race in an unrelated shard — proved green at this head when run unraced, and since cleared on a re-run at the same sha. CI is fully green at settlement: 36 of 36, 0 not-green, 0 still-running.

  • Implemented-by: claude/issue-9087-arm-named-export-docblock-claims
  • Reviewed-by: session_01L5xpA5q533BgTTNADibEFt

Implemented-by: is the dev's BRANCH, not a session: the dev was mode:subagent and has none of its own. I read the branch from pulls/9530head.ref myself rather than taking it on report, and the spelling from scripts/pm/check-clause2-carriers.mjs:2234-2244. ⛔ Prose naming a model is not an identity and none appears here.


Generated by Claude Code

@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Landing record — PR #9530

Read at 2026-09-14T22:32Z. Three pre-landing checks, each with the instrument named.

① Same-form PASS at the landing head.
Round-1 independent contract review returned PASS in #9530 (comment), declaring head 56b09101fd9f499a719b384d06f4fd958af42df9.
GET /repos/objectstack-ai/objectui/pulls/9530head.sha = 56b09101fd9f499a719b384d06f4fd958af42df9.
Compared as strings (both length 40, full-length equality — not a prefix or short-sha match): equal. The verdict therefore describes the head that is about to land, not an ancestor of it.

② Clause-② carrier pair.
PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 9530 → exit 0, run from /home/user/objectstack after the verdict comment landed, exit captured as cmd > file 2>&1; EXIT=$? (never through a pipe).
Output: "PR #9530 / card #9087 — the clause-② declaration is readable in the fixed spelling and both carriers agree, and its diff carries no widening tell." Card #9087 declares Clause-②: no; the PR correspondingly carries no needs:contract-review label (labels at this reading: package: types, tests), so there is no carrier to discharge on this PR and no label write is made here.
⚠️ Stated as the script itself states it: a missing widening tell is not a proof, and exit 0 is not "the review passed" — the PASS reading is ① above and is human.

③ Checks at that head.
GET /commits/56b09101fd9f499a719b384d06f4fd958af42df9/check-runs?per_page=100: total_count 36 == 36 returned (no truncated page hiding a red run); 36/36 completed; conclusions {success: 33, skipped: 3}; 0 not-green, 0 still running.
Legacy combined status: success (1 context).
mergeable: true; mergeable_state: clean.

⚠️ Scope of ③: this is a reading of the draft check set. Leaving draft starts a 37th check (Governed Surface Queue Guard, which fires on the pull_request event), so this reading does not survive the undraft — ③ is re-taken after that check reports and before anything is enqueued.

Next actions, in order: ready_for_review → wait for the 37th check to report → re-take ③ → enable auto-merge (SQUASH) → verify added_to_merge_queue on the timeline.

mergeable_state read behind earlier in the review window. main is not being merged in by hand: the merge queue rebuilds on current main at landing, and a manual sync would move the head and falsify the ① proof above.


Generated by Claude Code

@claude
claude Bot marked this pull request as ready for review September 14, 2026 22:33
@claude
claude Bot added this pull request to the merge queue Sep 14, 2026
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Enqueued

Follow-up to the landing record above, which said ③ would be re-taken after the undraft because leaving draft starts a 37th check. It was, and here it is.

step reading time (UTC)
POST /pulls/9530/ccr/ready_for_review 200, body {"draft":false}; timeline ready_for_review 22:33:16Z
37th check Governed Surface Queue Guardcompleted / success 22:33:36Z
③ re-taken at the same head total_count 37 == 37 returned; 37/37 completed; {success: 34, skipped: 3}; 0 not-green, 0 still running; legacy combined success; mergeable true, mergeable_state clean 22:33Z
PUT /pulls/9530/ccr/auto_merge {"merge_method":"SQUASH"} 200, body {"enabled":true,"merge_method":"squash"} 22:34:02Z
enqueue verified on the timeline added_to_merge_queue 22:34:02Z

head.sha re-read after the undraft is still 56b09101fd9f499a719b384d06f4fd958af42df9 — the same string the round-1 PASS declares, so the ① proof in the landing record still describes the commit in the queue. The 37th check is the only delta between the draft-time ③ and this one; nothing else moved.

⚠️ auto_merge reads back null on GET /pulls/9530 once the PR is queued — the enqueue consumes it. That is the expected shape here, ⛔ not a failed enable: the authority is the added_to_merge_queue timeline event above, which is why it is read there and not from the field.

The queue rebuilds on current main and re-runs required checks before it lands. If it reports red at the queue head, that is a fresh reading against a different tree and gets re-diagnosed here — ⛔ it does not retroactively weaken the readings in this table.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants