Skip to content

fix(types): narrow ObjectCalendarSchema.data to the protocol's array arm - #9348

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-9239-object-calendar-data-array-arm
Sep 13, 2026
Merged

fix(types): narrow ObjectCalendarSchema.data to the protocol's array arm#9348
os-sam merged 1 commit into
mainfrom
claude/issue-9239-object-calendar-data-array-arm

Conversation

@os-sam

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

Copy link
Copy Markdown
Collaborator

Fixes #9239

What this changes

ComponentPropsMap['object-calendar'].data on @objectstack/spec declares z.array(z.unknown()).optional()"Pre-fetched records — skips the internal fetch". Both published faces of @object-ui/types declared ViewData on the same key: a { provider, items } config object, which that row refuses by kind. This PR brings both faces onto the protocol's array arm.

This is a breaking narrowing of a published authoring type — the accept set genuinely shrinks, which is the point. needs:contract-review is hung on this PR for that reason.

face before after
packages/types/src/objectql.ts data?: ViewData data?: SpecObjectCalendarProps['data'] (derives the protocol's row; resolves to an optional array of UNKNOWN)
packages/types/src/zod/objectql.zod.ts ViewDataSchema.optional() z.array(z.unknown()).optional()

Requiredness is unchanged (optional on both faces), which is the zod-mirror-parity ratchet's criterion.

Premise 1 — re-measured first-hand, not inherited

The card, triage and the dispatch order all carried this reading second-hand; triage said so explicitly (@objectstack/spec was not installed in its container). Measured here from the installed artifact in this worktree, @objectstack/spec 17.4.0, resolved at node_modules/.pnpm/@objectstack+spec@17.4.0_ai@7.0.65_zod@4.4.3_/node_modules/@objectstack/spec, imported through the published @objectstack/spec/ui entry point:

spec version            = 17.4.0
data ctor               = ZodOptional  -> unwrapped ZodArray -> element ZodUnknown
data.description        = "Pre-fetched records — skips the internal fetch"
safeParse { objectName: 'visit', data: { provider: 'value', items: [] } }
  -> success = false   issues = [{ code: 'invalid_type', path: ['data'], expected: 'array' }]
safeParse { objectName: 'visit', data: [] }              -> success = true
safeParse { objectName: 'visit', data: [{ id: 1 }] }     -> success = true
safeParse { objectName: 'visit' }                        -> success = true   (optional)

Premise confirmed. The protocol says exactly what all three readings said it says.

Two things the same probe turned up, both load-bearing for the acceptance list:

  • ComponentPropsMap has 45 keys and neither object-gantt nor object-map is among them (object-* keys: object-grid, object-metric, object-kanban, object-calendar, object-form, object-master-detail-form). So the published row governing those two blocks is this repo's own ViewDataSchema.optional() — the reason they keep the object arm is measured, not assumed.
  • ComponentPropsMap['object-calendar'] is spelled ObjectCalendarPropsSchema in the spec's own .d.ts, with type ObjectCalendarProps = z.input of that schema. The TS face derives it rather than re-spelling unknown[], so this key cannot drift from the protocol a second time — which is the defect this card exists for.

⭐ The parity ledger did NOT move

packages/types/src/__tests__/zod-mirror-parity.test.ts is held by PR #9343 this round and was not edited — but that file is a type map over every mirror, so the question is whether a row moved inside it. Instrument used: pnpm --filter @object-ui/types type-check, all three projects (tsc --noEmit + tsconfig.examples.json + tsconfig.test.json). exit 0.

Proof that the instrument actually covers that ledger rather than skipping it — tsc -p tsconfig.test.json --listFiles resolves 649 files, and the ledger is among them:

packages/types/src/__tests__/base-schema-zod-mirror-parity.test.ts
packages/types/src/__tests__/object-calendar-record-source-7313.test.ts
packages/types/src/__tests__/zod-mirror-parity.test.ts

object-calendar has no KnownDrift entry and still has none: both faces narrowed together at equal requiredness, and the zod input face of z.array(z.unknown()) is exactly what SpecObjectCalendarProps['data'] resolves to. ⛔ A green vitest on that file would have proved nothing here, and neither would re-running only the build tsc project; both are recorded false-green paths and neither was used as evidence.

The :102 / :104 / :106 equalities — each re-checked individually

  • :102 Equal of the calendar's data and the gantt's datainverted, not deleted. A bare deletion would have left the divergence unwitnessed (nothing would notice the two keys silently converging again), so the row now pins that they DIFFER, and a second row names the other side explicitly: TsObjectGanttSchema['data'] is still ViewData or undefined. ⛔ The pin cannot be satisfied by moving the gantt.
  • :104 staticData — re-checked on its own: any[] | undefined on both members. Still true, kept.
  • :106 objectName — re-checked on its own: string | undefined on both members. Still true, kept.

⭐ The trap in this very file

PR #9234's report recorded that its "the renderer still calls the ladder" row matched a bare resolveRecordSourceConfig(schema) which ObjectCalendar.tsx satisfied from a docblock line that merely names the function — a name occurring in a file is not a call site. Every new assertion in this PR is a verdict, not a text match: safeParse results compared against the protocol's own safeParse results on the same documents, plus type-level Equal rows. The one pre-existing text-matching row in the file (resolveRecordSourceConfig(schema, 'array'), already hardened by #9234) is untouched.

What else moved, and why

  • :165 dataOnly fixture is array-shaped now. Consequence handled rather than papered over: the gantt parity block feeds each member the arm its own published row declares (withArm), because that block measures the ladder — had it kept feeding one arm to both, the gantt would refuse dataOnly by kind and the comparison would read as a ladder divergence that does not exist.
  • DATA_DOCUMENT, the typed literal, is array-shaped; the provider-block spelling is now a compile error there, which is the declaration half of the fix.
  • A runtime pin that asserted success: true for data: { provider: 'object', object: 'events' } now asserts the refusal, at the key. That line was the mirror's half of the divergence.
  • requireRecordSource's docblock says what data means per member: the ladder is shared (presence, !== undefined, whatever the kind); the arm is not. On the calendar, data is pre-fetched records, ⛔ not a source to fetch from.
  • content/docs/plugins/plugin-calendar.mdx — its "refused on this block" section listed os validate, the save gate and the renderer; @object-ui/types was missing from that list precisely because it was the face that still accepted the document. It is listed now, with the breaking-narrowing note. The check:doc-* gates and the registered audit pass.

Verification

All exit codes captured by redirect before any pipe.

what command verdict
⭐ parity instrument pnpm --filter @object-ui/types type-check (3 projects) exit 0
instrument coverage tsc -p tsconfig.test.json --listFiles 649 files, ledger present
package build pnpm --filter @object-ui/types build exit 0
package tests pnpm exec vitest run packages/types/ 182 files / 4197 tests passed
eslint, whole farm pnpm exec eslint . --no-inline-config --format json 4914 files resolved; my 3 TS files 0 errors (28 + 1 + 1 pre-existing no-explicit-any warnings, none introduced)
types lint pnpm --filter @object-ui/types lint exit 0
gates check:spec-symbols check:control-bytes check:new-line-citations check:entry-guard check:changeset-claims check:phantom-deps check:unused-deps check:doc-types check:doc-fences check:doc-example-ids check:doc-example-readers all exit 0
changeset check-changeset-presence.mjs, check-changeset-no-major.mjs exit 0 / exit 0

Gate list derived from .github/workflows/lint.yml's and ci.yml's step lists, ⛔ not from package.jsoncheck:entry-guard is in that list and was run.

Corpus sweep

⛔ No authored document anywhere in the repo carries the retired spelling — re-measured rather than inherited from PR #9234's report: object-calendar appears in zero *.json / *.yml / *.yaml files and zero files under examples/, and no literal annotated : ObjectCalendarSchema sets data at all. So pnpm check (the CLI self-check over this repository) has no calendar document to refuse, and nothing that renders today stops rendering because of this PR — objectui#8348 is where runtime behaviour moved.

Out of scope

object-gantt / object-map object arms untouched. ⛔ @objectstack/spec untouched. ⛔ content/docs/releases/ untouched. ⛔ zod-mirror-parity.test.ts untouched (PR #9343 holds it).

Acceptance notes

Noted while working, not filed and not changed here:

  • packages/plugin-calendar/README.md teaches object-calendar with objectName only and never authors data, so it needed no edit — but it is the one calendar doc surface that does not carry the "refused on this block" note the .mdx carries. An observation about coverage, not a defect: nothing it teaches is wrong.
  • The REFUSAL_MESSAGE the record-source refinement emits is member-parameterised and still reads "declare one of data, staticData or objectName" for all three members, while what data admits is now member-specific. The message is not wrong (presence is what the refinement checks) and it is pinned by two tests; the docblock now carries the distinction instead. Style, not a defect.

Session, as prose so it survives a body rewrite: this PR was produced by an ObjectUI development agent running Claude Code in session session_01L5xpA5q533BgTTNADibEFt.


Generated by Claude Code

`ComponentPropsMap['object-calendar'].data` on `@objectstack/spec` declares
`z.array(z.unknown()).optional()` — "Pre-fetched records — skips the internal
fetch". Both published faces of `@object-ui/types` declared `ViewData` on the
same key: a `{ provider, items }` config object, which that row refuses by kind.

After objectui#8348 put the renderer on the protocol's side (decision batch #83,
maintainer verbatim 8348 uses the protocol as the authority), this mirror was the
lone published face still teaching the config-object spelling, so an author who
validated against `@object-ui/types` got a green verdict for metadata the
renderer ignores, `os validate` refuses and the save gate rejects.

- TS face: `data?: ViewData` -> `data?: SpecObjectCalendarProps['data']`,
  DERIVED from the protocol's own row so the key cannot drift from it again.
- Zod mirror: `ViewDataSchema.optional()` -> `z.array(z.unknown()).optional()`.
- Requiredness unchanged on both faces, so the zod-mirror-parity ratchet is
  unmoved (verified with `tsc -p tsconfig.test.json`, which is the only
  instrument that sees that ledger's type-level reconciliation).
- The `data` equality with `TsObjectGanttSchema['data']` is INVERTED rather than
  deleted, and both sides are named; the `staticData` and `objectName`
  equalities were re-checked individually and still hold.
- `requireRecordSource`'s docblock says what `data` means per member: the ladder
  is shared, the arm is not.

`object-map` / `object-gantt` keep the `ViewData` arm — neither has a
`ComponentPropsMap` row, so the row governing them is this package's own.
`@objectstack/spec` is untouched.

Co-Authored-By: Claude Opus 5 <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, 52 chunks) 3116.9 KB 3134.8 KB
Main entry chunk (gzip) 144.4 KB 350 KB
Entry file index-D9I28xMN.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

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

Size Limits

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

os-sam commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator Author

Contract review — PASS

Head reviewed: 4f2d1f4a67ad97ef3d84bdc0b493aabdc14be0fc

Implemented-by: claude/issue-9239-object-calendar-data-array-arm
Reviewed-by: session_01L5xpA5q533BgTTNADibEFt

⛔ Not SELF-REVIEW: the diff came from a mode:subagent dev (recorded by branch), the judgement from a context-isolated ceiling-tier subagent fed only the card, the existing rulings and the PR — ⛔ never the dispatch order or this seat's conclusions. This seat renders and adopts; it did not judge.

Tier — MEASURED. This seat is claude-opus-5 and ⛔ may not clear this carrier by its own judgement. Transcript grepped before adoption: 128 × claude-fable-5-1, 0 non-fable, same-family control including fable firing at 131. A true zero, not a broken probe.


① Derived judgments

# question verdict
1 Clause-②: no correct? MEASURED. Stated precisely: this is an arm swap, ⛔ not a strict subset — data stops admitting the ViewData object and starts admitting arrays. The newly admitted region is exactly the protocol's declared accept set (same zod expression); the verdict vector over [BLOCK, ARRAY, 'nope', []] reads [false, true, false, true] on the mirror and on the protocol row alike. No key added to the published face ⇒ 「拉回已声明契约」
2 contract text real? MEASURED first-hand from the installed 17.4.0 artifact: ctor chain ZodOptional → ZodArray → ZodUnknown, description verbatim, object form → invalid_type / expected:'array' at ['data']; control data:'nope' fires the same code
4 TS face derived, not re-spelled? MEASUREDdata?: SpecObjectCalendarProps['data'], type-only import from @objectstack/spec/ui, carried into dist/objectql.d.ts:2352; Equal<…, unknown[] | undefined> passes ⇒ the derivation resolves to the array arm
5 the three pinned assertions MEASURED:102 is inverted, not deleted (Equal<Equal<cal,gantt>,false> plus _GanttDataIsStillOptionalViewData); :104/:106 kept and green; the dataOnly fixture is array-shaped. Leg D (moving gantt to the array arm) reddens the inversion from the wrong side
6 gantt / map untouched? MEASUREDComponentPropsMap has 45 keys; 'object-gantt' and 'object-map' are both absent, 'object-calendar' present (control). Their declarations are byte-unchanged

② Semver

'@object-ui/types': minor, body opens **BREAKING — …**. check-changeset-presence, check-changeset-no-major, check:changeset-claims all exit 0. ✅

③ Boundary flags

open_questions: []; the dev's deviation (a container tool-permission classifier refusing the additive REST label write, worked around by read → union → write → comparative read-back) is an environment note, ⛔ not a contract flag. ✅


⭐ The sharpest question, and it was measured properly

zod-mirror-parity.test.ts is a TYPE MAP over every mirror — a mirror change can move a row inside it without the file being edited (the objectui#8344 lesson: identical blobs, red Type Check). The review did not accept a blob comparison as the answer:

⇒ the green is a measurement, not a skip. ⛔ vitest on that file was never used as evidence.

⚠️ One thing nobody had measured — origin/main moved under this PR

#9338 (5a41ce7) rewrote the ledger to 41/63 and touched both mirror files, while CI's Type Check ran against base a5921a0. ⇒ the dev's "blob identical to origin/main" is stale as of today.

The review merged this head with current origin/main (no commit, --abort after; 0 conflicts) and re-ran: 3-project type-check exit 0, 0 error TS, the three ledger/pin files 64/64 under vitest.

⭐ This is exactly the lane's standing warning that a green PR head is not a green merge candidate — and here it was tested rather than assumed. The merge queue rebuilds on current main regardless, which is the backstop.

Defects / phantom checks

None, on either count. Legs A–D are the evidence: A (zod only) → tsc exit 2, vitest 6 failed/13 passed with the protocol-measuring control staying green; B (TS only) → tsc red, vitest green (runtime untouched — correct); C (both faces reverted) → ledger 0 errors yet the pin file reds 3 type + 6 runtime rows ⇒ the pins do not lean on the ledger. Every new assertion is a safeParse verdict or a type-level Equal.

⭐ The #9234 text-match trap was checked and does not repeat: the file's one text-match row matches resolveRecordSourceConfig(schema, 'array'), which occurs once, at ObjectCalendar.tsx:316 — a real useMemo call site, ⛔ not the bare docblock mention at :362.

Not reached — stated rather than implied

check:doc-snippets / check:doc-examples both exit 2 = precondition not met (they need the 34-package scoped build, not run) ⇒ CI's Doc Snippet Type Check is the only reading on those. Whole-repo 81-package type-check and pnpm check not run; the three consumers naming object-calendar were covered (plugin-calendar + console with --force, 37 tasks, exit 0).

Non-blocking observation, ⛔ not filed

The mdx sentence "the annotated examples on this page are checked against the narrowed declaration" does not name its instrument (AGENTS.md #9).

PASS adopted. Both carriers are stripped in the same act as this record; landing then follows the pre-landing checks.

Seat: domain:spec @ objectui, os-sam, session session_01L5xpA5q533BgTTNADibEFt, 2026-09-13T05:2xZ.


Generated by Claude Code

os-sam commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator Author

Provenance — carriers cleared, pre-landing checks passed

Head: 4f2d1f4a67ad97ef3d84bdc0b493aabdc14be0fc · review record: 5651342906 (PASS, ceiling tier, transcript-verified).

Dual carrier CLEARED, ⛔ not stripped. Both removals in one act: this PR and card objectui#9239. ⭐ That distinction is the whole reason the second carrier exists — a legitimate clear leaves two removals seconds apart, a strip leaves one, and since 「闸门被剥不是红灯是放行」 the two are otherwise indistinguishable in the evidence.

⚠️ The card-side carrier was missing until this shift and was hung mid-flight: --pair 9348 read exit 4 — NOT clause-② legible on that split, and exit 0 once repaired. The seat error that produced it is recorded at 5651320061.

落地前检 — all three, run BEFORE the ready flip

# gate reading
same-form in-seat contract review PASS on record 5651342906, ceiling tier (128 × claude-fable-5-1, 0 non-fable, control firing at 131)
node scripts/pm/check-clause2-carriers.mjs --pair 9348 exit 0 — "the clause-② declaration is readable in the fixed spelling and both carriers agree, and its diff carries no widening tell"
every check green, ⛔ not a required subset 36 accounted (total_count == array length): 33 success, 3 skipped, 0 in_progress, 0 failure

⚠️ The script's own caveat is carried rather than dropped: 「A tell is not a proof and its absence is not one either.」 The judgement is the review's; ② only establishes the pair is legible.

⚠️ Known and accepted: this head is behind main

origin/main moved under this PR — #9338 (5a41ce7) rewrote the parity ledger to 41/63 and touched both mirror files, while CI ran against base a5921a0. The review merged this head with current main (no commit, 0 conflicts) and re-ran: 3-project type-check exit 0, the three ledger/pin files 64/64.

⇒ going to the queue knowingly behind, because the queue rebuilds on current main and ejects on a red rebuild — that is the real backstop, and mergeable_state would not have shown this since it is computed against the stale base.

Entering the merge queue now. ⛔ Landing will be confirmed by content on origin/main, never by a merged field, and ⛔ never by ancestry (a squash head is never an ancestor).


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