Skip to content

docs(core): state the ruled reference-day rule on FormulaFunctions TODAY() (objectui#10903) - #10915

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10903-today-reference-day-rule
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10903-today-reference-day-rule

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #10903
Clause-②: no

What this does

The @object-ui/core FormulaFunctions registration of TODAY() gains a docblock that states the rule ruled on the card (ruling comment 5864664470, option A). A client-evaluated TODAY() names the same reference day the server resolves for that user, on the compute-tz axis of objectstack ADR-0053 (Phase 2 items 5 and 6). While no reference timezone reaches the client, it names the UTC day.

There is no behaviour change. The implementation reads no timezone, so it answers the UTC day, which is the ruled fallback. The one other file is an empty-frontmatter changeset, because check-changeset-presence asked for one: it counts any file under a released package's src/.

The new docblock (on the TODAY registration inside registerDateFunctions)

`TODAY()` — the current calendar day, as a date-only `YYYY-MM-DD` string.

Which day that is, is ruled (objectui#10903, ruling A): a client-evaluated
`TODAY()` names the same reference day the server resolves for that
user, on the compute-tz axis of objectstack ADR-0053
(`docs/adr/0053-date-and-datetime-semantics.md`, Phase 2 items 5 and 6),
and the UTC day while no reference timezone reaches the client. The aim
is one "today" per execution context: a client preview and the server's
`today()` naming the same day.

This implementation reads no timezone, so it answers the UTC day
(`toISOString()`), which is the ruled fallback. It is deliberately not
the viewer's local day. Until the reference timezone reaches the client
(a later card), a server that resolves a reference timezone other than
UTC can name a different day for part of every day. Once the timezone
reaches the client, `TODAY()` names that timezone's calendar day instead.

No date and no mechanism is promised. The docblock cites the ADR by path and section, not by line.

Other sentences touched: none

No existing comment or doc says anything about TODAY()'s zone that the rule makes false, so no other sentence is edited. What was searched, case-sensitive TODAY over the whole tree outside changelogs:

  • FormulaFunctions.ts: the module docblock and the class docblock say nothing about zones, and NOW() and DATEADD have no docblock at all.
  • ExpressionEvaluator.ts and predicateInput.ts: their today() mentions are the lowercase CEL builtin on the @objectstack/formula path. They say where that builtin exists, not which day it names.
  • The flow simulator comment and its two i18n strings describe the server runtime's NOW() / TODAY() tokens, not core's.
  • skills/objectui/guides/schema-expressions.md lists TODAY() as "current date". That is not false under the rule, and skills/** is a governed surface, so it is left alone (see Acceptance notes).

Proof that no code changed

  • Comment-stripped emit. ts.transpileModule (TypeScript 6.0.3, removeComments: true, ES2022 / ESNext) over FormulaFunctions.ts at base 733fd5ac6 and at head gives the same bytes. Both are sha256 83e35ae792e548c790edc050fe5a35a51f4a9dc78c5227ff5fe9b84a7fc9b769, 10,703 bytes, and cmp exits 0.
  • Control. The same instrument on a scratch copy with one same-length code change (the TODAY split index [0] changed to [1]) gives sha256 d83ea446… at the same 10,703 bytes, and cmp exits 1. So the check reads content, not length.
  • Built output. After tsc, dist/evaluator/FormulaFunctions.d.ts has 0 hits for "reference day" and declares only private registerDateFunctions;. The emitted .js has 1 hit, which is the positive control. The docblock cannot reach a published type.

Verification (head 2e09f293e)

  • Closure build pnpm --workspace-concurrency=2 --filter '@object-ui/core...' run build (types, then core, with check-dist-completeness): VERDICT command-exit 0.
  • pnpm --filter @object-ui/core run type-check echoes tsc --noEmit && tsc -p tsconfig.test.json. Then pnpm exec vitest run packages/core/src/evaluator/ --maxWorkers=2 gives Test Files 16 passed (16) and Tests 425 passed (425), VERDICT command-exit 0.
  • scripts/__tests__/, all 179 files, run in two halves under the verify lock. The first half gives Test Files 90 passed (90), Tests 3534 passed (3534). The second gives Test Files 87 passed | 2 skipped (89), Tests 1788 passed | 2 skipped (1790). Both are VERDICT command-exit 0.
  • These gates exit 0: check-changeset-presence (1 source file of 1 released package changed, 1 changeset declared, empty frontmatter), changeset:check (fixed group, no major), check-changeset-overwrite, check:pending-changeset-literals, check:changeset-claims, check:control-bytes, check:new-line-citations (VERDICT new-cross-file-line-citations: 0 new citation(s)), and check-governed-queue-guard --test over both paths (NOT GOVERNED).
  • Narrowed ESLint, root config with --no-inline-config --format json, over the one changed code file (1 result): 0 errors and 26 warnings at head, the same 0 and 26 on the base blob via --stdin. The resolved config has no parserOptions.project or projectService, so linting is not type-aware and a comment cannot move any untouched file's verdict. The repo-wide lint belongs to CI.
  • NOT MEASURED: check:doc-examples, because its prerequisite is that every package is built. Per its header, it compiles fenced @example blocks on exported declarations only, and this docblock has no @example and sits on a statement inside a private method.

Acceptance notes


Generated by Claude Code

…DAY()

A client-evaluated TODAY() names the same reference day the server
resolves for that user (the compute-tz axis of objectstack ADR-0053,
Phase 2 items 5 and 6), and the UTC day while no reference timezone
reaches the client. The implementation reads no timezone, so it answers
the UTC day, which is the ruled fallback. JSDoc only; the
comment-stripped emit is byte-identical.

Claude-Session: https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk
Co-authored-by: Claude <noreply@anthropic.com>
Empty frontmatter, as check-changeset-presence asks: the change is a
comment inside a private method body, so no published behaviour moves.

Claude-Session: https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk
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) 3096.1 KB 3104.5 KB
Main entry chunk (gzip) 148.6 KB 350 KB
Entry file index-CXe0iCYr.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.58KB 6.17KB
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) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
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.70KB 2.23KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
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) 27.13KB 7.95KB
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) 557.59KB 133.60KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 226.44KB 63.00KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
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) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
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.33KB 3.07KB
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) 16.01KB 3.93KB
plugin-calendar (index.js) 51.96KB 14.83KB
plugin-charts (index.js) 83.99KB 22.86KB
plugin-chatbot (index.js) 197.67KB 46.90KB
plugin-dashboard (index.js) 136.93KB 36.48KB
plugin-designer (index.js) 215.03KB 44.21KB
plugin-detail (index.js) 233.48KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.00KB 9.09KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 88.55KB 22.21KB
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.81KB 3.58KB
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) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
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.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
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.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
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) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.27KB 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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 2e09f293e4149601d6b5f13a7a1c52d844904e49

Read: card objectui#10903 (body, intake 5864368308, ruling 5864664470 = A, claim 5865010442, dev report 5865234676); PR #10915 body, diff, file list, commits and the check-runs on its head; objectui at refs/review/pr-10915 (2 commits on 733fd5ac6, which is origin/main); objectstack docs/adr/0053-date-and-datetime-semantics.md, packages/runtime/src/security/resolve-execution-context.ts and its test, packages/core/src/security/resolve-authz-context.ts (resolveLocalizationContext), packages/core/src/security/assemble-execution-context.ts, packages/services/service-settings/src/manifests/localization.manifest.ts, packages/formula/src/stdlib.ts, cel-engine.ts, types.ts, packages/objectql/src/engine.ts. The dev report was read as a set of claims and each was re-measured here.

① Derived judgments

Diff: 2 files, +24 / -0. packages/core/src/evaluator/FormulaFunctions.ts gains 18 lines, all inside one /** ... */ block on the this.register('TODAY', ...) statement inside registerDateFunctions; .changeset/10903-today-reference-day-rule.md is new. git diff --check is clean.

Every docblock sentence, against the code and against ruling A:

  • "the current calendar day, as a date-only YYYY-MM-DD string" — true: the body is new Date().toISOString().split('T')[0].
  • "names the same reference day the server resolves for that user, on the compute-tz axis of objectstack ADR-0053 (..., Phase 2 items 5 and 6), and the UTC day while no reference timezone reaches the client" — the ruling's own wording. ADR-0053 Phase 2 items 5 and 6 are the reference timezone and the tz-aware today(); "Compute-tz" is the ADR's name for the today() row of its two-axis table in the Phase 2 detailed design. True.
  • "one 'today' per execution context: a client preview and the server's today() naming the same day" — restates the ruling's "One 'today' per execution context: a preview and the saved answer agree".
  • "reads no timezone, so it answers the UTC day (toISOString()), which is the ruled fallback" — true of the code (the only timezone hits under packages/core/src/evaluator/ are this docblock) and of the ruling's fallback.
  • "deliberately not the viewer's local day" — right under A: the ruling names B, the viewer's local day, as not taken.
  • "a server that resolves a reference timezone other than UTC can name a different day for part of every day" — reachable today, verified end to end in objectstack rather than taken from the report: resolveExecutionContext calls resolveLocalizationContext, whose uncached body reads localization.timezone through the settings service (the manifest declares scope: 'tenant' and says per-user overrides are intentionally out of scope for v1) or a scope: 'tenant' sys_setting fallback, then assembleExecutionContext sets timezone for non-anonymous requests; applyFormulaPlan in objectql/engine.ts passes execCtx?.timezone into ExpressionEngine.evaluate; cel-engine.ts builds its env with ctx.timezone ?? 'UTC', and registerStdLib defines today() as calendarDayUtc(now(), timezone). A tenant with a non-UTC localization.timezone already gets the shifted server day. The dev's "org-level localization.timezone only" is correct.
  • "Once the timezone reaches the client, TODAY() names that timezone's calendar day instead" — the ruling's follow-up. The docblock promises no date and no mechanism: "a later card" names neither.

No code change, measured here rather than adopted: ts.transpileModule (TypeScript 6.0.3 installed into the review scratchpad; removeComments: true, ES2022 / ESNext) over the file at 733fd5ac6 and at 2e09f293e gives identical bytes — sha256 83e35ae792e548c790edc050fe5a35a51f4a9dc78c5227ff5fe9b84a7fc9b769, 10,703 bytes, cmp exit 0 — so the dev's numbers reproduce. Control: the same instrument with the split index [0] changed to [1] (one anchor hit) gives d83ea446ab0a... at the same length and cmp exit 1. A second, instrument-free proof: each of the 18 added lines is a line of one block comment (/**, then lines opening with * and holding no */, then */), and no line is removed.

Other text about TODAY()'s zone: swept case-sensitive TODAY over the whole tree outside changelogs, and case-insensitive today() over *.md, *.mdx, docs, skills and apps. Hits that name the function: this file; FormulaFunctions.test.ts ("should return current date string", asserting only the YYYY-MM-DD shape); ExpressionEvaluator.ts and predicateInput.ts (the CEL today() exists on the @objectstack/formula path — a where, not a which-day); flow-simulator.ts, its two i18n.ts strings and .changeset/10615-simulator-cel-guards.md (the server runtime's NOW() / TODAY() tokens); skills/objectui/guides/schema-expressions.md ("current date" — not falsified, and skills/** is in GOVERNED_SURFACES); packages/plugin-form/README.md (a cel today() default the server evaluates). None states a zone the rule makes false; the dev missed nothing.

Clause-②: no is honest: the ruling sets it, and under objectstack AGENTS.md's clause yes means a change to what an author can write — a comment inside a private method body accepts or refuses nothing differently.

CI on this head, read after it settled: 43 check-runs, all completed — 39 success, 3 skipped by design (the two coverage rows and dependabot), 1 failure, Spec Main Shape Gate. Of the eight contexts the live main ruleset requires, seven are green and that one is red for an inherited, cross-repo reason that no line of this PR can move; ③ carries the attribution. This verdict is on the PR's content against the ruling; the landing waits on that gate.

② Semver level

None, declared by an empty-frontmatter changeset, and the entry was required: scripts/check-changeset-presence.mjs guards src/** of every package in the fixed group (isPublishedSource returns true for any src/ path), @object-ui/core is in the one fixed group and not in ignore, and the script's header says an empty frontmatter counts. Independent proof that the gate asked: on the superseded commit 97b175cb6, which lacks the changeset, Changeset Declaration concluded failure; on 2e09f293e it is success. The shape has precedent: 594 of the 2,264 entries in .changeset/ on origin/main open with an empty frontmatter.

Every sentence of the changeset is true: comment-only in @object-ui/core; the rule as ruled; the implementation answers the UTC day and nothing it returns changes; "the docblock sits inside the private registerDateFunctions body, so it cannot reach the emitted FormulaFunctions.d.ts" — a declaration-only emit of the head file here has zero hits for "reference day" and declares private registerDateFunctions; (a comment on a statement inside a method body is attached to no declaration, so no declaration emit can carry it; "private" describes the method rather than being the mechanism, and the sentence stays true); the comment-stripped emit is byte-identical (above); no published behaviour changes.

③ Boundary flags

  • ADR-0053 prose drift, confirmed for relay to docs(adr-0053): one addendum line — a client-evaluated formula's today() names the compute-tz reference day, UTC while no reference timezone reaches the client (ruling A on objectui#10903) objectstack#20394: Phase 2 item 5 ("optionally overridden by sys-user-preference.timezone") and the resolver precedence table ("User override | sys-user-preference (key='timezone')") describe a per-user layer that objectstack's runtime does not have. resolveLocalizationContextUncached reads localization.{timezone,locale,currency} from the settings service, where the localization manifest is scope: 'tenant', or from sys_setting with scope: 'tenant', and never sys_user_preference; the runtime test header says per-user overrides are intentionally out of scope (organization-level only) and pins that a sys_user_preference timezone row is ignored. The settings service does carry a user scope tier, but localization is not declared in it, so the org-only reading holds. Not this PR's to fix.
  • PR opened at 97b175cb6, the changeset commit pushed a minute later. CI on the final head 2e09f293e is complete and not green: 43 check-runs, 39 success, 3 skipped, 1 failure — Spec Main Shape Gate, which the live main ruleset requires and scripts/dependabot-merge-gate.mjs lists in REQUIRED_CONTEXTS. Its subject is not the PR's diff: it compiles objectui against @objectstack/spec built from objectstack's live main, unfiltered by paths on purpose. Its own annotation attributes the red: packages/types/src/__tests__/report-chart-query-spec-parity.test.ts line 747, TS1360: Type 'true' does not satisfy the expected type 'false', compiled against @objectstack/spec built from objectstack df3ba164a588. That file is byte-identical on origin/main and this head, and packages/types has no diff at all. The pin there (true satisfies IsUnknown of SpecJoinedReportBlock; its header says the pin "is what says when this is burnable") is written to fire when upstream types JoinedReportBlock, and objectstack 681868ca ("fix(spec): JoinedReportBlock, a ViewItem config and the overlay viewKind carry the shapes their doors accept", #20369) rewrites JoinedReportBlockSchema from z.ZodTypeAny to a typed schema in packages/spec/src/ui/report.zod.ts; df3ba164a588 is the main tip that contains it. The same gate was green on the merge-base 733fd5ac6 (run started 06:38Z) against the earlier main. So this is drift that landed between the two runs: objectui#3162's ledger pin is now burnable on objectui main, its own card, and this head needs a re-run (or a rebase onto the healed main) before the ruleset lets it land. Not a defect of this PR. The 97b175cb6 runs (2 failures — Changeset Declaration, expected without the changeset, and the aggregate Test — plus 20 cancelled) are superseded and judge nothing.
  • PR body, read in full: no sentence found false. Fixes #10903 is line 1 and Clause-②: no line 2; no angle brackets; no line addresses (scanned for file:N, LN, line N and :NN forms — zero). Its claims that check-changeset-presence counts any file under a released package's src/, that skills/** is governed, that the interim split is reachable today, and that the resolved ESLint config is not type-aware (no parserOptions.project or projectService in eslint.config.js; Lint green on this head) all check out. The one gate it lists as NOT MEASURED, check:doc-examples, runs in CI as the second blocking step of the Doc Snippet Type Check job, green on this head.
  • State: the PR is a draft; the labeler put package: core on it. Nothing else is owed under the ruling's first execution parameter — the ADR addendum and the client-timezone thread stay on their own cards, and the D1 arithmetic slice rides objectui#10866.
  • Nits, not defects: "for that user" is the ruling's phrase, and the runtime resolves per organization, not per user — the docblock states no precedence, so it is not false. "Phase 2 items 5 and 6" is where the rule lives; the "compute-tz" name is given in the ADR's detailed-design table, which those items feed.

Implemented-by: claude/issue-10903-today-reference-day-rule
Reviewed-by: session_01DuWo5bdP9SdVebamn99GGk

VERDICT: PASS


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3096.1 KB 3104.5 KB
Main entry chunk (gzip) 148.6 KB 350 KB
Entry file index-CXe0iCYr.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.58KB 6.17KB
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) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
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.70KB 2.23KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
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) 27.13KB 7.95KB
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) 557.59KB 133.60KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 226.44KB 63.00KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
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) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
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.33KB 3.07KB
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) 16.01KB 3.93KB
plugin-calendar (index.js) 51.96KB 14.83KB
plugin-charts (index.js) 83.99KB 22.86KB
plugin-chatbot (index.js) 197.67KB 46.90KB
plugin-dashboard (index.js) 136.93KB 36.48KB
plugin-designer (index.js) 215.03KB 44.21KB
plugin-detail (index.js) 233.48KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.00KB 9.09KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 88.55KB 22.21KB
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.81KB 3.58KB
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) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
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.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
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.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
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) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.27KB 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

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 28, 2026 09:09
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit 708ecf8 Sep 28, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10903-today-reference-day-rule branch September 28, 2026 09:46
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Oct 7, 2026
… family D input slice) (objectstack-ai#10907)

Part of objectstack-ai#9256
Clause-②: yes

**Clause-② `yes`, as the claim declared:** `input` accepted an authored
`children` on both published faces and rendered nothing, with no
render-time error or warning and no element; only the parser tier's
`not-a-container` warning (objectui#9910) noticed it. It now refuses it
by name. A published accept set narrows, so a contract review is owed
before landing.

This is the family-D slice that release `5864147075` on objectui#9256
lists, and nothing else: `input`, the family-D test header's superseded
remarks, and `PartialSchema`'s docblock claim about generic `Omit`
re-spellings. `br`, `hr` and `img` are not touched. objectui#9256 stays
open.

## What changed

- **`input` narrowed** in the family-D shape PRs objectui#9589 and
objectui#10897 used:
- TypeScript face (`InputSchema` in `packages/types/src/form.ts`):
`body?: never` and `children?: never`, each with a docblock saying what
the renderer reads and what it renders instead.
- zod mirror (`InputSchema` in `zod/form.zod.ts`): two
`retirementTombstone` members fed one string, `INPUT_NEITHER_CHANNEL`,
built by `neitherContentChannelGuidance`. Both stay MEMBERS, so
`zod-mirror-parity`'s key sets stay equal.
- `body` was already refused on both faces by `BaseSchema`
(objectui#6771). It is restated because that refusal names `children` as
the remedy, and `input` does not read `children` either.
- **`email` / `password` (`InputShorthandSchema`)**: no type or
accept-set change. See "The shorthand's own pair" below.
- **Texts (comments only):**
- The family-D test header: 「family E, frozen」 (the `InputSchema`
bullet), 「unattributable」 (the `DetailViewSchema` bullet) and the two
`ui:calendar` remarks that still said plain `Omit` (the header bullet on
the six bare-name hold-outs, and the comment above the slice-2 rows) now
say what is true. No assertion in that file moved.
- `PartialSchema`'s docblock in `packages/types/src/index.ts`: the
sentence reading every generic re-spelling as collapsing is marked
false, with `OmitDeclared` named as the counter-example.
- **Bounded in-place fix, declared:**
`partial-schema-collapse-pin.test.ts`'s header carried the same false
claim, and even named the key-remapping spelling as one that collapses.
It is corrected the same way, comment only. See "Texts" below for the
four conditions and the evidence.
- **New pin file**
`packages/types/src/__tests__/content-channel-input-9256.test.ts`.
- **One changeset** `.changeset/9256-input-content-channels.md`:
`@object-ui/types` `minor` with an explicit BREAKING note and a
migration line, the spelling PRs objectui#9589 and objectui#10897 used
under this repo's no-major rule.
- **Four pending changesets are corrected, prose only.**
- Round 2 added a dated note to `6397-partial-schema-collapse-pin.md`,
`9256-content-channel-family-d.md` and
`8499-node-slot-registered-arms.md`.
- Round 3 added a second note to the family-D entry and reworded the E3
entry's render claim in place.
- Each carries a sentence a later change in this same release made
false, or an overstatement, and each ships verbatim into the CHANGELOG.
Every frontmatter block is byte-identical. See "Round 2" and "Round 3"
below.

## Measurement, taken on `origin/main` `244d516df` before any edit

**Instrument.** The TypeScript compiler API (6.0.3), ⛔ not grep. It
builds one program per `tsconfig.json`: the 40 workspace packages,
`apps/console` and the three examples, 44 programs over 2015 non-test
source files. It ran on a BUILT tree (`turbo run build` 43/43, 0 cached)
with 0 unresolved-module diagnostics. It files every `.body` /
`.children` read (property access, string element access, object
destructuring) under the declared type of its receiver: 520 channel
reads were filed.

- **`InputSchema`: 0 `body` / 0 `children` reads.**
`InputShorthandSchema` is never a receiver: the `email` / `password`
wrappers are `any`-typed and hand the node to the `input` renderer,
which reads it as `InputSchema`.
- **Lit controls fire in the same run:** `ButtonSchema` 1, `DivSchema`
1, `CardSchema` 1, `ContainerSchema` 1 (each `children`).
- **Receiver reachability:** the same programs see `InputSchema` as the
receiver of 23 reads over 17 keys: `defaultValue`, `description`,
`error`, `id`, `inputType`, `label`, `max`, `maxLength`, `min`, `name`,
`pattern`, `placeholder`, `readOnly`, `required`, `step`, `value`,
`wrapperClass`. Neither channel is among them, so the zero is a reading,
not blindness.

**Every registration that claims `input`, bare and namespaced**
(`check:registry-bare-names --json`, 1657 files, 424 claims, 0
contested):

- `input`: sole claimant `ui:input`, a literal
`ComponentRegistry.register('input', InputRenderer, …)` in
`renderers/form/input.tsx`. The hop is typed (`InputRenderer` takes
`schema: InputSchema`), not `any`. No `field:input` or other claimant
exists; `ai:input` is a different key, a guarded protocol placeholder.
- `email` / `password`: `ui:email` / `ui:password` own the bare keys;
`field:email` / `field:password` stand down with `skipFallback: true`.

**Does any path pass the node's content through? No.**

- `SchemaRenderer` destructures `children` and `body` out of the props
bag before the spread.
- The only pass-through `InputRenderer` gives the native element is
`toFormControlDomProps(inputProps)`, a whitelist pick of
`SDUI_DOM_PASS_THROUGH_KEYS` plus `name` / `disabled`; `children` is not
on it.
- The `email` / `password` wrappers only spread `props` and
`props.schema` into `InputRenderer`.
- The registration declares no `children` slot input (objectui#9910).
- No in-source host builds an `input` node by spreading another node.
The literal `input` nodes in `form.tsx`, `cli`'s `init` scaffold and the
dashboard / designer config panels are assembled key by key, and none
carries a channel.
- **The 75 `any`-typed channel reads were attributed file by file.**
None can receive an `input` node's channel:
- app-shell nav / search trees, and HTTP request, error or email `body`
payloads;
- React `children` destructured off component props (`view:simple`'s
renderer, `react-page`'s wrapper), which `SchemaRenderer` never fills
from a node;
- the metadata-admin preview canvases, which read a page block's
`properties.children` or a page draft's root `children` to list and
append blocks for selection, not to render a node's channel;
- the page-container and sectioning registrations in `containers.tsx`,
`page.tsx` and `semantic.tsx`, and the html-elements factory, whose tag
list excludes `input` by name (its own comment lists `input` among the
tags it never registers);
- `core`'s schema builder (card / grid / flex builders; `InputBuilder`
has no children setter);
- `record:alert`'s own `props.body`, the runner's nav items, and the
vscode validator.
- **Generic traversers** (`validateChildren` in core, `sdui-parser`'s
parse / validate, `cli validate`) walk children to validate. They render
nothing and are node-agnostic, as for every family-D row already landed.

**Producers.** `pnpm census:body-dialect --keys
input,email,password,ui:input,ui:email,ui:password,div,card,page,button`
read 9016 files. The key population is printed by the tool itself, so
objectui#9545's blindness does not apply.

- Nodes on the six input keys: `input` 321, `email` 75, `password` 60,
`ui:email` 1, `ui:password` 17, `ui:input` 0. **0 author `body`, 0
author `children`.**
- The controls fire in the same pass: `children` on `div` 172, `card`
183, `page` 51, `button` 5.
- Real producers: 0. Nothing was migrated.

## The shorthand's own pair: type-redundant now, kept on purpose

`InputShorthandSchema` inherits `InputSchema` through `OmitDeclared` on
the TypeScript face and `.omit()` on the mirror, so it now inherits the
new pair. It already declared its own (PR objectui#10897).

- **Redundant as a type:** on the built d.ts both are the same `?:
never`, and the new `Eq` pin makes `tsc` hold that.
- **Not redundant as text.** The repo's rule for a restated tombstone is
the one `neitherContentChannelGuidance`'s docblock gives for restating
`body` over `BaseSchema`: restate when the inherited message misdirects
this node's author. The helper's contract spells the node and its route
into the message. The inherited string names `input` and the `ui:input`
route; the shorthand's names `email` / `password` and the wrapper route.
- **Decision:** keep both members and rewrite their docblocks (TS) and
comment (zod) to say they are type-redundant and why they stay. The
shorthand's old `body` docblock gave a reason that became false with
this change (that the inherited refusal names `children` as the remedy),
and it is replaced.
- **Pinned:** a CONTROL row asserts that an author of `email` /
`password` reads the message naming those two nodes and not the `input`
one, so deleting the restatement turns it red.
- Counter-precedents, noted: `UiCalendarSchema` restates nothing and
inherits `calendar`'s message, which names it. objectui#8072 deleted the
shorthand's `wrapperClass` restatement once `InputSchema` carried the
key; that was a live key with one description, not a refusal message
naming a node.

## The `OmitDeclared` propagation, measured on the BUILT d.ts

A compiler-API probe over `packages/types/dist/form.d.ts` reads the
heritage of `InputShorthandSchema` on its own: the base type the checker
gives for `OmitDeclared` of `InputSchema` minus `type` / `inputType`,
before the interface's own members.

| built d.ts | `InputSchema.children` | heritage `.children` | shorthand
face `.children` |
|---|---|---|---|
| base `244d516df` | `SchemaNode \| SchemaNode[]` | `SchemaNode \|
SchemaNode[]` | `undefined` (its own restated tombstone) |
| this branch | `undefined` | `undefined` | `undefined` |

A consumer-side probe resolves `@object-ui/types` the way
`packages/components` does (`--traceResolution`:
`packages/types/dist/index.d.ts`). There, `input` + `children`, `input`
+ `body` and `email` + `children` each answer their `@ts-expect-error`,
and a lit control (`const n: number = 'x'`) fires.

## Red on base, then green — one-shot, the restore proven by state

The predictions were written to a file BEFORE the mutation. The mutation
was `git checkout 244d516` of the two declaration files (`form.ts`,
`zod/form.zod.ts`), with every test at HEAD. It was proven by both blob
hashes equal to BASE and by anchor counts: `INPUT_NEITHER_CHANNEL` 0,
the new TS docblock marker 0. The run sat under `trap … EXIT INT TERM`
with absolute paths.

| reader | mutated (BASE declarations) | restored (HEAD `72dba894a`) |
|---|---|---|
| vitest, `content-channel-input-9256.test.ts` | **RED** exit 1: 6
failed / 13 passed | **GREEN** 19 / 19 |
| `tsc -p packages/types/tsconfig.test.json` | **RED** exit 2: TS2578 on
the `inputChildren` pin, TS2322 on the `Eq` pin for `children`; 0
elsewhere (`zod-mirror-parity` included) | **GREEN** exit 0 |
| built d.ts probe (types rebuilt; dist markers 0 then 2) |
`InputSchema` and heritage `children` accepted | refused |

- **The 6 red rows:** `input.children` refused at its path, its message,
`.describe()` equality, every-value, and the root + nested
`AnyComponentSchema` row; plus the `input.body` message row. The base
message is `BaseSchema`'s `Did you mean` pointer.
- **Green on base, by design:** `input.body` refused at its path,
every-value and `AnyComponentSchema` (`BaseSchema` already refused
`body`), and the shorthand message CONTROL (its own pair is unchanged).
- **One prediction was recorded as uncertain and read, not assumed:**
`input.body` `.describe()` equality is GREEN on base, because
`BaseSchema`'s alias refusal also describes with its own message.
- ⚠️ `?: never` is erased before vitest runs: the TypeScript half is
read by `tsc` alone. `tsc --listFiles` counts the new pin file 1 under
`tsconfig.test.json` and 0 under `tsconfig.json`, so it is a real
assertion under the `type-check` script.
- **Restore:** proven by both blob hashes equal to HEAD, `git diff HEAD`
empty and `git status` clean.

## Texts

- **Family-D test header.** The `InputSchema` bullet now records the
plain-`Omit` hold-out ground in the past tense, the E3 slice's
`OmitDeclared` repair, and this slice's narrowing, pinned in the new
file. The `DetailViewSchema` bullet records that the E3 slice attributed
the `any` hop directly and narrowed it there. The two `ui:calendar`
remarks name `OmitDeclared` for the TS face, `.extend()` for the mirror,
and say which file pins which half. The third `ui:calendar` remark (the
old TRIPWIRE note) was already in the past tense and is unchanged.
- **`PartialSchema`'s docblock.** The triage's reading is kept as the
triage's. The re-spelling claim is marked FALSE, with `OmitDeclared` as
the counter-example. The docblock now says that re-spelling the alias
would narrow a published type, a contract change not made in a comment
correction, and that objectui#6397 is closed. The alias itself is
unchanged.
- **Bounded in-place fix: `partial-schema-collapse-pin.test.ts` header**
(comment only; its assertions are about the alias as written and do not
move). All four conditions hold:
1. It is the same defect as the carried item: the same sentence, one hop
away. The corrected docblock points readers at this file.
2. The fix is mechanical, and its shape is pinned by the carried
correction.
  3. No open PR touches the file.
4. Same gate family: types `tsc` and vitest, no new verification
surface.

**Evidence, a one-shot probe against the built d.ts (not shipped):** the
exact spelling the header named as collapsing, `{ type } & { [K in keyof
T as K extends 'type' ? never : K]?: T[K] }`, declares 62 properties at
`ObjectGridSchema` and 28 at `ButtonSchema`, where the shipped
`PartialSchema` declares 1 at each. It refuses `label: 42` on
`ButtonSchema` and `objectName: 42` on `ObjectGridSchema` (both
`@ts-expect-error` used; tsc exit 0).

## Gates at HEAD `72dba894a` (round 1; round 2's are above) (exit codes
captured by redirect-then-capture; heavy runs through the shared verify
lock)

| gate | result |
|---|---|
| `@object-ui/types` build (tsc + vite + dist completeness) | exit 0,
134 emitted files verified |
| `@object-ui/types` `type-check` (`tsc --noEmit` + examples +
`tsconfig.test.json`) | exit 0 |
| `pnpm exec vitest run packages/types/` (parity, the family-D / E3 /
input pins, the 8072 and 8762 shorthand pins) | 265 files / 5873 tests,
exit 0 |
| `pnpm exec vitest run scripts/` (every `scripts/__tests__` gate suite,
a superset of those whose corpus holds a touched file) | 177 passed + 2
skipped of 179 files / 5322 tests, exit 0 |
| downstream consumer type-check: the packages naming `InputSchema`
(`components`, `core`) plus those authoring `input` nodes or configs
(`cli`, `plugin-dashboard`, `plugin-designer`) and `fields`, `react`,
`types`, against the rebuilt `dist` | 8 × `type-check: Done`, exit 0 |
| `@object-ui/types` lint (`eslint .`, JSON) | 338 files, 0 errors; the
9 `form.ts` warnings sit outside every added hunk |
| `check:handler-key-reads` | exit 0; 128 arms, 50 / 50 reads judged, 0
unjudged |
| `check:control-bytes` · `check:new-line-citations` (0 new) ·
`changeset:check` · `check-changeset-presence` ·
`check:changeset-claims` · `check:pending-changeset-literals` | exit 0
each |
| `check:spec-symbols` · `check:component-surface-parity` ·
`check:readme-exports` · `check:registry-bare-names` ·
`check:prompt-keys` · `check:doc-types` · `check:test-path-roots` ·
`check:esm-specifiers` · `check:vi-mock-*` · `type-check:coverage` |
exit 0 each |
| `check:doc-snippets` (678 / 678) · `check:doc-examples` ·
`check:skill-examples` · `check:doc-fences` · `check:doc-example-ids` ·
`check:sdui-registration-pins` · `pnpm check` (CLI self-check) | exit 0
each |
| governed guard `--test` over the 7 paths | NOT GOVERNED (lit control
`AGENTS.md`: exit 3) |

- **The gate list was derived by hand.** objectstack's
`dispatch-gates.mjs` refuses an objectui answer by design (exit 2), so
the list comes from objectui's `package.json` and `.github/workflows/`.
- `check:changeset-claims` is report-only. It names 18 pending
changesets that mention a touched file; each was read, and none is
falsified as a record of its own change. The two that carry a claim this
slice bears on are in the Acceptance notes.
- **NOT MEASURED, left to CI:** the 8-way `pnpm test` shards beyond
`packages/types` and `scripts/`, `test:dist`, and E2E.
`check:sdui-registration-pins` read the console built at base; this diff
touches no registration and no `sideEffects` array.

## Round 2: four prose edits, head `6eb3397b4`

These come from contract review `5865087439` ③. The seat took them into
this PR because each ships verbatim into the CHANGELOG. They are prose
only: no code, no test logic, and every frontmatter block is
byte-identical (the `name: bump` lines at base and head compare equal).

1. **`.changeset/9256-input-content-channels.md`, and the Clause-②
sentence above.**
- Old: "an authored child list on it rendered nothing: no error, no
warning, no element."
- New: "an authored child list on it rendered nothing, with no
render-time error or warning and no element; only the parser tier's
`not-a-container` warning (objectui#9910) noticed it."
- Measured with a one-shot probe, not committed. It built the manifest
from the live registry the way `container-declaration-ratchet.test.tsx`
does, then ran `validateTree` and `compile`.
- `input`, `ui:input`, `email` and `password` carrying `children`: each
draws exactly `not-a-container`.
     - The same four without `children`: none.
     - `div` carrying `children` (control): none.
- The JSX `input` tag with a child: `not-a-container`, severity
`warning`.
2. **`.changeset/6397-partial-schema-collapse-pin.md`.** A dated note
follows the "Why a pin and not a repair or a retirement" paragraph: a
generic re-spelling of this `Omit` does not collapse, objectui#9256.
- Spelling: `OmitDeclared` is spelled exactly as quoted, in
`packages/types/src/form.ts`.
- Counts: the re-spelled alias declares 28 properties at `ButtonSchema`
and 62 at `ObjectGridSchema` on this head and on `main` `733fd5ac6`. The
shipped alias declares 1 at each.
- "Not true when written": at that entry's own commit (`c8ea8af9c`),
under the same TypeScript 6.0.3 its lockfile pins, the same spelling
declares 27 and 61. Those are the source counts in that entry's own
table. Both re-spellings refuse `label: 42` (the `@ts-expect-error` is
used), and a lit control on the shipped alias admits it.
- "Later in this same release": the E3 entry and the 6397 entry are both
pending on `main` (`git cat-file -e` exit 0 for each; control on an
absent path: exit 128).
3. **`.changeset/9256-content-channel-family-d.md`.** A dated note
follows the AMENDED paragraph, in that file's own convention, naming the
hold-outs this card later narrowed in this release.
   - Six of the nine names, in `nine-holdouts-six-narrow.md`.
- `list` and `timeline`, in `9256-list-timeline-content-channels.md`.
This is one more than the reviewer's draft, measured.
   - `DetailViewSchema` and the shorthand faces, in the E3 entry.
   - `InputSchema`, in this PR.
- A compiler-API probe over the source reads `body` / `children` as
`undefined` with their own `?: never` members on `TextSchema`,
`ImageSchema`, `IconSchema`, `TabsSchema`, `AccordionSchema`,
`CalendarSchema`, `ListSchema`, `TimelineSchema`, `DetailViewSchema`,
`InputShorthandSchema` and `InputSchema`. The zod faces are pinned in
the family-D, E3 and input pin files.
- `ButtonSchema` and `AppComponentSchema` still resolve `children` to
`SchemaNode | SchemaNode[]` (`ButtonSchema`'s zod mirror declares its
own `children` union; `AppComponentSchema`'s extends `BaseSchema`, which
declares one). That is the note's "still accept `children`" sentence.
4. **`.changeset/8499-node-slot-registered-arms.md`.** "`inputType` is
deliberately NOT declared on this arm" is false on `main`.
- `form.zod.ts` declares `inputType:
retirementTombstone(SHORTHAND_INPUT_TYPE_REFUSAL)` on
`InputShorthandSchema`, and `form.ts` declares `inputType?: never`. Both
files are identical on `main` and at the base.
- A runtime probe: `inputType` is a member of the mirror shape. `{ type:
'password', inputType: 'text' }` is refused with `invalid_type` at path
`inputType`, the message opening with the objectui#8762 guidance. Bare
`{ type: 'password' }` (control) parses.
- objectui#8762's entry landed a day after 8499's (2026-09-10 against
2026-09-09), and both are pending on `main`.
- A dated note in the file's own form follows the `InputShorthandSchema`
bullet, the same place its objectui#10756 note sits under the
`HtmlElementSchema` bullet.

| gate at `6eb3397b4` | result |
|---|---|
| `check-changeset-overwrite` | exit 0 (report-only), "1 changeset(s)
added, 3 modified, 0 deleted". Each of the three modified entries prints
the same declaration at base and now (`@object-ui/types: patch`,
`minor`, `minor`), so no declaration is lost: its case 2, a correction
on purpose. |
| `changeset:check` · `check-changeset-presence` ·
`check:changeset-claims` · `check:pending-changeset-literals` ·
`check:control-bytes` · `check:new-line-citations` | exit 0 each |
| the 24 `scripts/__tests__` suites that name `.changeset` (the
changeset gates' own suites, the polarity census, control bytes, pending
literals, docs and workflow suites) | 24 files / 913 tests, exit 0 |

## Round 3: two more prose edits, head `876f6d7ea`

These come from delta review `5865466392` ③ flags 2 and 3. They are
prose only, and both frontmatter blocks are byte-identical.

1. **`.changeset/9256-content-channel-family-d.md`: a second dated note,
directly after the AMENDED paragraph** and before round 2's note. The
headline is "the two twins no longer take `body` — objectui#6771".
- Old, still in the AMENDED paragraph: "The two twins are unchanged and
go on inheriting `body` as the content slot".
- New note: objectui#6771 retired `body` on `BaseSchema` itself, so both
twins refuse `body` on both faces. The TypeScript face does it through
`BaseSchema`'s `body?: never`; each mirror does it by name, pointing at
`requestBody`.
- Measured, TypeScript: a compiler-API probe over the source gives
`BaseSchema` its own `body?: never`. `ChatbotEnhancedSchema` and
`ChatbotFloatingSchema` resolve `body` to `undefined` and declare only
`children?: never` themselves.
- Measured, zod, with a one-shot runtime probe (deleted): each twin's
mirror refuses `body` with `invalid_type` at path `body`, and the
message names `requestBody`. `requestBody` parses (control).
- Provenance, from `git log -S`: `BaseSchema`'s `body?: never` and both
twins' mirror tombstones were introduced by `2acd8e109` (objectui#6771,
2026-09-19). The AMENDED paragraph was written on 2026-09-17
(`c42554e94`), and `6771-retire-body-child-list-dialect.md` is pending
on `main`. So "later in this same release" holds.
- The reviewer's draft also cited objectui#9659. That card did not
introduce the twin refusals, so the note cites objectui#6771 alone.
- Round 2's note opens "most of the hold-outs above have since been
narrowed". It is still true beside the new one: it names what this card
narrowed, and it ends with `button` and `AppComponentSchema`, which
still accept `children`.
2. **`.changeset/9256-e3-residual-content-channels.md`, in place.**
- Old: "an authored child list on them rendered nothing: no error, no
warning, no element."
- New: "an authored child list on them rendered nothing, with no
render-time error or warning and no element; only the parser tier's
`not-a-container` warning (objectui#9910) noticed it."
- Measured for all twelve keys, not just the two in round 2. A one-shot
runtime probe (deleted) loaded every plugin registration (`plugin-grid`,
`-form`, `-kanban`, `-map`, `-tree`, `-view`, `-gantt`, `-calendar`,
`-charts`, `-detail`, and `components`). It built the manifest from the
live registry and ran `validateTree`.
- Each of the twelve is known to the registry, and none declares a
`children` input.
- Carrying `children`, each draws `not-a-container` (severity
`warning`). Without it, none does. `div` carrying `children` (control)
draws nothing.
- `object-chart`, `email` and `password` also draw one `unknown-prop` in
both legs, from the probe's own `objectName` fixture key. It is not part
of the difference.

| gate at `876f6d7ea` | result |
|---|---|
| `check-changeset-overwrite` | exit 0 (report-only), "1 changeset(s)
added, 4 modified, 0 deleted". Each modified entry prints the same
declaration at base and now, so no declaration is lost. |
| `changeset:check` · `check-changeset-presence` ·
`check:changeset-claims` · `check:pending-changeset-literals` ·
`check:control-bytes` · `check:new-line-citations` | exit 0 each |
| the 24 `scripts/__tests__` suites that name `.changeset` | 24 files /
913 tests, exit 0 |

## Serial constraints

- **Re-mapped at branch time (`244d516df`), before each round's push,
and again before round 3's edits.** At round 3 the open PRs are
objectui#10921, objectstack-ai#10915, objectstack-ai#10914, objectstack-ai#10912, objectstack-ai#10911, objectstack-ai#10910, objectstack-ai#10908, objectstack-ai#10906,
objectstack-ai#10901, objectstack-ai#10777 and objectstack-ai#10278. objectstack-ai#10904, objectstack-ai#10902 and objectstack-ai#10891 have merged since
the first map.
  - None touches any of the five changesets this PR adds or edits.
- One touches a source path here: PR objectui#10914 edits
`packages/types/src/zod/form.zod.ts`, in two comment / `.describe()`
hunks on the `wrapperClass` key (in `InputSchema` and in the
`InputShorthandSchema` restatement note). Neither overlaps or touches
this PR's hunks, and `git merge-tree` of this head (`876f6d7ea`) against
objectstack-ai#10914's head is clean.
- PR objectui#10908 edits `zod-mirror-parity.test.ts` and
`zod/index.zod.ts`, neither of which this PR touches.
- **The release PR objectui#5400** (1732 files, enumerated to the end,
last updated 2026-09-18) touches none of the source paths. As release
consumption it deletes pending changesets, three of the four edited here
among them (not the E3 entry); that is not a hunk overlap.
- **`origin/main` moved four commits since the base** (`9f0c84a44`,
`7ea8118f7`, `8522396c0`, `733fd5ac6`). None touches `packages/types` or
the changesets edited here, and `git merge-tree` of this head
(`876f6d7ea`) against `733fd5ac6` is clean. `main` has not moved since.

## Acceptance notes: out of scope, not fixed here

- **The `PartialSchema` docblock and its pin file still sequence the
alias's repair on objectui#5155.** That card is closed as a duplicate of
objectui#5250, whose ruling keeps the tolerant face on renderer props.
objectui#8347 (open) is the card on removing the index signature from
the authoring face. "Once objectui#5155 removes the root index
signature" therefore names a card that will not do it. Noted, not
edited: rewriting the sequencing is a judgement about objectui#5250 /
objectstack-ai#8347, not a comment correction. Carrier: none.
- **The same "no error, no warning, no element" sentence lives in
published refusal messages and docblocks,** not only in changesets.
Examples: the family-D `retirementTombstone` strings on the zod mirrors
(the chatbot twins' included) and the matching TypeScript docblocks.
Wherever the node's registration declares no `children` input, the
parser tier's `not-a-container` warning did fire. Correcting those
strings is a published-message change, not prose, and it is not this
PR's. Noted, not edited. Carrier: none.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Decision] Which calendar day does a client-evaluated TODAY() name: the server's reference timezone, the viewer's, or UTC?

2 participants