Skip to content

docs(citations): re-point the 30 dead objectstack#N citations and the bare apiMethods-card number (objectui#10803, batch 7) - #10962

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-10803-dead-citations-seventh-batch
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-10803-dead-citations-seventh-batch

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Part of #10803
Clause-②: no

Dispatched implementation of the domain:ui seat #1 claim (comment 5867587761) on objectui#10803, batch 7, session https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk. Citations only: no sentence's claim moves, and every edited pending changeset's frontmatter is byte-identical. The only runtime text that moves is two console warnings, one in @object-ui/app-shell and one in @object-ui/plugin-detail, which lose their dead pointer and nothing else (amendment 5860244997, Q1 = A; patch changeset). No test pins any changed text: the literal-anchor sweep below finds no specific anchor, so no test file is edited.

This batch carries release 5866922219's two lists:

  • the 30 objectstack#N citations that answer 404, in the card's two classes (pending changeset prose and non-test packages/*/src);
  • the 28 bare #3391 lines in 10 files that mean objectstack's apiMethods whitelist card, which batch 6 fixed at 2 other sites.

Why Part of, not a closing line

Both lists read 0 after this batch (Census). The brief's rule was a closing line if the card's lists all read 0. They do, but reading the sentences found 10 more lines in the same two classes that cite a dead objectstack number written bare, three numbers in all (Acceptance notes 1). Triage item 3 puts a dead number found later in these classes on this card, so the card is not finished. Whether it carries them as a batch 8 or closes is the seat's call.

Premise, re-measured on origin/main 3b469c8ea (the branch point)

Census (the enumeration pin for this batch)

The 30 numbers (REF = a commit or tree):

git grep -nE 'objectstack#(5970|5976|6038|6124|6281|6331|6450|6483|6515|9933|9934|10354|10485|10695|11330|11507|11513|11658|11703|11753|11846|12009|12868|13117|13670|16126|17147|17762|17987|18012)([^0-9]|$)' REF -- '.changeset/*.md' 'packages/*/src/**' ':!**/__tests__/**' ':!**/*.test.*' ':!**/*.spec.*' ':!**/test/**' ':!**/tests/**' | wc -l

The bare #3391, with the same pathspec:

git grep -nE '(^|[^0-9A-Za-z_#/])#3391([^0-9]|$)' REF -- '.changeset/*.md' 'packages/*/src/**' ':!**/__tests__/**' ':!**/*.test.*' ':!**/*.spec.*' ':!**/test/**' ':!**/tests/**' | wc -l
REF the 30 bare #3391
3b469c8ea (branch point) 82: 26 changeset lines in 24 files, 56 src lines in 39 files 28 in 10 files
c292a6400 (this head) 0 0
this head merged with main 5c94589f0 (git merge-tree --write-tree, clean, tree bba27eece) 0 0
5c94589f0 (main alone) 82 28
  • Lit controls on the same instruments at this head: live objectstack#10856 reads 4 lines (4 at the branch point); objectstack#3391 reads 32 lines, against 3 at the branch point (28 re-qualified here, and this PR's sweep changeset names it once).
  • Out of scope, as it stands (whole tree at this head, unfiltered):
    • the 30 numbers: 84 test lines in 47 files, 1 scripts line, 2 lines in 2 apps/console files, 2 package READMEs (auth, react), 11 lines of published CHANGELOG.md history in 8 files; 0 .github, 0 governed, 0 content/docs;
    • bare #3391: 12 test lines in 9 files and 23 CHANGELOG.md lines in 8 files.

Citation form

Mapping, the 30 numbers

Lines / files are the branch-point census for that number (a line naming two of them counts under both).

dead number resolution what that commit carries lines / files
#5970 objectstack 97e7e3caa "unify ActionSchema.visible/disabled on one condition shape (#6450)", body "(#5970)": visible gains the boolean arm 2 / 1
#6450 (PR) the same 97e7e3caa, its squash as above; the card / PR pair collapses 2 / 1
#5976 (PR) objectstack 795b6e1aa, its squash "5 值子集改名 HttpMethodSubset" 1 / 1
#6038 objectstack 7618ee814 "key a container's default list _views name by the runtime identity": leg 2 of 3 of the objectstack#5164 ruling, the packages/lint half 1 / 1
#6124 (PR) objectstack b3c1f3cd5, its squash "key _views translations by the runtime view identity"; "The extractor now ASKS the composer for the key" 1 / 1
#6281 (PR) objectstack 85ec26d28, its squash, 2026-08-07 "SDUI props — enforce or remove (#5775) (#6281)": the shared PageContainerProps, whose single key is children, for page:section / page:footer / page:sidebar, which were EmptyProps 1 / 1
#6331 objectui 7a197e7c5 this repository's "SchemaForm reads the canonical visibleWhen, reviving every metadata-form predicate (objectstack#6331)" 1 / 1
#6483 objectstack ee58392e1 "ADR-0005 白名单强制 … (#6483)"; its diff carries the sentence the comment quotes, "Runtime-created sets … ride allowRuntimeCreate (still true) and keep working" 1 / 1
#6515 (PR) objectstack 2fdb36eb9, its squash "SpecifierSchema gains a closed valueDomain enum": "bcp47_locale is deliberately not in the vocabulary", because localization.locale's options ARE the shipped catalogs 1 / 1
#9933 objectstack d5552ca13 "admit columnState as an explicitly runtime-only view-overlay key" (subject ending "(#9996)"; "(#9933)" is on the message's first body line), on the overlay faces including viewItemWireFields 3 / 3
#9934 objectstack 79c46da90 "producer-side user-facing marking for hook refusal messages — userMessage channel (#9934)": ApiErrorSchema.userMessage, the contract half of the objectui#5210 split 10 / 9
#10354 (PR) objectstack 9e04c3e35, its squash "let the publish door state the package it is promoting"; its changeset and code comment carry the key-presence / no_draft warning ResourceEditPage.tsx points at 4 / 3
#10485 objectstack 35ad101bc "retire the themes carrier key and ThemeSchema (#10485, ADR-0049) (#10695)": "Ruled B (退役授权面, 2026-08-21)", "delete ui/theme.zod.ts whole" 15 / 13
#10695 (PR) the same 35ad101bc, its squash as above; the card / PR pair collapses 6 / 6
#11330 objectstack a9ee98992 "manifest.runtime trust-tier text states publish-gate-only enforcement truthfully", the trust-tier half (Special cases 2) 1 / 1
#11507 objectstack 88b9d749a "declare sys_activity.type as an open, author-extensible vocabulary": "Maintainer ruling 2026-08-24, direction 4" 13 / 9
#11513 objectstack e170b0ae5 "lock package-declared permission sets at the save door; clone to customize", quoting the 2026-08-24 ruling 「同意 第一步(创业阶段,Salesforce 式)」 3 / 3
#11658 objectstack 1a6a19c31 "open RecordActivityProps.types to author-contributed activity kinds"; its message names #11658 as the card it settles, and it executes the 2026-08-24 ruling 1 / 1
#11703 objectstack 5cb62d88b "make clone_permission_set carry all five copied facets"; its message names #11703 as the card it settles: the silent-grant-loss shape 1 / 1
#11753 "the 2026-08-25 ruling whose spec half is objectstack 0e4e51b0a" ActionParamSchema.carryOver, whose changeset reads "(#11753 ruling, spec half; #11992)" and "The maintainer's 2026-08-25 ruling on #11753" 2 / 2
#11846 objectstack 0c2334f6c; dropped beside PR objectstack#12718 "retire preview mode — the RuntimeMode 'preview' value and the whole PreviewModeConfig block (#12718)" 3 / 3
#12009 dropped beside objectstack 89448a52b the card of the AUTH_SSO_PROVIDER_SCHEMA removal, whose landing the line already cites (Special cases 3) 1 / 1
#12868 dropped beside objectstack c459da6bc the line already cites the commit that executed the ruling; objectstack's own f415bcf18 anchors #12868 to the same c459da6bc 1 / 1
#13117 (PR) objectstack 225e7690f, its squash "Readiness read for the Phase-2 members … global:search and global:notifications both have shipped platform data sources, so per the ruling both STAY declared" 1 / 1
#13670 "maintainer ruling 2026-08-31, option 2, recorded in objectstack 8c6a7fc0b" "The #13670 ruling settled the question: text's intended evaluation channel is content alone" 1 / 1
#16126 dropped beside PR objectstack#16920 PR objectstack#16920 (200) names #16126 in its body as the card it settles; merged 2026-09-08 as 859ded3ec 2 / 2
#17147 objectstack aaacf1d5c "the install-time granted permission set is REGISTERED at load and refuses nothing — say so, and pin the measurement (#17147)", the measurement on 9bd4344e4 2 / 2
#17762 objectstack 4342c9923 "guard three data lookups against Object.prototype fall-through"; its message names #17762 as a card it settles, classifyFilterToken among the three lookups 1 / 1
#17987 objectstack e233db9db "declare element-level navigation on object-kanban / object-calendar …"; its message names #17987 as the card it settles, and its Downstream note: objectui#8652 waits on it, unlock criterion a released, installable @objectstack/spec (Special cases 1) 2 / 2
#18012 objectstack 176b03582 "$between requires two non-blank endpoints (#18012)": "Ruling executed: decision batch #146 item 5, letter A" 3 / 3

The 28 #3391 lines, all now objectstack#3391: ObjectDataPage.tsx (3) and ObjectView.tsx (2) in app-shell; managedBy.ts (5); MePermissionsProvider.tsx (2), PermissionContext.ts, PermissionProvider.tsx; fieldWriteGate.ts; ImportWizard.tsx (6), ObjectGrid.tsx (4); ListView.tsx (3). Each was read: every one names the server's effective API operation set, /me/permissions apiOperations, or the 405 import refusal.

Special cases (the judgement calls)

  1. #17987, two sentences.
    • ObjectTree.tsx: "blocked on objectstack#17987, whose unlock criterion is a released @objectstack/spec carrying the declaration being installable here" becomes "blocked on objectstack e233db9db, whose unlock criterion …". That commit's Downstream note states the same criterion.
    • ObjectCalendar.tsx: "that card is pm:blocked on objectstack#17987" becomes "that card waits on objectstack e233db9db". The label word is not kept, because objectui#8652's label reads pm:on-hold today (measured); "waits on" is the phrase e233db9db's own note uses for that card.
  2. #11330. "it is objectstack#11330's half of the same panel" becomes "it is the trust-tier half of the same panel, which objectstack a9ee98992 settled separately". aaacf1d5c's message calls #11330 "the sibling half of this very sentence", ruled the same way on 2026-08-30, and a9ee98992 (2026-08-30) is that half's landing.
  3. #12009 collapses into the sha beside it. objectui#6910's body and ruling comment 5534414562 name "objectstack#12009 / PR #13413" together as the one AUTH_SSO_PROVIDER_SCHEMA precedent, a card and its pull request. Batch 6 replaced PR #13413 with its squash 89448a52b, so the card goes the way of batch 3's record:alert silently ignores the declared visibleWhen gate that its sibling blocks honour — the same defect already closed on the record-header face #5401 / Bind record into node-level visibleWhen, and stop visible swallowing it #5505 pair.
  4. #11753, two sites. The card carried the ruling, and 0e4e51b0a is its spec half. Both sites keep "ruling" as the antecedent that ActionParamDialog.tsx's next paragraph ("The ruling's point …") reads.
  5. finding(plugin-grid): a fixed-currency field (currencyConfig.defaultCurrency) shows the tenant's currency on the grid cell and the summary footer, because the column cell bag and columnHints drop currencyConfig #10354 in ResourceEditPage.tsx. "since objectstack#10354 doPublish states" gains a comma, "since objectstack 9e04c3e35, doPublish states", so two adjacent code spans do not read as one.
  6. #11507 in the 8137 changeset. "objectstack#11658 executing the maintainer's 2026-08-24 ruling on objectstack#11507" becomes "objectstack 1a6a19c31 executing the maintainer's 2026-08-24 ruling": the executing commit is named, and the ruling is cited by its date.
  7. Line breaks moved where the stand-in is longer or shorter: ActionRunner.ts (two sites), ActionParamDialog.tsx, theme.ts, theme.zod.ts (two sites), index.zod.ts and the metadata-admin i18n.ts comment, where "ruling on" became "ruling of 2026-08-24,".
  8. The runtime strings. Only the listed text moves.
file member before after
app-shell/src/layout/activityItemType.ts the console.warn in warnUnmappedActivityType "… sys_activity.type is author-extensible (objectstack#11507, ruled 2026-08-24) and is not validated on write …" "… sys_activity.type is author-extensible (ruled 2026-08-24) and is not validated on write …"
plugin-detail/src/renderers/recordActivityFeed.ts the warnOnce message in warnUnknownActivityType "… sys_activity.type is author-extensible (objectstack#11507, ruled 2026-08-24) and is not validated on write …" "… sys_activity.type is author-extensible (ruled 2026-08-24) and is not validated on write …"

No test, doc or changeset quotes either message with the pointer: the census reads 0 in .changeset/, and the anchor sweep finds no test literal that drops.
9. #3391/#3546. On the two lines that pair them (managedBy.ts, ObjectGrid.tsx), both halves are qualified, as batch 6 qualified both halves of "#13337/#13086". The other bare #3546 lines are not in this batch's lists and are left (Acceptance notes 2).

The literal-anchor sweep (both test-pin classes, ruling 5861900779)

  • Instrument. Every string, template and regex literal in all 4073 tracked test and script files (106544 distinct literals), read with the TypeScript scanner.
  • Candidate filter. A literal is a candidate if it matches the diff's removed lines with two lines of context, raw or comment-flattened: 1983.
  • Test. Does its occurrence count DROP between 3b469c8ea and c292a6400 in any of the 74 changed files, raw or comment-flattened? 136 do.
  • Every one is generic: digits, punctuation, single words ("object", "blocked", "locked"), character classes, and two regexes that read no changed file: /objectui#\d+|objectstack#\d+/ in registry-inputs-spec-parity, which asserts over its own ledger's reasons, and the older spelling of the three submitRedirect tests' ruling matcher, quoted in their own doc comments (the live CITES_ITS_RULING asserts over their own refusal text). None is a changed phrase, a dead number or a changed warning.

Held

By the serial rule: nothing. Open PRs were mapped at branch time (9 open) and again after the push, before this PR opened (11 open). The second mapping came after the push, not before it; the same three files were shared both times.

Three open PRs share a file with this PR:

  • Both PRs below have merged since this PR opened (objectui#10945 as 06a96e948, objectui#10908 as b45d463a9). The trial merge with today's main is clean, and both censuses read 0 on it (contract review 5870922323), so nothing is owed. The two rows are kept as the record at the time.
  • objectui#10945, RecordDetailView.tsx. The blob at its merge-base equals the branch point's. Its hunks are the imports and one block far below; this PR's one changed line in that file is far from both.
  • objectui#10908, types/src/zod/index.zod.ts. Its one insertion is in the export list, far below this PR's two changed comment lines.
  • objectui#10278, plugin-grid/src/ObjectGrid.tsx. The file drifted between its merge-base and the branch point, so this PR's four changed lines were mapped onto its merge-base by a line alignment: the nearest of its hunks is more than 150 lines from any of them.

Trial merges with this head (git merge-tree --write-tree):

.changeset/9954-read-rate-banner.md is held by this seat's objectui#10913 dispatch (PR objectui#10949) and is untouched here. It carries none of this batch's numbers.

Changesets

  • .changeset/10803-dead-citation-sweep-seventh-batch.md, EMPTY frontmatter. It covers the comment-only edits in 17 released packages; no published behaviour changes through them. It points at the second file for the runtime text.
  • .changeset/10803-seventh-batch-runtime-strings.md, '@object-ui/app-shell': patch and '@object-ui/plugin-detail': patch: the two warnings lose their pointer. What renders, and when and how often each warning fires, are unchanged.

Proof of prose-only (C4), against 3b469c8ea

  • Source. Each of the 48 touched .ts / .tsx files was parsed at 3b469c8ea and at this head with TypeScript 6.0.3's createSourceFile, and re-printed by createPrinter({ removeComments: true }).
    • 46 of 48 prints are identical.
    • activityItemType.ts and recordActivityFeed.ts are equal once the one listed substitution each (Special cases 8) is applied to the base print, each matched once.
    • 0 parse diagnostics.
    • Lit controls on the same instrument: editing a string literal moves the print; re-spacing a comment does not.
  • Changesets. The frontmatter block of every one of the 24 edited changesets is byte-identical at 3b469c8ea and this head (24 of 24, md5). The overwrite gate below agrees.
  • Scope of the diff: 74 files, +157 / −115: 24 edited and 2 new changesets, and 48 non-test source files in 17 released packages. No test file.

Gates, on this head c292a6400

Each line is the gate's own verdict and exit code, captured by redirect-then-$?.

  • node scripts/check-changeset-presence.mjs, exit 0: "48 source file(s) of 17 released package(s) changed, and this change declares 2 changeset(s): .changeset/10803-dead-citation-sweep-seventh-batch.md, .changeset/10803-seventh-batch-runtime-strings.md."
  • pnpm changeset:check, exit 0: "All workspace packages are in the changeset fixed group." / "No changeset declares a major bump."
  • node scripts/check-changeset-overwrite.mjs (report-only), exit 0: "2 changeset(s) added, 24 modified, 0 deleted". declared at base equals declares now for each of the 24.
  • pnpm check:changeset-claims (report-only), exit 0:
    • "Every one of those 1 address(es) either names the tree it was read from, or points at a line this change does not move";
    • "Every package declared across those 22 body(ies) is either not negated …";
    • the standing notice "87 pending changeset(s) describe a file this change touches". Read against the diff: a pending changeset quoting a replaced pointer would itself carry a dead number and sit in the census, which reads 0.
  • pnpm check:control-bytes, exit 0: "check-control-bytes: OK (scanned 9229 tracked text file(s); skipped 85 binary)." A grep -P control-byte self-scan of the 74 files finds none.
  • pnpm check:new-line-citations, exit 0: "VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0".
  • pnpm check:pending-changeset-literals, exit 0: "No test source names a pending changeset."
  • Also run over the touched comments:
    • pnpm check:spec-symbols, exit 0: "spec member citations: 1421 sources + 184 documentation pages; nothing cites a key its spec symbol does not declare.";
    • pnpm check:installed-pin-claims, exit 0 ("OK");
    • pnpm check:comment-mask-corpus, exit 0 (1 disagreeing file, within the ceiling objectui#7882 holds open);
    • node scripts/check-hand-rolled-comment-mask.mjs, exit 0 ("OK every carrier is a DEBT entry, and every DEBT entry still carries one.");
    • pnpm check:handler-key-reads, exit 0 ("every judged read is a declared member of it").
  • The governed-surface predicate over the 74 paths, exit 0: "NOT GOVERNED — 74 path(s) checked against 5 governed surface(s); none matched." Lit control AGENTS.md: exit 3.

Tests and type-check, through the shared verify lock, on c292a6400. Each is VERDICT command-exit 0.

  • scripts/__tests__/, the whole directory, whose whole-tree scanners read the touched files and changesets: Test Files 177 passed | 2 skipped (179), Tests 5332 passed | 2 skipped (5334). The two skipped files are the network-escape fixtures that run only as a child.
  • packages/types/, core/, react/, i18n/, providers/, permissions/ and data-objectstack/, whole packages, in one run: Test Files 704 passed (704), Tests 13170 passed | 13 skipped (13183).
  • The eight touched plugin packages (calendar, designer, detail, form, grid, kanban, list, tree): Test Files 787 passed | 1 skipped (788), Tests 7521 passed | 27 skipped (7548).
  • packages/components/: Test Files 324 passed | 1 skipped (325), Tests 3148 passed | 24 skipped (3172).
  • packages/app-shell/: Test Files 854 passed | 1 skipped (855), Tests 8789 passed | 9 skipped (8798).
  • Type-check: turbo run build of the 28-package dependency closure (Tasks: 28 successful, 28 total), then pnpm --workspace-concurrency=2 with the 17 package filters run type-check: 17 script echoes, 17 Done. A first attempt before the build exited 2 on an unbuilt dependency (Cannot find module '@object-ui/types') and measured nothing.
  • No red leg: the sweep found no anchor to move, so there is no pin whose old copy should fail.

CI on c292a6400: 43 check-runs, 40 success, 3 skipped, 0 failed; Spec Main Shape Gate success.

Acceptance notes

  1. Dead objectstack numbers written bare: 10 more lines in the same two classes. A bare number resolves to this repository, where each of these is a live, unrelated card, so no objectstack# census sees them.
  2. The rest of the bare #3546 population. Five more comment lines write objectstack#3546 as a bare #3546 (RecordDetailView.tsx 2, RelatedRecordActionsBridge.tsx, record-details.tsx, fieldWriteGate.ts), and one writes it as objectui#3546 (plugin-detail's index.tsx). Each names the server's effective API operation set on a detail or form surface. It is live-but-wrong, not a 404, like the #3391 class this batch closed. Carrier: none.
  3. A stale label in a comment. The ObjectCalendar.tsx sentence said objectui#8652 is pm:blocked; that card reads pm:on-hold today. Special cases 1 says how the repaired sentence avoids the label.
  4. Filenames are not citations. .changeset/17147-plugin-disclosure-not-enforced.md carries one of the 30 numbers in its name; it stays, as in PRs objectui#10707, docs: cite landing commits where pending changesets and source docblocks name ten objectui issues that answer 404 (objectui#10771) #10797, docs: cite landing commits where pending changesets and source comments name fourteen objectui issues that answer 404 (objectui#10803) #10854, docs: cite landing commits where pending changesets and source comments name 32 objectui issues that answer 404 (objectui#10803, batch 3) #10869, docs: cite landing commits where pending changesets and source comments name 30 objectui issues that answer 404 (objectui#10803, batch 4) #10875, docs: cite landing commits where pending changesets and source comments name 41 objectui issues that answer 404 (objectui#10803, batch 5) #10892 and docs: cite landing commits where pending changesets and source comments name 23 objectui issues that answer 404, and re-qualify 19 bare objectstack numbers (objectui#10803, batch 6) #10914.

Generated by Claude Code

Tests block completed by the domain:ui seat #1 from the dev report 5870507620 (the suites that finished after this PR opened), and three figures corrected after contract review 5870922323 (the #9933 and #6515 quotes, and the Held rows); no code claim moved.

…re apiMethods-card number (objectui#10803, batch 7)

The 30 objectstack issue and pull-request numbers that answer 404 in the
card's two classes (pending changeset prose and non-test packages/*/src)
now cite the objectstack commit that landed each change, or drop the dead
number where the sentence already names the live pull request or commit,
or dates the ruling it points at. One sentence cites this repository's own
landing commit. The 28 comment lines that wrote the apiMethods whitelist
card as a bare number now read objectstack#3391 (and objectstack#3546 on
the two lines that pair it).

Two console warnings in @object-ui/app-shell and @object-ui/plugin-detail
lose the dead pointer and nothing else (patch changeset); every other edit
is comment or changeset prose, and every edited changeset keeps its
frontmatter byte-identical.

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

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 87 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/10612-form-arms-managed-lock.md

  • names fieldWriteGate.ts → packages/plugin-form/src/fieldWriteGate.ts — edited by this change

    Clause-②: no. No exported symbol, type or prop changes. The shared step lives in fieldWriteGate.ts, an internal module that index.tsx does not re-export. What changes is which inputs the drawer, modal, tabbed, split and wizard layouts draw enabled.

.changeset/4730-retire-dead-locale-key-batch.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    • Superseded twin vocabularies. cellRender.* and rowAction.* duplicated a grid.* vocabulary that won. RowActionMenu.tsx is fully i18n-wired and reads grid.openMenu / grid.edit / grid.delete; ObjectGrid.tsx reads grid.empty / grid.yes / grid.no / grid.systemFields. The twins had no reader on either side. - Labels that outlived their control. calendar.agenda labelled a view mode b55a34647 retired from CalendarViewMode (now 'month' | 'week' | 'day'). home.quickActions.createApp*, layout.systemNav.createApp, actionDialog.defaultActionTitle / .ok and grid.bulk.selectPlaceholder sit in namespaces whose consumers are live and wired but demonstrably read other siblings. - Surfaces that left the product. map.* is the strongest form: @object-ui/plugin-map declares no @object-ui/i18n dependency and contains no t() call at all, so it cannot consume a locale string. home.stats.* and recordDetail.viewersTooltip name surfaces nothing renders.

.changeset/5903-objectgantt-declared-keys.md

  • names packages/types/src/objectql.ts → packages/types/src/objectql.ts — edited by this change

    Both halves move together. The TS declaration (packages/types/src/objectql.ts) and its zod mirror (src/zod/objectql.zod.ts) gain the same ten keys at the same requiredness — all optional — and no KnownDrift entry is added. navigation is taken from @objectstack/spec's NavigationConfigSchema by reference rather than restated, matching ObjectGridSchema.navigation.

.changeset/5905-componentinput-inputtype-tombstone.md

  • names zod/base.zod.ts → packages/types/src/zod/base.zod.ts — edited by this change

    Stale wording corrected in the same pass, because this change falsifies it: base.ts and zod/base.zod.ts both said the fork was "recorded for a ruling; until then this stays a live, writable key", and widget.ts called it "the open fork". All three now record the ruling. A reader who greps the source instead of the card thread was meeting an open fork that no longer existed.

.changeset/5905-componentinput-retire-constraint-keys.md

  • names packages/types/src/zod/base.zod.ts → packages/types/src/zod/base.zod.ts — edited by this change

    The retirement kit: ?: never on the interface (packages/types/src/base.ts), so authoring one is a tsc error at the registration site; retirementTombstone() on the Zod mirror (packages/types/src/zod/base.zod.ts), so an authored value is REFUSED at parse time with code: 'invalid_type', the key named in the issue path, and the migration note as the message. Deleting the members outright was the option NOT taken: ComponentInputSchema is a non-strict z.object, which strips an undeclared key silently — one silent no-op traded for another. Pinned in packages/types/src/__tests__/component-input-retired-constraint-keys.test.ts.

.changeset/6051-gantt-flat-config-declared-keys.md

  • names packages/types/src/index.ts → packages/types/src/index.ts — edited by this change

    GanttConfig itself gains nine members and is a published type, exported by name from packages/types/src/index.ts: lockField, objectField, summaryExtent, defaultCollapsedDepth, borderColorField, dependencyTypes, timeZone, exportFileName, interactions. The entry file's diff is empty only because the export list already named the type — the widening happened at the declaration.

.changeset/6247-option-visiblewhen-metadata-admin.md

  • names SchemaForm.tsx → packages/app-shell/src/views/metadata-admin/SchemaForm.tsx — edited by this change

    Three consuming controls now filter, and each has a pin asserting an option absent on a false predicate: the builtin Select in SchemaForm.tsx, MultiSelectWidget, and colorPaletteOptions → ColorSwatchGroupWidget. Asserting absence is the whole of the test design — this evaluator fails OPEN, so "the option is shown" is equally the outcome of predicate-true, predicate-never-arrived and predicate-faulted, and a positive-only pin passes against the unfixed renderer.

.changeset/6310-designer-formula-key-retired.md

  • names packages/app-shell/src/views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Not touched: designer.field.formula ('Formula (CEL)') in packages/app-shell/src/views/metadata-admin/i18n.ts, a different and live key belonging to metadata-admin's ObjectFieldInspector — the surface that still authors formula expressions.

.changeset/6473-metadatatypeactions-param-dialog-close.md

  • names ActionParamDialog.tsx → packages/app-shell/src/views/ActionParamDialog.tsx — edited by this change

    The pre-reset paramState.resolve?.(null) is dropped as well, on an enumeration rather than on "resolving twice is a no-op": onOpenChange is reachable from exactly three places, all inside ActionParamDialog, and every one settles the promise before asking for the close — handleSubmit, handleCancel, and the Radix root handler that delegates to handleCancel (the single route Escape, an overlay click and the header close button all take). All four routes are driven in the new test, with a census over ActionParamDialog.tsx so a later call site that skipped the settle is red there instead of leaving a promise pending forever.

.changeset/6519-retired-field-key-strip.md

  • names plugin-designer/src/MetadataFieldsPage.tsx → packages/plugin-designer/src/MetadataFieldsPage.tsx — edited by this change

    Unifying the three retired-key lists on this seam is deliberately not part of this change: it spans plugin-designer/src/MetadataFieldsPage.tsx, which objectui#6489 owns in flight.

.changeset/6597-retire-fieldmeta-referenceto.md

  • names plugin-grid/src/ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    No authoring story survived the search either. ObjectGrid's own relational-meta pass-through (applyRelationalMeta, plugin-grid/src/ObjectGrid.tsx) copies reference_to / reference / display_field / etc. from the SCHEMA field def only, at all three of its call sites — never from an authored column override. No doc, example, or fixture in this repo shows a table column pinning a lookup's target away from what its schema field already says. Under the maintainer's standing startup-stage rule (2026-08-27: deprecated/alias spellings retire immediately, no transition windows), no measured demand selects withdraw.

.changeset/6771-retire-body-child-list-dialect.md

  • names renderers/layout/containers.tsx → packages/components/src/renderers/layout/containers.tsx — edited by this change

    Non-rendering readers keep their arm on the same rule, and none of them renders anything: while a renderer still reaches stored body content, a reader that must see the SAME content keeps its arm, or the renderer draws what the reader cannot find. Those are the two tab-subtree walkers in renderers/layout/containers.tsx, app-shell's pageSchemaIntrospect (CONTAINER_KEYS) and PageBlockInspector (STRUCTURAL_PROP_KEYS, the inspector half of a stored properties.body), and the CLI's OBJECTUI_STRUCTURAL_KEYS — a file-IDENTIFICATION marker, where keeping body is what lets an old file still be recognised as an ObjectUI node and therefore refused, instead of silently not judged.

.changeset/6816-activityfeed-unknown-kind-fails-open.md

  • names activityItemType.ts → packages/app-shell/src/layout/activityItemType.ts — edited by this change

    An unrecognised kind now fails open: the row renders, through the generic system presentation activityItemType.ts already declares for a value outside its mapping table (neutral on purpose, and in particular not update). A kind that is present and toggled off is still filtered out — presence, not truthiness, is now the question the filter asks.

.changeset/6939-filter-builder-mirror.md

  • names packages/components/src/custom/filter-builder.tsx → packages/components/src/custom/filter-builder.tsx — edited by this change

    1. FilterFieldSchema required name. Every read site matches an entry by value — fields.find((f) => f.value === …) in getOperatorsForField, changeField, getInputType and renderValueInput, fields[0]?.value in addCondition, and ANGLE-BRACKETS(SelectItem value={field.value}) in the field dropdown (packages/components/src/custom/filter-builder.tsx). name had zero read sites, and FilterBuilderProps.fields in that same file already declares ArrayANGLE-BRACKETS({ value, label, type? }). @object-ui/fields' deriveFilterFields, the real producer that builds this list from an object schema, emits value too, and the published doc (content/docs/components/complex/filter-builder.mdx) has declared value: string all along. 2. Its type enum was string | number | date | boolean | select. string is a phantom, and text, datetime and time — three of the six FilterValueFamily members the component folds a column into — were all refused. 3. FilterGroupSchema was { operator, conditions }. The gate is isValidGroup, which tests Array.isArray(v.conditions) and v.logic === "and" || v.logic === "or" and nothing else.

.changeset/7070-no-invented-gantt-date-fields.md

  • names app-shell/src/views/ObjectView.tsx → packages/app-shell/src/views/ObjectView.tsx — edited by this change

    • app-shell/src/views/ObjectView.tsx — the console object page. The inline branch becomes ganttViewOptions, the sibling of calendarViewOptions and timelineViewOptions: the declared block spread whole, title floored at 'name', no date field invented. - plugin-list/src/ListView.tsx — the render branch AND the capability gate. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.
  • names plugin-list/src/ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    • app-shell/src/views/ObjectView.tsx — the console object page. The inline branch becomes ganttViewOptions, the sibling of calendarViewOptions and timelineViewOptions: the declared block spread whole, title floored at 'name', no date field invented. - plugin-list/src/ListView.tsx — the render branch AND the capability gate. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.

.changeset/7087-disabled-twin-symmetry.md

  • names base.zod.ts → packages/types/src/zod/base.zod.ts — edited by this change

    Those 18 redeclarations are removed, on both faces. The interfaces inherit BaseSchema.disabled the way they always inherited visible; the zod mirrors inherit base.zod.ts's z.union([z.boolean(), z.string()]) through .extend()'s merged .shape, so there is no second spelling of the union to drift from — the route ChatbotSchema took in objectui#6169.

.changeset/7125-dashboard-empty-state-keys-retired.md

  • names packages/app-shell/src/views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Not touched: table.noRows ('No rows to display') and engine.form.noRows (packages/app-shell/src/views/metadata-admin/i18n.ts, read at widgets.tsx) — two different, same-named keys in different namespaces. Nor the comments in WidgetEmptyState.tsx, DatasetWidget.tsx, ObjectDataTable.tsx and PivotTable.tsx that record WHY three widgets with three strings became one shared empty state; the packs' own comment keeps that rationale and now names the retirement instead of a row that is gone.

.changeset/7182-declared-action-ids-one-rule.md

  • names containers.tsx → packages/components/src/renderers/layout/containers.tsx — edited by this change

    New on @object-ui/types, beside actionRendersAt: the pure resolveDeclaredActionIds(elements, registeredActions), with the DeclaredActionsResolution / DeclaredActionsRefusal result types (the shape classifier stays module-internal: called with no registry, the function already returns the registry-independent verdict a renderer needs before its lookup). Both renderers call it; the whole-array switch in record-quick-actions.tsx and the per-element normalisation in containers.tsx are gone. The rule is closed: a string is an id, a non-null non-array object is an inline definition, and any other element (null, a number, a nested array) is refused at its index too. An all-id array resolves by name in authored order, first registration winning on a duplicate name; ids that name nothing are reported back with their index for the caller to warn about once its lookup has settled.

.changeset/7313-object-calendar-record-source.md

  • names plugin-calendar/src/ObjectCalendar.tsx → packages/plugin-calendar/src/ObjectCalendar.tsx — edited by this change

    ObjectCalendar resolves its records through the shared ladder (resolveRecordSourceConfig in @object-ui/core, called from plugin-calendar/src/ObjectCalendar.tsx): data first, then staticData, then objectName. The published TypeScript interface REQUIRED objectName and declared neither data nor staticData; the published Zod mirror did the same. So an object-calendar node authored on staticData — the route the plugin page documents twice — rendered correctly and was refused by safeValidateSchema, and could not be annotated with its own type (TS2741: Property 'objectName' is missing).

.changeset/7322-object-kanban-component-props.md

  • names ObjectKanban.tsx → packages/plugin-kanban/src/ObjectKanban.tsx — edited by this change

    Inside ObjectKanban.tsx, three schema-key reads drop their as any: titleField (two sites, now honest because the object-kanban arm declares it) and cardFields / cardTitle (already declared; the casts were redundant). (schema as any).navigation stays — navigation is declared on neither face, so removing the cast would change nothing but the spelling of an index-signature read.

.changeset/7322-object-kanban-group-by-limit.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    Breaking for authored metadata: ObjectKanbanSchema.groupField is RETIRED (objectui#7322, ADR-0049 enforce-or-remove), and the two keys the object-kanban renderer actually reads — groupBy and limit — are now DECLARED and validated on both published faces: the TypeScript interface in objectql.ts and the Zod mirror in zod/objectql.zod.ts.

  • names packages/plugin-kanban/src/ObjectKanban.tsx → packages/plugin-kanban/src/ObjectKanban.tsx — edited by this change

    What was measured, on this branch's base (53ded82b). packages/plugin-kanban/src/ObjectKanban.tsx reads schema.groupBy at thirteen sites (lane materialisation at :601 / :625 / :640, card moves at :747 / :865, and their effect deps) and schema.limit at two (:264, $top: schema.limit ?? DEFAULT_KANBAN_LIMIT, and the effect deps at :291). groupField has ZERO read sites anywhere under packages/plugin-kanban/ — against a control of those thirteen groupBy reads in the same query, so the zero is a reading, not a blind grep. Yet the declaration REQUIRED groupField and declared neither groupBy nor limit. Measured from source: the documented, tested, working shape — { type: 'object-kanban', objectName, groupBy, limit } — failed ObjectKanbanSchema.safeParse and safeValidateSchema on the missing groupField, and only ever reached the renderer through BaseSchema's [key: string]: any and .passthrough(), admitted unexamined. An author who followed the declaration wrote groupField and got a board that grouped nothing, with no diagnostic on either face.

.changeset/7363-objectql-union-arms.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    ObjectGallerySchema and ObjectDataTableSchema are members of ObjectQLComponentSchema on both faces — the TS union in objectql.ts and the zod union in zod/objectql.zod.ts — so AnyComponentSchema, and with it validateSchema / safeValidateSchema / objectui validate, has an arm for object-gallery and object-data-table nodes (objectui#7363).

.changeset/7493-componentinput-retire-label-defaultvalue-advanced.md

  • names packages/types/src/zod/base.zod.ts → packages/types/src/zod/base.zod.ts — edited by this change

    The retirement kit: ?: never on ComponentInput (packages/types/src/base.ts), so authoring one is a tsc error at the registration site; retirementTombstone() on ComponentInputSchema (packages/types/src/zod/base.zod.ts), so an authored value is REFUSED at parse time with code: 'invalid_type', the key named in the issue path, and the migration note as the message (one string, both channels). Pinned in packages/types/src/__tests__/component-input-retired-keys-7493.test.ts, which also holds a tree-scoped absence census over every inputs: array under packages/** and apps/**.

.changeset/7499-gantt-non-axis-floors-omitted.md

  • names plugin-list/src/ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    • plugin-list/src/ListView.tsx — the object-gantt render branch. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.

.changeset/7530-predicate-envelope-declared.md

  • names base.zod.ts → packages/types/src/zod/base.zod.ts — edited by this change

    • ExpressionWire (type, main entry) — the TypeScript wire union, in packages/types/src/expression.ts. - ExpressionWireSchema (@object-ui/types/zod) — its runtime twin, hoisted out of zod/form.zod.ts (where it was module-private) into zod/expression.zod.ts and imported by both base.zod.ts and form.zod.ts. One envelope type, reused by reference; no second spelling.

.changeset/7632-shared-record-source-config.md

  • names packages/types/src/objectql.ts → packages/types/src/objectql.ts — edited by this change

    That ladder is published contract on both faces — packages/types/src/objectql.ts and its zod mirror both ship .describe() strings naming getDataConfig's order (77cb489b4, maintainer ruling 2026-09-02), pinned by objectql-record-source-refinement-6939.test.ts — and it was hand-copied into five plugin components with no gate holding them together. A change to the ruled order had five edit sites and nothing that noticed a missed one; that is the AGENTS.md #0.1 drift class.

.changeset/7694-chart-series-chart-type-alias-refusal.md

  • names zod/index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    • Before: series: [{ name: 'revenue', chartType: 'line' }] validated green through @object-ui/types/zod (safeValidateSchema, objectui check / objectui validate, any pipeline that keeps parse()'s output) — and the key was gone from the output, so a consumer of the parse result drew that series in the chart's own family, precisely what the author was overriding. On the TypeScript face the key was merely an excess property on a fresh literal; a widened object carrying it assigned structurally. - After: the same document REFUSES at series[i].chartType (issue code invalid_type) with one message on both channels — the parse-time issue and the .describe() metadata: Unrecognized key(s) on this chart series: \chartType`. Did you mean `chartType` → `type`? …followed by the reason and the remedy. Writetype: 'bar' | 'line' | 'area'. On the TypeScript face ChartDataSeries.chartTypeis a?: nevertombstone, so both the fresh literal and the widened assignment aretsc errors. - **Both written** ({ type: 'bar', chartType: 'line' }) is refused at chartTypealone — the key is not folded ontotypeand no precedence is minted between the two spellings. - **Which documents to scan.** The narrowing does not stop atChartDataSeriesSchema; it reaches every document through the parents that embed it — ChartSchema.series (zod/data-display.zod.ts, z.array(ChartDataSeriesSchema)) and, one level further out, ReportSectionSchema.chart (zod/reports.zod.ts, ChartSchema.optional()). Authors meet it through safeValidateSchema() (zod/index.zod.ts, which parses AnyComponentSchema) and through the CLI's objectui validate command (packages/cli/src/cli.ts). In practice: every chartnode'sseries[], and every report section whose chart` carries one.

.changeset/7712-kanban-calendar-filter-input.md

  • names ObjectKanban.tsx → packages/plugin-kanban/src/ObjectKanban.tsx — edited by this change

    ObjectKanban.tsx sends the authored key to the query as $filter: schema.filter and ObjectCalendar.tsx does the same, and @objectstack/spec's ComponentPropsMap declares filter on both blocks (measured: safeParse accepts it, and refuses an undeclared key by name on the same call). But none of the four registrations that publish those two renderers listed filter in inputs, and sdui-parser's validateTree reports unknown-prop for every key no inputs entry claims. So an author writing the one spelling that WORKS was told it was unknown — objectui#6678's shape, where a correct write draws the same diagnostic as a write that does nothing. That is worse than an inert key: it actively punishes the correct behaviour, and the honest response to it is to delete working metadata.

  • names ObjectCalendar.tsx → packages/plugin-calendar/src/ObjectCalendar.tsx — edited by this change

    ObjectKanban.tsx sends the authored key to the query as $filter: schema.filter and ObjectCalendar.tsx does the same, and @objectstack/spec's ComponentPropsMap declares filter on both blocks (measured: safeParse accepts it, and refuses an undeclared key by name on the same call). But none of the four registrations that publish those two renderers listed filter in inputs, and sdui-parser's validateTree reports unknown-prop for every key no inputs entry claims. So an author writing the one spelling that WORKS was told it was unknown — objectui#6678's shape, where a correct write draws the same diagnostic as a write that does nothing. That is worse than an inert key: it actively punishes the correct behaviour, and the honest response to it is to delete working metadata.

.changeset/7762-object-grid-export-options-bare-array-refusal.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    What was measured, on this branch's base. The mirror declared NO exportOptions member at all, and BaseSchema is .passthrough(), so ObjectGridSchema.safeParse({ type: 'object-grid', objectName: 'accounts', exportOptions: ['csv', 'xlsx'] }) returned success: true with the array back VERBATIM — as did { formats: ['csv', 'pdf'], compression: 'gzip' }. Nothing on the render path parses, and ObjectGrid.tsx reads schema.exportOptions?.formats and only that, so the authored array then lost SILENTLY to the ['csv', 'json'] default: the useEffect that warns about dropped formats reads .formats too and returns early when it is absent, while !!schema.exportOptions kept the export button on screen. An author declared ['csv', 'xlsx'] and got csv/json with no error, no warning and no console line. The two authoring faces disagreed in the direction opposite to objectui#6956's: the TypeScript interface already declared the object form only, so TS refused what zod admitted.

.changeset/7772-page-block-kanban-group-by-control.md

  • names ObjectKanban.tsx → packages/plugin-kanban/src/ObjectKanban.tsx — edited by this change

    What was measured, on this branch's base (0c8dbc49). BLOCK_CONFIG['object-kanban'] offered exactly four controls — objectName / groupField / titleField / cardFields. ObjectKanbanSchema.groupBy is REQUIRED on both published faces (objectql.ts:2765 groupBy: string, no ?; objectql.zod.ts:1001 groupBy: z.string(), no .optional()) and had no control at all, while groupField — the only control able to set grouping — has been a retirementTombstone() on the Zod face and ?: never on the TS face since objectui#7322. ObjectKanban.tsx reads schema.groupBy at thirteen sites and groupField at zero (control: those same thirteen hits in the one query, so the zero is a reading).

.changeset/7773-kanban-adapter-groupfield-write.md

  • names ObjectKanban.tsx → packages/plugin-kanban/src/ObjectKanban.tsx — edited by this change

    The object-kanban renderer never read it: groupField has ZERO hits anywhere under packages/plugin-kanban/, against a control of thirteen schema.groupBy read sites in ObjectKanban.tsx from the same query — so the zero is a reading, not a blind grep. The write was inert; the board grouped by groupBy and groupField rode along unread.

  • names ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    Who is NOT affected — the boundary is node-local. Every VIEW-LEVEL groupField read is untouched and still live: it is a legacy alias of the spec's groupByField on the kanban view config, mapped by normalize-list-view.ts, and both adapters still resolve lanes through it (ObjectView.tsx's kanbanCfg.groupField ||, ListView.tsx's groupByField || groupField). Authoring options.kanban.groupField on a list-view or object-view keeps working exactly as documented in packages/plugin-list/README.md. groupField is dead only on the generated object-kanban NODE.

.changeset/7780-object-kanban-record-source.md

  • names packages/plugin-kanban/src/ObjectKanban.tsx → packages/plugin-kanban/src/ObjectKanban.tsx — edited by this change

    packages/plugin-kanban/src/ObjectKanban.tsx resolves a board's rows in four steps: the pre-fetched data PROP a parent passes (hasExternalData), then bind through useDataScope(schema.bind), then the inline ROW ARRAY on schema.data, and only then a fetch keyed by schema.objectName — rawData = (hasExternalData ? externalData : undefined) || boundData || schema.data || fetchedData, with the fetch itself gated on schema.objectName && !boundData && !schema.data. Every objectName read is guarded. Both published faces nevertheless REQUIRED objectName, so a bind-only or data-only board — one that renders correctly today — was refused by the shipped validator and could not be annotated with its own type.

  • names ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    groupBy is untouched and stays REQUIRED (objectui#7322, PR feat(types): declare ObjectKanbanSchema.groupBy and .limit, retire groupField on both faces (objectui#7322 item 1) #7774). A record source and a lane key are different questions: every lane-less document above is still refused at groupBy, and the two readings PR feat(types): declare ObjectKanbanSchema.groupBy and .limit, retire groupField on both faces (objectui#7322 item 1) #7774 excluded from counting as a lane-less mode — the dataSource json fragment in content/docs/utilities/data-objectstack.mdx and ListView.tsx's runtime-generated node — are asserted still-refused in the new pin. The retired groupField tombstone is likewise still refused by name.

.changeset/7804-objectql-handler-key-arms.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    The four plain objectql.ts node faces declare the nine handler keys their registered renderers read (the objectql.ts slice): ObjectFormSchema.onCancel / .onError / .onOpenChange / .onStepChange / .onSuccess, ObjectGallerySchema.onCardClick / .onRowClick, ObjectGridSchema.onNavigate and ObjectViewSchema.onNavigate.

.changeset/7804-tree-view-handler-slot.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    'runtime-slot' and not 'retired', measured at this key's own channel. 'retired' publishes "no renderer reads this key, so nothing could ever run it" — true of the two siblings already tombstoned on this arm (onSelectChange, onExpandChange) and flatly false here, since the read is live and INVOKED. ⚠️ No in-repo host builds a tree-view node carrying the key: the channel is wired end to end and only the supplier is absent, which is the same shape as ObjectFormSchema.onStepChange in this card's objectql.ts slice and is not evidence of a dead read. The TypeScript declaration is unchanged and still callable, so a programmatic host supplies it exactly as before.

.changeset/7917-export-breadcrumb-object-tree-zod-schemas.md

  • names index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    AnyComponentSchema declares 107 node component types. 105 of them could be named on the ./zod barrel — ButtonSchema.safeParse(node), which is what a designer, a form builder or a targeted test needs. The arms declaring type: 'breadcrumb' (navigation.zod.ts) and type: 'object-tree' (objectql.zod.ts) could not: both were already export const in their own module, but index.zod.ts — the package's only zod entry point — did not re-export them, so the schemas existed, were maintained, and were applied by the union while no consumer could name them.

.changeset/7918-zod-lazy-getter-identity.md

  • names base.zod.ts → packages/types/src/zod/base.zod.ts — edited by this change

    The card that found this did not claim the ten were wrong. It asked whether the spelling was buying a temporal-dead-zone dodge, and that check is what shipped. Each of the ten was rewritten in place to const inner = ANGLE-BRACKETS(body); z.lazy(() => inner), the package rebuilt, and the built barrel imported in a fresh process. Eight refuse to load. Seven name the very const being declared (children: z.array(TreeNodeSchema) sits inside TreeNodeSchema's own initialiser); SchemaNodeSchema names BaseSchemaCore, which base.zod.ts declares below it. For those eight the z.lazy is load-bearing, so ActionSchema, AppMenuItemSchema, FilterGroupSchema, MenuItemSchema, NavLinkSchema, NavigationMenuItemSchema, SchemaNodeSchema and TreeNodeSchema keep the spelling they have. The two that loaded clean are memoised: FilterBuilderConditionSchema is not recursive at all, and NavigationItemSchema already defers its self-reference through an inner z.lazy(() => NavigationItemSchema) on children.

.changeset/7963-alert-dialog-footer-keys-retired.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    Nothing else moves. These spellings are overloaded across the tree and every other owner is a live key on a different declaration — FormSchema.cancelLabel, objectql.ts's confirmLabel, plugin-designer's ConfirmDialog React props, plugin-grid's def.confirmLabel, and plugin-form's ModalForm / DrawerForm, which build a local cancelLabel from schema.cancelText. None is an AlertDialogSchema; none is touched, and a pin asserts it. No fixture, catalog schema, example app or doc fence authored any of the three on an alert-dialog node, so no shipped document is stranded.

.changeset/7997-detail-view-related-retired.md

  • names RecordDetailView.tsx → packages/app-shell/src/views/RecordDetailView.tsx — edited by this change

    Why it retired. @objectstack/spec declares no DetailView schema at all — every DetailView occurrence in packages/spec/src is prose about this repo's own RecordDetailView.tsx — so this array mirrored no protocol schema and drifted freely: it declared columns as TableColumn[] while the renderer it fed also accepted bare field names, { field, label } and legacy { name, label } spellings. The axis that carried the ruling was measured zero pull: no application code authored the member, both internal producers of a detail-view node (RecordDetailDrawer, renderers/record-details.tsx) synthesize it without related, and the only in-tree authorings carrying real columns were two documents — both rewritten here.

.changeset/8155-app-root-residue-swept.md

  • names containers.tsx → packages/components/src/renderers/layout/containers.tsx — edited by this change

    Swept as a class, not as two coordinates. Every other place in this tree that stated app was a bound expression-scope root is corrected in the same change — the diagnostic copy and its byte-pin, the ambient-scope docblocks in @object-ui/react (SchemaRenderer, useExpression), @object-ui/core (ActionRunner.ParamDef.visible, RowPredicateOptions.scope), @object-ui/components (form.tsx, containers.tsx), @object-ui/plugin-detail, @object-ui/plugin-form (docblock and README), @object-ui/app-shell and the console app, plus fourteen test fixtures that transcribed the old bag with an app key. The fixtures in @object-ui/app-shell now call buildExpressionScope instead of transcribing it, so that pair cannot drift again.

.changeset/8171-calendar-sort-input.md

  • names ObjectCalendar.tsx → packages/plugin-calendar/src/ObjectCalendar.tsx — edited by this change

    objectui#7712's defect, one key over. ObjectCalendar.tsx lowers the authored key onto its own query as $orderby: convertSortToQueryParams(schema.sort), and @objectstack/spec's ComponentPropsMap['object-calendar'] declares sort (measured on 17.2.0: safeParse({ objectName, sort }) returns success: true, while the same strict schema on the same call refuses bogusProp by name — that control is what makes the acceptance a verdict). But neither of the two registrations that publish this renderer — plugin-calendar:object-calendar and view:calendar — listed sort in inputs, and sdui-parser's validateTree reports unknown-prop for every key no inputs entry claims. So an author writing the one spelling that WORKS was told it was unknown — objectui#6678's shape, where a correct write draws the same diagnostic as a write that does nothing.

.changeset/8174-kanban-calendar-filter-sort.md

  • names ObjectKanban.tsx → packages/plugin-kanban/src/ObjectKanban.tsx — edited by this change

    filter had four declaration faces and only three of them named it: @objectstack/spec declares it (ComponentPropsMap['object-kanban'] and ['object-calendar']), both plugins' registration inputs publish it, and both renderers read it — ObjectKanban.tsx lowers schema.filter onto $filter, ObjectCalendar.tsx lowers schema.filter onto $filter and schema.sort onto $orderby through convertSortToQueryParams. This package's own published faces (the TypeScript interface and its zod mirror) named none of them, so an authored value reached the renderer only through BaseSchema's index signature and the mirror's .passthrough() — admitted, never examined. That is the same reasoning finding(types,plugin-kanban): ObjectKanbanSchema requires groupField (zero read sites) and declares neither groupBy nor limit — no working object-kanban node is assignable to any declared type #7322 used to move groupBy and limit into this same interface.

  • names ObjectCalendar.tsx → packages/plugin-calendar/src/ObjectCalendar.tsx — edited by this change

    filter had four declaration faces and only three of them named it: @objectstack/spec declares it (ComponentPropsMap['object-kanban'] and ['object-calendar']), both plugins' registration inputs publish it, and both renderers read it — ObjectKanban.tsx lowers schema.filter onto $filter, ObjectCalendar.tsx lowers schema.filter onto $filter and schema.sort onto $orderby through convertSortToQueryParams. This package's own published faces (the TypeScript interface and its zod mirror) named none of them, so an authored value reached the renderer only through BaseSchema's index signature and the mirror's .passthrough() — admitted, never examined. That is the same reasoning finding(types,plugin-kanban): ObjectKanbanSchema requires groupField (zero read sites) and declares neither groupBy nor limit — no working object-kanban node is assignable to any declared type #7322 used to move groupBy and limit into this same interface.

.changeset/8253-export-tree-view-config.md

  • names plugin-tree/src/ObjectTree.tsx → packages/plugin-tree/src/ObjectTree.tsx — edited by this change

    What was wrong. tree is a host-composition-only view type — ruled deliberate on objectui#5321, it is a member of neither ObjectViewSchema.defaultViewType nor NamedListView.type, so the branch runs only when a host passes a views prop. On that path a per-view tree block is read at four sites, and its only description anywhere was a module-local, non-exported interface TreeConfig inside plugin-tree/src/ObjectTree.tsx. The live host is the console: it stores view records and passes them as views, and its create-view dialog offers tree. So a real consumer wrote this block with no type to write it against, and a misspelled parentFeild was admitted by the views entry's [key: string]: any, stored, read by nobody and reported by nothing. Declared ≠ enforced on a surface a non-author re-writes.

.changeset/8318-jsdoc-default-tag-corrections.md

  • names core/src/actions/ActionRunner.ts → packages/core/src/actions/ActionRunner.ts — edited by this change

    • CRUDDialogSchema.size / .closeOnOutsideClick / .closeOnEscape / .showClose — there is no register('crud-dialog' anywhere, so no node of that type ever reaches a renderer. Recorded once on the interface. Per key, the name census differs: two spellings occur nowhere outside the declaration and its zod twin, and showClose's one other occurrence (renderers/overlay/drawer.tsx:38) belongs to DrawerSchema. - ActionSchema.level — type: 'action' is not a rendered node type, and core/src/actions/ActionRunner.ts, which is what makes method / chainMode / reload / close live, does not read level. - CardSchema.variant — card IS registered, twice, and neither registration reads it: the ui route forwards the key to ui/card.tsx, which spreads onto a div and mentions variant nowhere, and the page route forwards only its designer props. - PageNodeSchema.isDefault — page IS registered, and PageRenderer neither reads the key nor forwards it: the wrapper element gets toDomProps(props), an allow-list that does not carry it.

.changeset/8344-node-recursion-point-redirect.md

  • names zod/index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    Two mechanical notes for anyone editing the wiring. AnyComponentSchema is built in zod/index.zod.ts from all 13 category modules and 14 modules import zod/base.zod.ts, so the arm cannot be an import — z.lazy defers evaluation, not the module graph. It is a written option slot that index.zod.ts fills inside AnyComponentSchema's own initializer, and it is a z.union option rather than a z.lazy holder because z.lazy memoises its getter: a holder would let whichever module graph parsed first decide the accept set for the whole process. Both constraints are measured, and the reasoning lives on defineNodeComponentUnion in zod/base.zod.ts.

  • names zod/base.zod.ts → packages/types/src/zod/base.zod.ts — edited by this change

    Two mechanical notes for anyone editing the wiring. AnyComponentSchema is built in zod/index.zod.ts from all 13 category modules and 14 modules import zod/base.zod.ts, so the arm cannot be an import — z.lazy defers evaluation, not the module graph. It is a written option slot that index.zod.ts fills inside AnyComponentSchema's own initializer, and it is a z.union option rather than a z.lazy holder because z.lazy memoises its getter: a holder would let whichever module graph parsed first decide the accept set for the whole process. Both constraints are measured, and the reasoning lives on defineNodeComponentUnion in zod/base.zod.ts.

  • names index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    Two mechanical notes for anyone editing the wiring. AnyComponentSchema is built in zod/index.zod.ts from all 13 category modules and 14 modules import zod/base.zod.ts, so the arm cannot be an import — z.lazy defers evaluation, not the module graph. It is a written option slot that index.zod.ts fills inside AnyComponentSchema's own initializer, and it is a z.union option rather than a z.lazy holder because z.lazy memoises its getter: a holder would let whichever module graph parsed first decide the accept set for the whole process. Both constraints are measured, and the reasoning lives on defineNodeComponentUnion in zod/base.zod.ts.

  • names base.zod.ts → packages/types/src/zod/base.zod.ts — edited by this change

    ⛔ It is NOT closed here, and the reason is measured rather than argued. The route that closes it by binding the union inside SchemaNodeSchema's z.lazy getter was implemented and pushed, and CI refused it: Build Docs failed with ReferenceError: Cannot access 'BaseSchema' before initialization out of packages/types/dist/zod/app.zod.js, because that import makes base.zod.ts depend on the barrel and a bundler is free to evaluate the resulting cycle category-module-first. Reproduced locally in one line — importing dist/zod/app.zod.js throws with the binding in place and loads clean without it. The other three candidates were measured too: a narrowed sideEffects array is not a legal declaration for this package (one gate requires every entry form to be named, another refuses a named entry with no load-time effect, and this package's entry forms are pure), a bare top-level call is dropped by the same flag, and dropping the flag costs 16,078 gzipped bytes on the console framework chunk and moves a workspace census a guard pins.

.changeset/8415-filter-builder-condition-id.md

  • names packages/components/src/custom/filter-builder.tsx → packages/components/src/custom/filter-builder.tsx — edited by this change

    Re-derived from packages/components/src/custom/filter-builder.tsx rather than inherited, and stated as a property rather than as a tally of read sites, since that file keeps gaining condition.id reads and a count goes stale with the next one: which row an edit lands on is decided by id and by nothing else. Each mutator that acts on a single row picks it with a MATCH on id (removeCondition's c.id !== conditionId, updateCondition's and changeOperator's c.id === conditionId, changeField's c.id !== conditionId), every call to one of them hands it condition.id, the row's React key is condition.id, and nothing in the file matches a row by its index. The file's other reads of condition.id pick no row; one of them builds the element id that a half-filled range's blank bound names in aria-describedby. The MATCH sites and the key are re-derived from the component source by the re-derived from the reader block of packages/types/src/__tests__/filter-builder-condition-id-8415.test.ts; the rest of the property is read off the source, and no test pins it.

.changeset/8466-calendar-color-allday-fields.md

  • names ObjectCalendar.tsx → packages/plugin-calendar/src/ObjectCalendar.tsx — edited by this change

    ObjectCalendar.tsx's getCalendarConfig reads FIVE flat keys off the node, and packages/plugin-calendar/README.md teaches all five in one sentence — "point titleField / startDateField / endDateField / allDayField / colorField at your own fields when they differ." Only three of the five were declared. The other two reached the renderer through BaseSchema's [key: string]: any on the TypeScript face and its .passthrough() on the zod mirror: admitted, never examined. A misspelling therefore left the calendar silently colourless while every published gate passed.

.changeset/8499-node-slot-registered-arms.md

  • names zod/index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    Why 49 is acceptable here where 2 was a defect: the 2 were data blocks a consumer had a standing reason to validate on their own, and objectui#7917 (PR feat(types): re-export BreadcrumbSchema and ObjectTreeSchema from the zod barrel (objectui#7917) #8777, open at the time of writing, and the holder of zod/index.zod.ts) exists to export exactly those. The 47 added here are HTML primitives and two input aliases — they have no per-tag consumer to serve, and exporting a SemanticElementSchema / HtmlElementSchema pair would publish a NAMED authoring surface (z.enum families, not per-tag schemas) that this card's ruling does not cover: the ruling is "arm the registered renderers", not "add public exports to @object-ui/types". So the metric is left to move and said out loud instead. ⇒ Whoever next runs that measurement should expect 49, and whoever wants the number back down should treat naming these families as its own decision. If PR feat(types): re-export BreadcrumbSchema and ObjectTreeSchema from the zod barrel (objectui#7917) #8777 lands first, the same movement reads 0 to 47.

.changeset/8598-zod-subpath-single-bundled-module.md

  • names src/zod/index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    The defect. This package declares "sideEffects": false, and src/zod/index.zod.ts fills the node recursion point as the initializer of its AnyComponentSchema const. tsc emitted that barrel as a module whose only other content is re-exports — so a bundler resolving import { CardSchema } from '@object-ui/types/zod' followed the re-export to dist/zod/layout.zod.js, needed nothing from the barrel's own body, and the flag let it drop that body whole. The fill went with it, and every child slot then validated against the pre-spec(types): redirect the node recursion point from BaseSchemaCore to AnyComponentSchema — measured at 9 newly-refused documents, and it drops 118 phantom strict refusals #8344 BaseSchemaCore arm — the ~21 base keys and nothing type-specific — with no error, no warning and no way for the guard inside the dropped code to notice. objectui#8344 shipped that window DECLARED and pointed here to close it.

.changeset/8632-malformed-picklist-option-loud.md

  • names views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Two new strings land in the designer's own en / zh tables — the metadata-admin console owns its strings in views/metadata-admin/i18n.ts and is deliberately outside the ten locale packs (packages/i18n/README.md, "Scope — the engine.* carve-out").

.changeset/8767-object-grid-refuses-string-sort.md

  • names packages/types/src/objectql.ts → packages/types/src/objectql.ts — edited by this change

    Migration. Write the array: sort: [{ field: 'name', order: 'desc' }]. Both keys are required. SortConfig.order carries no ? in @object-ui/types (packages/types/src/objectql.ts) and no .optional() in its zod mirror, and the protocol's own reusable SortItemSchema requires order as well — measured: that schema refuses [{ field: 'name' }] with invalid_value at 0.order. Do not omit it: this block's array arm interpolates whatever is present, so an omitted order lowers to $orderby: 'name undefined' today. That is pre-existing behaviour on the arm this change does not touch, and it is filed as a successor card rather than widened into here.

.changeset/8801-object-kanban-allow-collapse-retired.md

  • names packages/types/src/objectql.ts → packages/types/src/objectql.ts — edited by this change

    • the declarations retired here — packages/types/src/objectql.ts and its mirror packages/types/src/zod/objectql.zod.ts; - the pins that assert the retirement — object-kanban-allow-collapse-retired-8801.test.ts and bare-kanban-node-key-retired-8802.test.ts; - a comment in packages/types/src/zod/complex.zod.ts, recording that the deleted retiredZeroReadKanbanKey helper once carried this spelling on the SIBLING arm; - one row of content/docs/api/schema-reference.md; - the .changeset/ release notes that discuss it — this one, the two historical entries covering the sibling arm's own spelling, and objectui#9629's note recording the correction to this paragraph.

.changeset/8827-kanban-empty-state-settled.md

  • names ObjectKanban.tsx → packages/plugin-kanban/src/ObjectKanban.tsx — edited by this change

    A genuinely empty board still paints DataEmptyState. The reverse regression — "gating on a truthy definition would leave those boards empty forever", named in ObjectKanban.tsx itself — is what the settle contract exists to prevent, and every exit settles: the query succeeding, the query throwing, no readable source, the non-fetch record sources (external, bound and inline data are settled from the first frame), and the schema-only kanban-ui entry, which has no ObjectKanban and therefore no provider and so takes the context's settled default. Lanes, headers, counts and drop targets keep rendering while the rows are in flight; only the claim is withheld.

.changeset/8854-app-shell-readme-flow-draft.md

  • names clientValidation.ts → packages/app-shell/src/views/metadata-admin/clientValidation.ts — edited by this change

    Which door was measured. Not the server, and not the node and edge schemas one at a time: the app-shell metadata admin validates a flow draft client-side by handing the whole draft to the spec's FlowSchema — clientValidation.ts's LOADERS.flow resolves it and ResourceEditPage calls validateMetadataDraft(type, draft) with the editor's entire body. Graded per node and per edge, this block would have reported green while still being refused on save; that is how the identity keys stayed invisible to two previous cards against this same fence.

.changeset/8885-object-chart-drilldown-title-compareto.md

  • names packages/types/src/objectql.ts → packages/types/src/objectql.ts — edited by this change

    ObjectChart.tsx reads all three off schema, and until now neither published copy declared any of them: not the TS interface (packages/types/src/objectql.ts) and not the zod mirror (packages/types/src/zod/objectql.zod.ts). They rode BaseSchema's index signature / .passthrough() and arrived unvalidated. drillDown was the sharpest case — this component's registry inputs advertise it to the designer palette, and @objectstack/spec publishes ChartDrillDownSchema for exactly this carrier, so an author was offered a key that neither published shape mentioned.

.changeset/8913-object-kanban-columns-declared.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    What moved. ObjectKanbanSchema gains columns on both halves that move together — the TypeScript interface (objectql.ts) and its Zod mirror (zod/objectql.zod.ts). Retiring the bare kanban node type key (objectui#8802) removed the only face that judged a lane, and object-kanban had never declared the key, so it rode BaseSchema's [key: string]: any / .passthrough(): read by the renderer at three sites, named by no published face.

.changeset/8990-object-kanban-groupby-optional.md

  • names packages/types/src/objectql.ts → packages/types/src/objectql.ts — edited by this change

    @objectstack/spec declares the key optional — groupBy: z.string().optional() on ObjectKanbanPropsSchema — while this package required it on the TypeScript declaration (packages/types/src/objectql.ts) and on the Zod mirror (packages/types/src/zod/objectql.zod.ts). objectui was therefore narrower than the protocol on a published key: ObjectKanbanSchema.safeParse and safeValidateSchema refused an object-kanban node the protocol accepts, and such a node could not be annotated with its own type.

  • names packages/plugin-list/src/ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    • packages/plugin-list/src/ListView.tsx generates the node as groupBy: laneField. objectDef loads asynchronously, so laneField is undefined on every load until it lands, and stays undefined whenever the object offers no stageField hint and none of status / stage / state / phase. The renderer serves that node; both published faces refused it. - content/docs/utilities/data-objectstack.mdx documents an object-kanban node that is exactly { type, dataSource }, with no groupBy. ⚠️ This one is weaker and is cited for what it is: that fragment is still refused after this change, at RECORD_SOURCE_REQUIRED, because dataSource is not a rung of the record-source ladder. It shows a lane-less board is a documented authoring; it is not a document this change admits.
  • names ObjectKanban.tsx → packages/plugin-kanban/src/ObjectKanban.tsx — edited by this change

    What a lane-less board does, measured rather than assumed. Every schema.groupBy read in ObjectKanban.tsx is a guarded early-return, so the board degrades instead of breaking: with no lane key and no columns it renders an empty board; with bare-string columns it draws those lanes, titled by the raw strings; card moves are inert (persistCardMove and the move callback both open if (!groupBy) return). ⚠️ Every lane-less board holds zero cards — bucketCardsIntoColumns returns before distributing records when there is no lane key — so omitting groupBy is not a way to configure a board, it is a board that groups by nothing.

.changeset/9067-zod-barrel-named-arms.md

  • names packages/types/src/zod/index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    Two new exported symbols, both on packages/types/src/zod/index.zod.ts:

.changeset/9092-inline-locale-declared-face.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    • AppComponentSchema.label (app.ts) - ObjectGridSchema.label and .description (objectql.ts) - PageNodeSchema.aria.ariaLabel (layout.ts)

.changeset/9162-node-slot-guard-class-closure.md

  • names packages/types/src/zod/base.zod.ts → packages/types/src/zod/base.zod.ts — edited by this change

    What leaked. {schema.footer && ANGLE-BRACKETS(CardFooter)…ANGLE-BRACKETS(/CardFooter)} does not evaluate to false when the slot is falsy — it evaluates to the slot, and React renders numbers. A node slot's published zod face carries a z.number() arm (nodeUnionOptions, packages/types/src/zod/base.zod.ts), so footer: 0 is legal authored input, and it painted a stray 0 into the DOM. NaN painted three characters. The && also short-circuits, so renderChildren's own if (!children) return null first leg was never reached — which is why the objectui#8908 bridge repair could not cover any of these sites.

.changeset/9174-interpolate-fastpath-trim.md

  • names packages/components/src/renderers/layout/containers.tsx → packages/components/src/renderers/layout/containers.tsx — edited by this change

    At this change page:header's title/subtitle (and the record-title titleFormat) all went through interpolate() in packages/components/src/renderers/layout/containers.tsx. When the template contained a {token} the function collapsed and trimmed whitespace before returning; when it contained no { at all it returned the raw string untouched. A whitespace-only authored title (e.g. ' ') has no token, so it came back unchanged — truthy — and PageHeaderRenderer's {explicitTitle && ANGLE-BRACKETS(h1)} gate drew a blank h1. Because literalTitleText (page.tsx), which decides whether PageRenderer delegates its own heading to the authored header, already trimmed and correctly read "no title", PageRenderer also drew its own implicit heading — two ANGLE-BRACKETS(h1) elements on one document, the broken outline objectui#3434 closed, arriving through a different door.

.changeset/9280-record-highlights-entry-icon-retired.md

  • names packages/plugin-detail/src/index.tsx → packages/plugin-detail/src/index.tsx — edited by this change

    • packages/types/src/record-components.ts — RecordHighlightsComponentProps.fields[]'s object arm: { name; label?; icon?; type?; readonly? } → { name; label?; type?; readonly? }. The key is removed, not tombstoned: the contract's arm is $strict, so the refusal an author needs already exists upstream and arrives named (invalid_union at the entry). A ?: never tombstone buys nothing here — it is the remedy for a non-strict mirror that would otherwise strip in silence, which is not this arm. - packages/plugin-detail/src/renderers/record-highlights.tsx — the entry normalizer stops copying icon: f?.icon into the normalized entry. That read was unreachable, not merely unused: no author could feed it past the $strict arm, and HeaderHighlight renders no .icon on the far side either, so the copy had no consumer in either direction. - packages/plugin-detail/src/index.tsx — the registry manifest's fields input description sketched the entry as {name,label?,icon?,type?,readonly?} → {name,label?,type?,readonly?}. The inputs ARE the published contract (gen-manifest.ts serializes them into sdui.manifest.json and sdui-intrinsics.d.ts), so leaving the sketch standing would have gone on teaching AI and human authors a key that gets the whole document refused at publish.

.changeset/9309-object-gallery-filter-destination-typed.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    ObjectGallerySchema.filter is typed as the destination its own docblock names — QueryParams['$filter'] — on both faces, the TS interface in objectql.ts and the zod mirror in zod/objectql.zod.ts (objectui#9309).

.changeset/9511-record-id-is-a-string.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    The three authorable keys, each on BOTH faces. ObjectFormSchema.recordId (objectql.ts + zod/objectql.zod.ts), DetailViewSchema.resourceId (views.ts + zod/views.zod.ts) and DetailSchema.resourceId (crud.ts + zod/crud.zod.ts). ⚠️ The crud pair is DetailSchema, not DetailViewSchema, and it reaches the same renderer — not by symbol but by data flow: plugin-detail registers the 'detail' node type onto DetailView. A read that follows TypeScript symbols alone finds two keys and is incomplete.

.changeset/9549-tree-filter-declared.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    ObjectTreeSchema.filter is declared on both faces, in the shape objectui#9309 settled for ObjectGallerySchema.filter: QueryParams['$filter'] by indexed access on the TS interface in objectql.ts, and the same two-arm union (array first) on the zod mirror in zod/objectql.zod.ts (objectui#9549).

.changeset/9550-object-tree-root-barrel.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    ObjectQLComponentSchema declares the node types an ObjectQL block may be. Every one of its arms was a named export of this package's root barrel except ObjectTreeSchema, which was declared in objectql.ts, applied by the union, and re-exported by the ./zod barrel (objectui#7917) — while no TypeScript consumer could name it. There is no ./objectql subpath to reach around the barrel: the package's exports map is pinned by packages/types/src/__tests__/package-exports-manifest.test.ts, and the root barrel was the only route to this type.

.changeset/9606-object-kanban-card-title.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    Both published faces of the object-kanban arm now name the key: the zod mirror ObjectKanbanSchema in zod/objectql.zod.ts and its TypeScript twin, the ObjectKanbanSchema interface in objectql.ts. Both declare it OPTIONAL, at the same requiredness the other face uses, so the two faces accept and refuse the same documents. (Located and cited by SYMBOL: line addresses in zod/objectql.zod.ts have drifted before, and this change is itself about a drifted mirror.)

.changeset/9628-kanban-column-collapsed-honoured.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    The key was declared on both published faces of the object-kanban arm — the lane element of ObjectKanbanSchema (objectql.ts and its Zod mirror) and the runtime lane KanbanColumn (complex.ts and its mirror) — and read by KanbanEnhanced alone, a module no production source imports. An authored { "id": "todo", "title": "To Do", "collapsed": true } therefore parsed green on both faces and reached a board that did nothing with it: KanbanImpl's only collapse is the SWIMLANE row's, held in viewer state under objectui:kanban-collapsed:ANGLE-BRACKETS(swimlaneField) and never keyed to a lane's declared value. That is the ADR-0049 declared-but-unhonoured shape.

.changeset/9659-node-recursion-point-inert-clause.md

  • names zod/base.zod.ts → packages/types/src/zod/base.zod.ts — edited by this change

    What was there. defineNodeComponentUnion (zod/base.zod.ts) installed the component union into the node slot WRAPPED in a superRefine clause that re-issued the node-slot's own refusal under body for a chatbot node. spec(types): redirect the node recursion point from BaseSchemaCore to AnyComponentSchema — measured at 9 newly-refused documents, and it drops 118 phantom strict refusals #8344 needed it because ChatbotSchema.body was then a record — the one redeclaration across the arms that was WIDER than the base key it restated — so installing the union bare would have narrowed at 108 child slots and widened at one.

  • names zod/index.zod.ts → packages/types/src/zod/index.zod.ts — edited by this change

    Prose amended where this change falsified it, ⛔ not rewritten. zod/index.zod.ts said defineNodeComponentUnion "wraps it rather than replacing it" — true only while a wrapper existed. zod/base.zod.ts's note on the loose parameter bound still names ChatbotSchema's record body in the present tense; the paragraph is kept for the reason it records and carries an AMENDED note saying the exclusion set that pin reads is now empty.

.changeset/9722-bulk-executor-datasource-face.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    • ObjectGrid.tsx → RecordDetailPanel: removing it type-checks GREEN. It was paying for nothing — both sides already declare the same DataSource | undefined from @object-ui/types. Removed outright. - ObjectGrid.tsx → BulkActionDialog: removing it reddens, and it was paying for TWO things at once — the optional-vs-required arm (the grid declares dataSource?: DataSource, the dialog demands one) AND the bulk-door contravariance. Only the first still needs erasing, so it is now a non-null assertion: today's runtime behaviour is preserved exactly, and any future drift of the face reddens at that site instead of passing silently.

.changeset/9853-grid-non-positive-page-size.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    Refuse a non-positive pageSize at all three of ObjectGrid.tsx's read points, instead of giving two different answers for one authored value (objectui#9853).

.changeset/action-keys-warning-text-5642.md

  • names ActionRunner.ts → packages/core/src/actions/ActionRunner.ts — edited by this change

    Both halves of the message's tail had outlived the change they described. It told the author the key was warned about rather than rejected because ActionDef "still carries [key: string]: any" — objectstack#4075 step 3 deleted that index signature, and actionKeys.pin.test.ts pins the deletion in the opposite direction ({ ActionDef: false, ActionContext: true }), while actionDef-closed-surface.test.ts pins that tsc now rejects exactly such a key at the construction site. And it prescribed promoting the key to an explicit field on ActionDef "(packages/core/src/actions/actionKeys.ts)" — that file holds the INVENTORY (ACTION_DEF_KEYS); the interface is in ActionRunner.ts. The wrong pointer had teeth: an author who followed it edited the inventory alone, which is precisely the half-change the pin test reddens on, since it re-derives the inventory from the interface's AST.

.changeset/app-shell-page-header-subtitle-4761.md

  • names ObjectDataPage.tsx → packages/app-shell/src/views/ObjectDataPage.tsx — edited by this change

    | gauge | result | |---|---| | exports of dist/index.d.ts, through the TypeScript checker | 226 symbols; PageHeader and PageHeaderComponentProps are not among them (controls: AppShell reachable, a nonsense name not) | | exports map | declares exactly . and ./styles.css | | Node resolving @object-ui/app-shell/layout, …/dist/layout/PageHeader.js, …/src/layout/PageHeader.js | ERR_PACKAGE_PATH_NOT_EXPORTED for all three, while the declared entry resolves | | in-repo call sites | 2, both inside this package (ObjectView.tsx, ObjectDataPage.tsx) | | emitted declarations that change | dist/layout/PageHeader.d.ts only — dist/index.d.ts and dist/layout/index.d.ts are byte-identical across the change (8c886251…, f9f4862b…, both legs) |

.changeset/component-meta-converge-and-deprecate-alias-5893.md

  • names zod/base.zod.ts → packages/types/src/zod/base.zod.ts — edited by this change

    Either side had already moved. base.ts declared eleven keys; the plugin-scoped copy declared nine — the same nine, minus tags and description. So a plugin author typing against the plugin-facing declaration could not write two keys the main surface advertises, and which the runtime validator already accepted: ComponentMetaSchema in zod/base.zod.ts declares all eleven, so two of the three authorities agreed and the plugin-facing one did not. resizeConstraints' six members were identical in both, so the delta was exactly those two keys.

.changeset/console-form-container-specs-one-declaration-5596.md

  • names SchemaForm.tsx → packages/app-shell/src/views/metadata-admin/SchemaForm.tsx — edited by this change

    objectui#5542 converged the LEAF of this contract — the field spec — and left the two containers above it untouched, because converging them was a bigger call than a mechanical import. FormSectionSpec and FormViewSpec were each hand-declared twice under the same names, once in packages/app-shell's SchemaForm.tsx and once in apps/console's FormPage.tsx. Unlike the leaf — whose console copy was a clean subset — these two had already drifted, in both directions, so neither copy was a subset of the other and there were two live answers to "what may an author write":

.changeset/console-form-field-spec-one-declaration-5542.md

  • names views/metadata-admin/form-spec.ts → packages/app-shell/src/views/metadata-admin/form-spec.ts — edited by this change

    objectui#5040 was not a missing key. It was that two hand-written descriptions of one contract drifted, and nothing could notice, because each was only ever checked against itself. PR metadata-admin: FormFieldSpec declares dependsOn, one declaration for both halves #5537 converged the two app-shell descriptions into views/metadata-admin/form-spec.ts. A third survived in apps/console: FormPage.tsx declared its own nine-key interface FormFieldSpec, under the same name, in a different package — so the same failure mode stayed fully available.

  • names form-spec.ts → packages/app-shell/src/views/metadata-admin/form-spec.ts — edited by this change

    @object-ui/app-shell therefore re-exports FormFieldSpec from its package root (type-only, erased at build — nothing is added to the bundle), and FormPage.tsx imports it and deletes the local declaration. Reachability is the load-bearing half: a type that cannot be imported is a type that gets retyped, and retyped copies drift. form-spec.ts itself is untouched.

.changeset/console-formpage-visible-predicates-5594.md

.changeset/formfieldspec-dependson-5040.md

  • names views/metadata-admin/form-spec.ts → packages/app-shell/src/views/metadata-admin/form-spec.ts — edited by this change

    No runtime behaviour changes: MetadataField already handed dependsOn through and both widgets already read it. What changes is the type face — it now admits what the runtime has always accepted. The two descriptions are one declaration, extracted to a leaf module (views/metadata-admin/form-spec.ts) that both halves import, because SchemaForm.tsx imports ./widgets.js and a back-edge would close a cycle. SchemaForm.tsx re-exports FormFieldSpec and VisibilityPredicate, so every existing importer is unaffected.

  • names SchemaForm.tsx → packages/app-shell/src/views/metadata-admin/SchemaForm.tsx — edited by this change

    No runtime behaviour changes: MetadataField already handed dependsOn through and both widgets already read it. What changes is the type face — it now admits what the runtime has always accepted. The two descriptions are one declaration, extracted to a leaf module (views/metadata-admin/form-spec.ts) that both halves import, because SchemaForm.tsx imports ./widgets.js and a back-edge would close a cycle. SchemaForm.tsx re-exports FormFieldSpec and VisibilityPredicate, so every existing importer is unaffected.

.changeset/list-user-actions-collision-5398.md

  • names ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    The harvest now reads the object block only. Both userActions read sites in ListView.tsx carry a comment naming the collision, and __tests__/ListView.userActionsCollision.test.tsx pins each clause of it: the two shapes, a producer that manufactures the view one, the harvest's blindness to it, and the projection that must keep the object's operand with a toolbar block — or an empty block — present on the view.

.changeset/listview-comment-pair-4559.md

  • names ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    Two comment corrections in ListView.tsx (objectui#4559, objectui#4966). No runtime behaviour changes and the emitted bundle is byte-identical; the published .d.ts does change, which is why this is a patch rather than an empty frontmatter.

.changeset/objectchart-fieldoptionlabel-ref-5587.md

  • names useObjectLabel.ts → packages/i18n/src/useObjectLabel.ts — edited by this change

    Pinned by ObjectChart.fieldOptionLabelRefetch.test.tsx, which counts fetches across forced re-renders both outside and inside a provider. Reverting useObjectLabel.ts to its pre-objectui#5564 state turns the no-provider case red (2 fetches instead of 1, alongside React's "Maximum update depth exceeded"), so the removal is pinned to the fix that unlocked it rather than to a comment.

.changeset/objectpivottable-i18n-resolver-refs-5625.md

  • names useObjectLabel.ts → packages/i18n/src/useObjectLabel.ts — edited by this change

    Pinned by ObjectPivotTable.i18nResolverDeps.test.tsx, which counts derivations across forced re-renders both outside and inside a provider, checks that the two derived state maps settle, and asserts the language-switch re-derivation. Reverting useObjectLabel.ts to its pre-objectui#5585 state turns the no-provider case red (derivations in the dozens instead of 1), so the removal is pinned to the fix that unlocked it rather than to a comment.

.changeset/plugin-form-readme-classname-quantifier-5131.md

  • names packages/components/src/renderers/form/form.tsx → packages/components/src/renderers/form/form.tsx — edited by this change

    README.md's "Not a FormField key" table said a field-level className is "read on exactly one pseudo-field, type: 'section-divider'". That quantifier holds only for the renderer's explicit read — className={fp.className} on the section-divider branch of packages/components/src/renderers/form/form.tsx. The same renderer forwards every key it did not destructure, and className is not among the names taken off the field config, not among the ones stripRendererOnlyProps removes, and so rides {...fieldProps} into renderFieldComponent, whose built-in input branch spreads it onto ANGLE-BRACKETS(Input). A field-level className therefore lands visibly on ordinary built-in controls, and a reader taking "exactly one" literally concludes the opposite of what the code does (objectui#5131).

.changeset/publish-envelope-one-spelling-6962.md

  • names metadata-client.ts → packages/data-objectstack/src/metadata-client.ts — edited by this change

    The two methods sit ~250 lines apart in metadata-client.ts and both POST /api/v1/meta/:type/:name/publish. publishDraft tolerated a dispatcher-shaped envelope and returned the inner object; publish returned the body as parsed. Nothing said which was right, and the card explicitly refused to settle it from PublishMetaItemResponseSchema alone — an inference from a declaration is not a measurement of the server.

.changeset/record-alert-cta-label-i18n-4998.md

.changeset/record-picker-label-placeholder-i18n-5637.md

  • names renderers/layout/containers.tsx → packages/components/src/renderers/layout/containers.tsx — edited by this change

    KNOWN GAP, unchanged by this release: the sibling label read sites in renderers/layout/containers.tsx compose translateLabel(pickLocalized(…), language), and that second helper is not applied here — translateLabel and its KNOWN_LABEL_DICT are module-private to that file. Only the locale-map resolution lands in this change; a plain-English string label is still rendered verbatim in every language, exactly as before.

.changeset/retire-components-action-param-dialog-5685.md

  • names src/views/ActionParamDialog.tsx → packages/app-shell/src/views/ActionParamDialog.tsx — edited by this change

    • import { ActionParamDialog } from '@object-ui/components' — no drop-in replacement is published. The surviving implementation is @object-ui/app-shell's ActionParamDialog (src/views/ActionParamDialog.tsx), rendered by app-shell's action runtime (useConsoleActionRuntime, RecordDetailView) rather than exported standalone. A host that needs its own param form builds on @object-ui/fields' shared field widgets (resolveFormWidgetType / getLazyFieldWidget, ADR-0059) — the same seam the surviving dialog renders through.

.changeset/text-input-description-aria-describedby-5735.md

  • names renderers/form/form.tsx → packages/components/src/renderers/form/form.tsx — edited by this change

    Before this the paragraph and the input were siblings with no programmatic relationship: a screen reader moving to the field announced the label and the value and never the helper text. The label half of the same block was already wired (htmlFor against the input's id), which is what made the gap specific to description rather than a general absence of a11y wiring — and the identical key authored on a field INSIDE renderers/form/form.tsx has been announced all along, so one authoring key behaved two ways depending on which container the author reached for. It no longer does.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

Compared the checked-out tree with 462c85868 (merge-base with origin/main): 48 file(s) changed outside .changeset/, read against 1667 pending declaration(s) that publish a body (2264 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3102.9 KB 3104.5 KB
Main entry chunk (gzip) 149.4 KB 350 KB
Entry file index-CAkxvQ_8.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) 558.56KB 133.87KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.62KB 63.16KB
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.35KB 9.18KB
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.53KB 4.89KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
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) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.08KB 46.94KB
plugin-dashboard (index.js) 137.82KB 36.70KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.49KB 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.15KB 9.14KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 89.15KB 22.35KB
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.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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c292a64004ab1fdbc6f2bd89cfa4340c8cda863b

Isolated adversarial re-derivation of PR objectui#10962 (card objectui#10803, batch 7; claim 5867587761, amendment 5860244997, release 5866922219, triage 5858031244 item 3, ruling 5861900779). Read: the card and all 33 comments, PR objectui#10914 and its review 5865956027, this PR's body, its 74-file diff (git diff origin/main..., +157 / −115, file set equal to REST), the head's check-runs, the trees at the head, the branch point 3b469c8ea and origin/main deca847a8 (moved past the body's 5c94589f0), and objectstack over REST (the local objectstack checkout is shallow, so every objectstack sha was read as commits/SHA plus compare/SHA...main). The dev report 5870507620 was treated as a claim; every figure below is my own measurement unless marked REASONED.

CI on the head: 43 check-runs, 40 success, 3 skipped (the two coverage legs, dependabot), 0 failed, 0 in progress; Spec Main Shape Gate success. REST mergeable_state: clean, mergeable true. Draft, assigned os-steve, one commit whose trailers are Co-Authored-By: Claude and the session URL; a model-name scan of the message finds none.

① Derived judgments

Premise, re-read over REST. All 30 numbers answer 404 on /issues/N AND on /pulls/N (30 of 30 each). Lit controls in the same run: objectstack#3391, #3546, #3720 answer 200 as issues; pulls/13267, pulls/12718, pulls/16920 answer 200. Nothing dead is left, and nothing beyond the 30 was taken out: every distinct objectstack#N in the two in-scope classes at the head (343 numbers) was read once as an issue: 342 answer 200, one answers 301 (objectstack#14026, the transferred card batch 6 re-qualified), 0 answer 404. The objectstack#N set in the diff's removed lines less its added lines is exactly the 30; the added-only set is objectstack#3391 and objectstack#3546.

Census, re-run as the body prints it. The 30-number instrument: 82 at 3b469c8ea (26 changeset lines in 24 files, 56 src lines in 39 files), 82 at origin/main deca847a8, 0 at the head, 0 on git merge-tree --write-tree origin/main head (clean, tree 1d4092394). Bare #3391: 28 in 10 files at the branch point and on main, 0 at the head and on the trial merge. Lit controls: objectstack#10856 4 at the head (4 at the base); objectstack#3391 32 at the head against 3 at the base. Out of scope at the head, whole tree: the 30 in tests 84 / 47, scripts 1 / 1, apps/console 2 / 2, READMEs 2 / 2 (auth, react), CHANGELOG.md 11 / 8, .github 0, content/docs 0; bare #3391 in tests 12 / 9 and CHANGELOG.md 23 / 8. Every one of these equals the body.

Shas. All 27 objectstack shas the diff and body name (the 24 added; c459da6bc, 89448a52b, 9bd4344e4 already in the sentences; 859ded3ec, 27358d517, f415bcf18, 66e266c93 in the body) exist and answer compare/SHA...main with behind_by 0, i.e. each is an ancestor of objectstack main; the 9-character spellings are the ones REST returns. The two objectui shas, 7a197e7c5 (added) and 78cbdb530 (pre-existing context), are ancestors of origin/main (merge-base --is-ancestor exit 0), and 7a197e7c5's subject is "SchemaForm reads the canonical visibleWhen, reviving every metadata-form predicate (objectstack#6331)", which is what the SchemaForm.tsx sentence says.

Mapping, all 30 rows read against the landing commit's message and, where the sentence quotes something, its patch:

Cross-check of the body's cross-check: objectstack's own sweep anchors #5970 → 97e7e3caa, #6483 → ee58392e1, #10485 → 35ad101bc, #11330 → a9ee98992, #11846 → 0c2334f6c, #17147 → aaacf1d5c (in 21ab41041), #9934 → 79c46da90 (5cf58eb16) and #12868 → c459da6bc (f415bcf18): the same eight commits.

#3391. All 28 rewritten lines read in context: each names the server's effective API operation set, /me/permissions apiOperations, or the 405 import refusal (OBJECT_API_METHOD_NOT_ALLOWED). objectstack#3391 (200, closed) is "跟踪:UI 操作按钮与 apiMethods 白名单一致性契约落地" and its body defines exactly that: the effective operation set from the apiMethods whitelist, 405 for a tightened primitive, "前端只渲染下发的 effective 结果", import / export derived. objectui#3391 is the record-header {id} api-action bug; unrelated. The two #3391/#3546 lines (managedBy.ts isObjectInlineEditable doc, ObjectGrid.tsx inline-edit gate): objectstack#3546 (200, closed) is "detail/form 面的 edit/delete 按钮接入服务端 effective 操作集", "#3391 的独立 follow-up ③", i.e. the inline-edit gate those lines describe; objectui#3546 is the missing-i18n-keys card. Both halves are right.

Prose-only. More than 40 changed sentences read in their before / after pairs (every changeset hunk and every source hunk in the diff); none asserts more than its citation carries. Frontmatter of all 24 edited changesets md5-identical at 3b469c8ea and the head (24 of 24); every one of the 24 is present in .changeset/ on origin/main. My own C4 instrument (createSourceFile plus createPrinter({ removeComments: true }), TypeScript 6 from /opt/node22, over the 48 touched .ts / .tsx at the base and the head): 46 prints identical; activityItemType.ts and recordActivityFeed.ts differ by exactly one printed line each, the concatenation fragment that loses objectstack#11507, ; 0 parse diagnostics; lit controls: re-spacing a comment leaves the print unchanged, editing a string moves it.

The two runtime warnings. Before: '… is author-extensible (objectstack#11507, ruled ' + '2026-08-24) …' (app-shell) and '… is author-extensible (objectstack#11507, ' + 'ruled 2026-08-24) …' (plugin-detail). After: '… is author-extensible (ruled ' + '2026-08-24) …' and '… is author-extensible (' + 'ruled 2026-08-24) …'. Rendered, both go from "author-extensible (objectstack#11507, ruled 2026-08-24)" to "author-extensible (ruled 2026-08-24)"; nothing else in either message moves. warnUnmappedActivityType is guarded by a module-level Set (once per value) and warnUnknownActivityType by warnOnce; neither guard moves. No test pins either string: objectstack#11507 occurs in 11 test lines at the head, all doc comments or the tests' own titles; author-extensible ( in tests only in 4 comment lines; no assertion reads either message with the pointer, no test file is in the diff, and the eight test shards are green on this head.

The ObjectCalendar.tsx wording change is true: objectui#8652 is open with labels pm:on-hold, priority:p2, domain:spec (measured), so "is pm:blocked" would have been false; "wait on this" is the verb e233db9db's Downstream note uses for that card.

② Semver level

  • .changeset/10803-seventh-batch-runtime-strings.md, '@object-ui/app-shell': patch and '@object-ui/plugin-detail': patch: right for two console-warning strings, the level batches 3 to 6 declared for the same class, and Q1 = A requires the drop rather than a sha. Every sentence in it is true as measured: app-shell warns once per value when a sys_activity row's type maps to no activity item type; plugin-detail's [record:activity] block does the same for feed item types; both said "author-extensible" with a 404 pointer beside the ruling's date; both now read "author-extensible (ruled 2026-08-24)"; nothing else in either message moves, and when and how often each fires is unchanged.
  • .changeset/10803-dead-citation-sweep-seventh-batch.md, empty frontmatter: right for comment-only edits. Its 17 named packages equal the 17 packages/* roots in the diff; "none of these comment edits moves a claim or changes a code or type token" is the C4 result; the frontmatter sentence is (①). One wording slip: it says the two warnings are "declared separately below", but they are declared in a separate file (batch 6's sweep named its file); since an empty changeset publishes nowhere, harmless.
  • check-changeset-presence on the head, run in a --shared clone of the head against --base 3b469c8ea: exit 0, "48 source file(s) of 17 released package(s) changed, and this change declares 2 changeset(s)", the body's verdict. No other package needs a changeset.
  • No edited changeset declares major; the 24 edited bodies stay grammatical and true.

③ Boundary flags

Implemented-by: claude/issue-10803-dead-citations-seventh-batch
Reviewed-by: session_01DuWo5bdP9SdVebamn99GGk

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 28, 2026 13:36
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 28, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 28, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Queue ejection disposition (2026-09-28T13:53Z), domain:ui seat #1 (session_01DuWo5bdP9SdVebamn99GGk). ⛔ Not re-queued yet.

The failing check is Spec Main Shape Gate, and the break is not this PR's. Both merge-group runs for this PR failed it: 68d615d6b (check run 108952923258) and 4313e09e7 (check run 108953913782). The annotations name only packages/types/src/__tests__/p1-spec-alignment.test.ts: TS2339: Property 'isDefault' does not exist on type 'never' and a TS2322, compiled against @objectstack/spec built from objectstack 6e3e5462c6d1.

The fix: objectui#10987, claimed as a stop-the-bleed by domain:ui seat 2 under the maintainer's standing rule. No fix exists on main yet, so none is ported here: porting a partial fix into a citation-only PR would widen it.

Next step: once objectui#10987 lands on main, this PR is re-queued (automerge_enable). Its own head c292a6400 is green, because its gate ran before the spec break, and its review 5870922323 and ACCEPT stand.


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

Development

Successfully merging this pull request may close these issues.

2 participants