Skip to content

chore(objectui): bump the console pin to ab1879721595 (carries objectui f624f278, b0bf413c and ab187972) - #21625

Merged
objectstack-fleet[bot] merged 12 commits into
mainfrom
claude/issue-21616-objectui-pin-bump
Oct 3, 2026
Merged

objectstack-fleet[bot] merged 12 commits into
mainfrom
claude/issue-21616-objectui-pin-bump

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #21616
Clause-②: no

Moves the bundled Console's objectui pin from 89cad75d5570 (#21380) to objectui main at claim time, ab187972159583b595facdcae3c73b50f6f312e9, so the Console carries objectui#11553, objectui#11544 and objectui#11562, and objectui's own move onto @objectstack/* 17.6.0. Unblocked by this, once it lands: #12438 (B1's re-run), #21464 (the held object-grid columns member). #21596's run reads whatever console this pin builds.

Range

89cad75d5570..ab1879721595: 65 non-merge commits, 119 changesets added (one no longer present at the tip, read from the commit that added it), 111 releasing (43 declared breaking, all by the author's annotation), 8 release-nothing, 1 commit without a changeset (6903eafbc, a docs example). git merge-base --is-ancestor exits 0 against objectui origin/main for the old pin and for every commit named below; ab1879721595 is origin/main at the claim-time read.

The landings this bump carries

objectui landing merge commit who waits here
objectui#11553 — Studio reaches the organization's package-less flows at /studio/~org/automations (PR objectui#11565) f624f278d77e #12438 B1's re-run
objectui#11544 — a grouped grid's group headers read the object field's options only, never a column's (PR objectui#11563) b0bf413cac21 #21464's held object-grid columns member
objectui#11562 — record:details edit mode edits a textarea field in a multi-line textarea, so a save keeps its line breaks (PR objectui#11567) ab1879721595 (the pin itself) the console this repo ships
objectui#11438 — objectui resolves @objectstack/* 17.6.0 and follows its mechanical contract moves (PR objectui#11531) 6158e4c93f0b none; carried by the range

What changed here, and why

  • .objectui-sha and .changeset/console-ab1879721595.md, written by scripts/bump-objectui.sh ab187972159583b595facdcae3c73b50f6f312e9 --no-commit (auto level minor). The explicit sha is passed because the shared ../objectui checkout sits on another branch. The script's adr-0087: TODO placeholder is answered not-required (no-migration-prescription), in the wording the previous bumps used, naming all 43 declared-breaking entries (26 commits). Each is objectui's own surface: its validators, layout nodes, node-type keys, CLI, designer and drill-report types. Where an entry narrows toward an ObjectStack shape, ObjectStack already declares that shape: the { condition, style } rule, a dataset-bound dashboard widget, deleteBehavior, percentScaleOf. Measured: none of the 22 retired or narrowed node keys probed is a ComponentPropsMap row (56 rows) or a PageComponentType member (31).
  • packages/sdui-parser/objectui-lockstep.json, re-recorded with pnpm gen:sdui-lockstep against the build worktree at the pin (OBJECTUI_ROOT=.cache/objectui-ab1879721595; the generator correctly refused the off-pin shared checkout). It reads 214 grammar lines (blob 0131f27cf86d), 25 codes and containment predicate 76c18fb95d1f, all unchanged, so no port is owed.
  • sdui.manifest.json + scripts/sdui-manifest.record.json, from node scripts/gen-sdui-manifest-node.mjs over the tree pnpm objectui:build built at the pin. It has 107 components, none added or removed. Fourteen changed their published inputs:
    • grid drops smColumns / mdColumns / lgColumns / xlColumns, and its columns and gap become value sets. stack / flex gap and container padding become value sets too.
    • page:header drops the inert breadcrumb.
    • object-grid gains description, emptyState and keyboardNavigation. object-pivot gains drillDown, and record:line_items gains ten inputs.
    • Descriptions changed on object-grid / object-kanban conditionalFormatting, action:button / action:icon successMessage, and the fields input of the three form blocks (object-form, embeddable-form, object-master-detail-form).
  • The 49 asserting pin citations in packages/spec/src (check:objectui-pin-citations reds on any pin move) were re-measured at the new pin, not restamped. Each anchor was mapped through its cited file's diff and re-read. Block hashes and corpus counts were re-taken with a method that first reproduced every 89cad75d5 number: the selection block still hashes to c88443302d40…, and the corpus is 10071 files with objectstack 16044 and @objectstack/spec 6461 at the old pin, against 10267, 16377 and 6665 here. This regenerates packages/spec/src/migrations/registry.ts and content/docs/references/ui/view.mdx. It adds .changeset/objectui-pin-citations-ab1879721595.md (@objectstack/spec patch), because the FormField.span describe and six migration descriptions name the pin.
  • Records whose claim moved, not only their numbers:
    • The four action:* rows. objectui#11344 now forwards outcomeMessages to the runner. The action:button / action:icon rows record it as a key the renderer forwards and the row does not declare: it arrives on the action:bar member path, and objectui leaves the input unpublished until the row declares it. The action:group / action:menu rows record it riding each member's forward.
    • The object-tree paragraph now cites the regenerated manifest record.
    • Patch round, after the contract review: object-grid keyboardNavigation. The grid reads the key at this pin (objectui#11068, 154075ab1: ObjectGrid.tsx:5458 hands schema.keyboardNavigation ?? inlineEditable to data-table.tsx, which makes the cells one roving Tab stop). So the describe drops [EXPERIMENTAL — not enforced] and its "no renderer reads it" sentence, and states the measured behaviour. The member docblock and the block docblock's [finding(spec): ComponentPropsMap['object-grid'] (ObjectGridPropsSchema) declares neither description nor emptyState, which objectui's ObjectGrid reads once objectui#11130 lands #20694] paragraph record the read and keep the db11afd4967c measurement as history. content/docs/references/ui/component.mdx is regenerated.
    • The same sweep found two more records of the same class:
      • object-grid emptyState's record called the unresolved locale map "a renderer gap". objectui#11227 (6158e4c93) ends that gap: ObjectGrid.tsx:6468-6469 resolves title / message against the display locale.
      • ActionSchema.outcomeMessages was planned, with an author warning that the console does not show outcome copy yet. The console reader is objectui#11344 (c476be0e0, ActionRunner.composeSuccessMessage), so the row is live, as it prescribed for this commit. This adds packages/spec/liveness/action.json and its count shard liveness/state-counts/action.md to the diff, and the successMessage row's note records the interpolation.
  • Every other anchor held on a byte-identical file or moved with its cited text byte-identical. That includes the three quoted-first-line anchors in ui/view.zod.ts that redded: ObjectView.tsx 2293 -> 2299, objectql.zod.ts 2145 -> 2370 (still .strict()) and 1160 -> 1419 (still without map). No key, default, enum member or export moves.
  • No example, test or gate needed adapting for the breaking entries or for objectui#11438's behaviour changes. Measured on this branch:
    • The showcase JSX pages use flex gaps 1, 2, 3, 4, 6 and 8 and grid gap 5 with columns 3 and 4, all inside the new sets.
    • No example names a retired grid *Columns key or any retired node key (0 git grep hits).
    • No example dashboard widget uses the dataset-less inline-object form, which the spec refuses without dataset anyway.
    • All three example percent fields declare max: 100, so objectui#11475's storage-driven scaling reads them as whole percents.
    • No example uses is_empty / $empty on a value provider.

Browser verification (examples/app-showcase, this branch)

pnpm dev -- --fresh --ui --no-watch -p 41737 ran at 87d7c51424, before the merge, with its own ephemeral DB and the seeded admin. The four main commits merged afterwards touch no console asset. check:console-sha reports objectui@ab1879721595. The run was driven in headless Chromium (/opt/pw-browsers/chromium) with every API response captured.

carried fix drive result at ab1879721595
objectui#11553 POST /api/v1/automation/showcase_task_completed/clone { name: 'pinprobe_flow_clone', label: 'Pinprobe flow clone' }, then open /_console/studio/~org/automations clone 200. GET /api/v1/meta/flow/pinprobe_flow_clone 200, _packageId null. The page header reads "Organization flows · Automations". The rail lists "Pinprobe flow clone" and the canvas opens it (?surface=flow:pinprobe_flow_clone, status draft). Studio's home lists "NOT IN A PACKAGE · Organization flows" beside "No writable packages yet"
objectui#11544 Tasks list → Group → Add group field → Status five headers, keyed backlog / done / in_progress / in_review / todo, labelled from the object field's options: Backlog, Done, In Progress, In Review, To Do. Each counts 2, matching POST /api/v1/data/showcase_task/query (groupBy: ['status'], 5 records). The retired column-options read cannot be authored (the strict ListColumnSchema refuses options), so there is no pre-fix leg to contrast
objectui#11562 PATCH a task's notes to "Call back Monday.\n\nBudget approved, needs legal review.\n", open its record page, double-click Notes, append "Signed off.", Save the editor is a TEXTAREA whose value carries both line breaks. The stored value after the save is "Call back Monday.\n\nBudget approved, needs legal review.\nSigned off."

Smoke: the /_console/home launcher, Capability Map, Command Center (大屏), the JSX Command Center (grid columns={4} gap={5}), the Delivery Operations dashboard (KPI tiles and charts drawn), Chart Gallery, Component Gallery and Task Board. 0 page errors. Non-2xx responses were only 401 /auth/get-session before sign-in, 404 /usage/storage and one 404 ?state=draft no-draft probe. Only the PIDs this run started were stopped.

Gates and tests run locally, at 4fc15b7e67 (sync round), 5eadf35375 (patch round) and b93b5fec00

4fc15b7e67 is the head after merging origin/main 5b5e83f446 with scripts/pm/os-regen-merge.sh. The merge commit 501902fb09 had no conflict hunk. component.zod.ts and migrations/registry.ts auto-merged, and both sides' added and removed lines are intact, compared line for line against each side's own diff. The os-regen driver kept this branch's side of content/docs/references/ui/component.mdx, dropping main's object-metric aggregate and trend rows (3f1bc816a2). check:generated --fix regenerated it in 4fc15b7e67, and it now differs from main only by this PR's keyboardNavigation row. The PR's own contribution, its diff against origin/main, is the same 25 paths with the same added and removed lines as at 5eadf35375.

At 4fc15b7e67, dispatch-gates --commands derived the same 128 commands from the same 25-path diff, and all 128 exited 0. --ran reports 128 derived, 128 run, 0 NOT-MEASURED. These also exited 0:

  • check:objectui-pin-citations --verify-anchors (54 asserting citations match ab1879721; 7 content assertions verified)
  • check:authorable-surface, check:migration-registry (registry.ts current: 360 semantic entries, with main's ui-object-metric-aggregate-trend-typed), check:generated (all 15 artifacts current), check:docs and check:liveness
  • @objectstack/spec test (609 files, 18057 passed, 1 todo) and typecheck
  • eslint on the 15 changed TS files (0 errors, 0 warnings)

At 5eadf35375, dispatch-gates --commands derived 128 commands from the 25-path diff (the 127 below plus check:platform-checklist), and all 128 exited 0. --ran reports 128 derived, 128 run, 0 NOT-MEASURED. These also exited 0:

  • check:objectui-pin-citations --verify-anchors (54 asserting citations match ab1879721; 7 content assertions verified)
  • check:generated and check:docs (component.mdx regenerated by check:generated --fix)
  • check:liveness (after gen:liveness-counts)
  • @objectstack/spec test (608 files, 18017 passed, 1 todo) and typecheck
  • @objectstack/lint lint-liveness-properties.test.ts (95 passed)
  • eslint on the 15 changed TS files (0 errors, 0 warnings)

b93b5fec00 is the head after merging origin/main f97660cdd6 with scripts/pm/os-regen-merge.sh. The regenerated registry.ts is byte-identical to the merge's and keeps main's new element-text-variant-heading-subheading-retired entry.

  • node scripts/pm/dispatch-gates.mjs --commands derived 127 commands, the same set before and after the merge. All 127 exited 0 at this head, and the --ran reconciliation reports 127 derived, 127 run, 0 NOT-MEASURED, every exit recorded. On the pre-merge pass at 87d7c51424, check:skill-examples and check:dual-build-cjs-loads first exited 3 (PREREQUISITE NOT MET: no dist/) and exited 0 after pnpm build.
  • Named by the dispatch, all exit 0:
    • check-adr-0087-registration --base origin/main, check-changeset-no-major --base origin/main
    • check:console-sha, check:console-injection, check:sdui-lockstep, check-sdui-manifest
    • check:objectui-pin-citations --verify-anchors (49 asserting citations match ab1879721; 7 content assertions verified)
    • check:migration-registry, check:objectui-bump
    • @objectstack/spec check:generated (after check:generated --fix regenerated content/docs/references/ui/view.mdx)
  • The console build passed its single-zod canary (one {major:4,minor:6,patch:5} literal) and its spec-injection check.
  • pnpm build exited 0. pnpm --filter @objectstack/spec test: 608 files, 18017 passed, 1 todo. pnpm --filter @objectstack/sdui-parser test: 218 passed. pnpm --filter @objectstack/spec typecheck: exit 0.
  • eslint --no-inline-config was run on the 13 changed TS files: 0 errors, 0 warnings. The population is **/*.{ts,…} (eslint.config.mjs:971), and the config enables no type-aware linting (:327-328), so the diff cannot move a judgment on an untouched file. Repo-wide pnpm lint is left to CI.

Environment: Node 22.22.0 sufficed. At this pin objectui locks jsdom 29.1.1 (engines ^22.13.0), so no newer Node was needed.

Acceptance notes

  • objectui, measured in the browser run above, not touched here: switching the list's Group field leaves stale "(empty)" groups stuck on "Loading grid…". The server answers only the real groups. Title (10 groups), then Priority, showed 13 groups (9 phantom + 4); then Status 8 (3 + 5); then Priority 8 (4 + 4). useServerGrouping.ts and useGroupedData.ts are byte-identical at 89cad75d5570 and ab1879721595, so this hop does not reach them. Not measured in a browser at the old pin.
  • object-kanban's conditionalFormatting is still z.unknown() in this spec, while objectui#11522 now declares only the { condition, style } rule on that block. This is the same typing-by-reference move spec(ui): the ComponentPropsMap rows still type renderer-read members as z.unknown() — navigation on object-map / object-gantt / object-tree and conditionalFormatting on object-kanban accept 42 — the family close-out after #21445 #21464 makes for its object-block members.
  • The action:button / action:icon rows in ui/component.zod.ts still do not declare outcomeMessages, which objectui now forwards. Declaring it is a contract decision, recorded on the rows and not made here.
  • conversions/registry.ts' PAGE_HEADER_COMPONENT_TYPES docblock still calls page-header objectui's legacy alias. objectui retired that node key in this range (ad1785c1d). The conversion itself is unaffected.

Generated by Claude Code

claude added 8 commits October 3, 2026 18:04
…bjectui.sh --no-commit output)

Claude-Session: https://claude.ai/code/session_01HRYqpqGcWpJuJkDmbRF75w
Co-authored-by: Claude <noreply@anthropic.com>
…kstep at ab1879721595; re-measure more pin citations (wip)

Claude-Session: https://claude.ai/code/session_01HRYqpqGcWpJuJkDmbRF75w
Co-authored-by: Claude <noreply@anthropic.com>
…d.span describe (wip)

Claude-Session: https://claude.ai/code/session_01HRYqpqGcWpJuJkDmbRF75w
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/sdui-parser, @objectstack/spec, touching 14 documentable anchor(s). ⚠️ 4 changed file(s) yielded no anchor (packages/sdui-parser/objectui-lockstep.json, packages/spec/liveness/action.json, packages/spec/liveness/state-counts/action.md, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/data-modeling/analytics.mdx (via DatasetMeasureSchema (symbol, a top-level const))
  • content/docs/protocol/objectui/layout-dsl.mdx (via ComponentPropsMap (symbol, a top-level const object))

⛔ 5 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v15.mdx (via PageTabsProps (symbol, a top-level const object))
  • content/docs/releases/v17/17-1.mdx (via ComponentPropsMap (symbol, a top-level const object))
  • content/docs/releases/v17/17-3.mdx (via ComponentPropsMap (symbol, a top-level const object))
  • content/docs/releases/v17/17-4.mdx (via ComponentPropsMap (symbol, a top-level const object))
  • content/docs/releases/v17/17-5.mdx (via ComponentPropsMap (symbol, a top-level const object))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 4 changed file(s) yielded no anchor (packages/sdui-parser/objectui-lockstep.json, packages/spec/liveness/action.json, packages/spec/liveness/state-counts/action.md, …) — pages documenting those are invisible to this run
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 139 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 5b5e83f446bde0bf6e13db304b9f07f115635704 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from a46cc222b2bb8a65baf6756259de4d63bdb3df2d — the merge of head 4fc15b7e67f4d4f25e3886058e3eb507b2947bbc into base 5b5e83f446bde0bf6e13db304b9f07f115635704, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin a46cc222b2bb8a65baf6756259de4d63bdb3df2d && git checkout a46cc222b2bb8a65baf6756259de4d63bdb3df2d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5b5e83f446bde0bf6e13db304b9f07f115635704 4fc15b7e67f4d4f25e3886058e3eb507b2947bbc && git checkout -B drift-repro 5b5e83f446bde0bf6e13db304b9f07f115635704 && git merge --no-ff 4fc15b7e67f4d4f25e3886058e3eb507b2947bbc

node scripts/docs-audit/affected-docs.mjs --json 5b5e83f446bde0bf6e13db304b9f07f115635704

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 5b5e83f446bde0bf6e13db304b9f07f115635704 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: b93b5fec0089f9a5b5ac0123fd359cb13dc7c1a4
Local-runs: none

① Derived judgments

Inputs: card #21616 (body, claim 5971910197, os-dev-report 5972904218), PR #21625 (body, the 20-file list, net diff f97660cdd6...b93b5fec00), and the 35 check-runs on the head: 34 success, 1 skipped (Packed-tarball smoke (opt-in)), none failing or pending. Those conclusions are the gate verdicts; nothing was re-run.

  • Spec accept set: right. Across the eleven non-test packages/spec/src/** files, the only changed line outside a docblock, a // comment or a migration-entry text string is the FormField.span .describe() in ui/view.zod.ts. In that describe the pin moves from 89cad75d5570 to ab1879721595. The six migration entries and their registry.ts mirror change only text: the pin sha and the re-taken corpus counts. No key, default, enum member, export or refusal moves, so the spec changeset's claim holds. registry.ts's net diff is exactly those six hunks, so main's entries survived the os-regen-merge.sh merge. content/docs/references/ui/view.mdx is the generated twin of the span describe. Both test files change in comments only.
  • Console surface through .objectui-sha: right. The regenerated sdui.manifest.json has 107 components, none added or removed, and 14 with changed inputs. That matches the PR body.
    • Widened inputs: object-grid gains description, emptyState and keyboardNavigation; record:line_items gains ten keys, parentObject through limit; object-pivot gains drillDown.
      • Every one of these that sits on a ComponentPropsMap row is already declared on the spec row.
      • object-pivot has no row.
      • So the CLI JSX gate's console-fallback accept set rises to meet the accept set the spec already publishes. It does not open a new one.
    • Narrowed inputs:
      • grid drops smColumns, mdColumns, lgColumns and xlColumns.
      • Value sets replace free numbers on grid columns, on grid/flex/stack gap and on container padding.
      • page:header drops breadcrumb, which the spec already tombstones (retiredKey).
      • The layout nodes are objectui's own and have no row.
      • The showcase values the body measured are inside the new sets: flex gap 1/2/3/4/6/8, grid gap 5, columns 3 and 4.
    • Description-only changes:
      • object-grid/object-kanban conditionalFormatting.
      • action:button/action:icon successMessage.
      • fields on the three form blocks.
    • Prose slip, non-blocking: the body says "the four form blocks". The manifest has three (object-form, embeddable-form, object-master-detail-form); view:form is not a manifest component. The total of 14 is correct.
  • ADR-0087 disposition on .changeset/console-ab1879721595.md: right. It answers not-required (no-migration-prescription) over the 43 declared-breaking upstream entries in 26 commits. Its ObjectStack-facing claims hold at this head:
    • DashboardWidgetSchema.dataset is required (ui/dashboard.zod.ts:1270).
    • percentScaleOf is the spec's own export (data/percent-scale.ts:61).
    • Check Changeset is green.
  • Lockstep and manifest records: right.
    • objectui-lockstep.json moves only rev, revDate and recordedAgainstPin. The grammar is unchanged, so no port is owed.
    • sdui-manifest.record.json moves only objectuiSha, modulesRoot, generatedAt and sha256.
    • Console Pin Gate and Lint & Repo Gates are green.
  • WRONG, blocking: ObjectGridPropsSchema.keyboardNavigation still ships as inert at a pin where the grid reads it.
    • What the spec says now:
      • The head's ui/component.zod.ts:4389 describe reads "[EXPERIMENTAL — not enforced] … No renderer reads it yet … so authoring it changes nothing today".
      • The member docblock (:4376-4387) says an authored value "changes nothing yet".
      • The generated content/docs/references/ui/component.mdx:738 repeats the describe.
    • What objectui does at this pin: the range carries objectui#11068's build, 154075ab1.
      • The console changeset says "An object-grid honours keyboardNavigation".
      • The regenerated manifest publishes the input with its behaviour ("Defaults to on when the grid renders editable …").
      • This PR's own re-measures record it as new ObjectGrid.tsx code at this pin: the api-methods-batch-conformance.test.ts comment and component.zod.ts:6616.
    • On main (pin 89cad75d5570) the describe was true. At this head it is false, in text @objectstack/spec publishes beside a console that honours the key.
    • The record prescribes its own exit at :4386: "When the BUILD lands and the grid reads it, drop the marker in the same change". This bump is the objectstack change that lands that build.
    • This is a re-measure, not a ride-along. The record cites objectui read state that this bump moves. That is the same class as the four action:* records this PR already corrected ("Records whose claim moved, not only their numbers"). The os-dev-report does not name it.
    • Cure, inside the claimed surface:

② Semver level

  • @objectstack/spec patch: matches. It publishes describe and description text only, and the cure above stays patch.
  • @objectstack/console minor: matches. That is the bump script's auto level over a range that both widens and narrows. The launch-window no-major rule holds it at minor (check-changeset-no-major green), and each of the 43 entries carries its own BREAKING annotation beside the ADR-0087 marker.
  • objectui-lockstep.json, sdui.manifest.json and scripts/sdui-manifest.record.json ship in no package's files, so no changeset is owed for them.

Clause-②: no — right. No ObjectStack published accept set widens and no public surface is added. The spec moves text only. @objectstack/console lists only ./package.json in its exports, so its bundled manifest counts as shipped bytes under the exports-map rule. The console's widenings meet keys the spec rows already declare; its narrowings are objectui's own nodes, each marked BREAKING in a minor changeset. The absent arm declares no direction, and both gates that read it are green. The PR body and the spec changeset carry the same line. The claim pre-declared the path limb (packages/spec/src/** non-test). It hits, and this record is the at-tier review it owes.

③ Boundary flags

open_questions: none. The five out_of_scope_findings:

  1. Grouping-field switch leaves phantom "(empty)" groups on "Loading grid…" (class a, public door).
    • Not introduced by this hop: useServerGrouping.ts and useGroupedData.ts are byte-identical at both pins.
    • Non-blocking. The fix is objectui's.
    • ESCALATED to the seat: no filed carrier is cited. File it in objectui with the report's dedupe words.
  2. object-kanban conditionalFormatting still z.unknown().
  3. action:button/action:icon do not declare outcomeMessages.
    • Correctly left undeclared: declaring it widens a published row (Clause-②: yes, at least minor), which is outside a pin bump.
    • The rows record the forward truthfully.
    • ESCALATED: the carrier is "none", so the contract decision needs a card filed by the seat.
  4. conversions/registry.ts PAGE_HEADER_COMPONENT_TYPES docblock still calls page-header a legacy alias.
    • It is an internal constant, and the conversion is unaffected.
    • Non-blocking. It may ride the cure push as a one-line comment fix, or go to a carrier.
  5. record:details read mode draws a multi-line textarea on one line.
    • This is objectui display, outside objectui#11562's edit-mode scope.
    • Non-blocking.
    • ESCALATED to the seat for an objectui filing: the carrier is "none".

Deviations:

  • The browser run at pre-merge 87d7c51424 stands: the merged main commits touch no console asset.
  • The merge is sound: registry.ts's net diff is the six text hunks.
  • The A4 falsification (Node 22 sufficed) has no contract reach.
  • The shared-checkout detached-HEAD note is the seat's housekeeping.

Not flagged by the dev, raised here: the keyboardNavigation item in ①. It is the sole blocking item.

Implemented-by: claude/issue-21616-objectui-pin-bump
Reviewed-by: session_01HRYqpqGcWpJuJkDmbRF75w

VERDICT: FAIL


Generated by Claude Code

claude added 2 commits October 3, 2026 20:42
…te records and the outcomeMessages liveness row at objectui ab1879721

The console reader for each landed inside 89cad75d5570..ab1879721595
(objectui#11068, objectui#11227, objectui#11344), so the records that said
no renderer reads them are rewritten to the measured read at the pin, and
the keyboardNavigation describe drops its not-enforced marker.

Claude-Session: https://claude.ai/code/session_01HRYqpqGcWpJuJkDmbRF75w
Co-authored-by: Claude <noreply@anthropic.com>
…boardNavigation describe

Claude-Session: https://claude.ai/code/session_01HRYqpqGcWpJuJkDmbRF75w
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5eadf35375dc406b7c7e027d9310ac8e9e491794
Local-runs: none

① Derived judgments

Inputs.

  • Card chore(objectui): bump the console pin past objectui f624f278 (objectui#11553), carrying objectui#11544 and objectui#11562 too — #12438 B1 and #21464's columns hold wait on it #21616: its body, the claim (5971910197), both os-dev-reports (5972904218 and 5973614379) and the claim amendment (5973630403).

  • PR chore(objectui): bump the console pin to ab1879721595 (carries objectui f624f278, b0bf413c and ab187972) #21625: its body, the 25-file list and the net diff f97660cdd6...5eadf35375.

  • The 42 check-runs on the head: 39 success and 3 skipped. The skipped ones are Packed-tarball smoke (opt-in) and one duplicate run each of Check PR Size and Auto Label, whose other run is success. None failed.

    • Lint & Repo Gates was still running at the first read and concluded success on the re-read.
    • These conclusions are the gate verdicts. Nothing was built, run or re-run.
  • The FAIL record on b93b5fec00 (5973239645) was read as the finding this round answers. Each item below was judged again, not carried over.

  • Spec accept set: right. In the net diff, only these packages/spec/src/** lines change outside a docblock or a // comment:

    • two .describe() strings: ObjectGridPropsSchema.keyboardNavigation in ui/component.zod.ts and FormField.span in ui/view.zod.ts;
    • the text strings of six migration entries and their registry.ts mirror, which carry the pin sha and the re-taken corpus counts.

    No key, default, enum member, refusal or export moves. The three test files change in comments only. content/docs/references/ui/component.mdx and ui/view.mdx are the generated twins of the two describes.

  • The earlier blocking item, keyboardNavigation: cured, right. Each claim was checked against objectui at ab1879721595:

    • ObjectGrid.tsx:5458 passes schema.keyboardNavigation ?? inlineEditable to the data table.
    • At :1812, inlineEditable is (schema.editable ?? false) && objectInlineEditable.
    • In data-table.tsx, the default is false (:787) and role="grid" is set at :2235. At :2700 the table keeps one cell at tabIndex 0 and the rest at -1, and every cell gets 0 when the key is off. navigationTarget (:1964) and handleCellKeyDown (:1990) move the focus.
    • 154075ab1 (objectui#11068) is an ancestor of the new pin and not of 89cad75d5570.

    The cure is complete:

  • object-grid emptyState record: right.

    • ObjectGrid.tsx:6468-6469 resolves title and message through resolveInlineI18nLabel(…, displayLocale), and they are drawn at :6485-6486.
    • The commit that does this is 6158e4c93. Its message carries Fixes #11227, and it is in the range.
    • Only the docblock changes. No accept set moves.
  • ActionSchema.outcomeMessages liveness row, planned to live: right.

    • The reader exists at the pin. ActionRunner.ts:1497 composeSuccessMessage takes outcomeMessages[outcome], then successMessage, then the runner default, and fills ${result.*}. handlePostExecution calls it at :1535.
    • The renderers forward the key: action-button.tsx:261, action-icon.tsx:189, action-group.tsx:363 and action-menu.tsx:307. The reader commit c476be0e0 (objectui#11344) is in the range.
    • The row prescribed this exact exit: flip to live on the commit that cites the console reader at the pin, and drop authorWarn / authorHint in the same edit. Both are dropped.
    • The count shard moves from 45 live / 1 planned to 46 / 0, which is consistent. Spec property liveness is green.
    • Reach: liveness/ ships in @objectstack/spec's files, and no exports subpath reaches it. @objectstack/lint reads it from disk at run time (resolveLivenessDir), and that rule never fails a build. So the only effect is that one advisory liveness-planned-property warning stops. No accept set moves.
    • Scope: the file was outside the original claim surface. The same session's amendment (5973630403) added it before this review. I judge it in scope: it is a record whose claim this pin moves, the same class as keyboardNavigation. The cross-lane declaration the amendment cites (seat post [PM seat] domain:spec — 🟢 os-litant · session_01LAi5BVvQNiYzepSAcsoFLK #6017) is outside this review's inputs and was not read.
  • Console surface through .objectui-sha: right. I re-diffed sdui.manifest.json against main. It has 107 components, none added or removed, and 14 with changed inputs, which matches the body.

    • Widenings.
      • object-grid gains description, emptyState and keyboardNavigation.
      • record:line_items gains ten keys. RecordLineItemsProps already declares each of them.
      • object-pivot gains drillDown. It has no ComponentPropsMap row: it is objectui's own registration, listed as minor in the console changeset.
    • Narrowings.
      • On objectui's grid, flex, stack and container nodes: grid drops smColumns / mdColumns / lgColumns / xlColumns, and value sets replace free numbers on grid columns, on grid / flex / stack gap and on container padding. None of these four nodes is a ComponentPropsMap row or a PageComponentType member.
      • page:header drops breadcrumb, which PageHeaderProps already tombstones with retiredKey.
    • Effect on the JSX gate. It falls back to the console copy that sdui-manifest.ts reads beside the console's package.json. That copy now accepts the keys the spec rows already declare. It narrows only on objectui's own nodes, and each narrowing is marked BREAKING in the console changeset.
    • Description-only changes: object-grid / object-kanban conditionalFormatting, action:button / action:icon successMessage, and fields on the three form blocks.
    • None of the four action:* input lists publishes outcomeMessages. That matches what the rows record.
  • ADR-0087 disposition on .changeset/console-ab1879721595.md: right. It carries one marker, not-required (no-migration-prescription). The first item shows that the diff adds, removes and renames no ObjectStack-authorable key. Check Changeset is green.

  • Lockstep and manifest records: right.

    • objectui-lockstep.json moves only rev, revDate and recordedAgainstPin. The grammar is unchanged, so no port is owed.
    • scripts/sdui-manifest.record.json moves only objectuiSha, modulesRoot, generatedAt and sha256.
    • Console Pin Gate and Lint & Repo Gates are green.
  • Not flagged by the dev; non-blocking. The header of ui/component-object-grid-declared-keys.pin.test.ts:4-6 still says the grid "is being built to read" keyboardNavigation. It is a test file, outside the spec's files, and carries no pin citation. The fix can ride a later push.

  • For landing, not a contract item. origin/main is now 5b5e83f446, 8 commits past the merge base. Three of those commits touch this PR's paths: ui/component.zod.ts, component.mdx and registry.ts. GitHub reports the PR mergeable: true, and the queue's gates judge the merged tree.

② Semver level

  • @objectstack/spec patch: matches. The package publishes describe text, docblocks, migration-entry text and ledger data. The ledger change only withdraws one advisory lint warning.
  • @objectstack/console minor: matches. minor is the bump script's automatic level for a range that both widens and narrows. The launch-window no-major rule holds it there, and Check Changeset is green. All 43 upstream breaking entries carry a BREAKING annotation.
  • No further changeset is owed.
    • packages/sdui-parser/objectui-lockstep.json is outside that package's files (dist, README.md, CHANGELOG.md).
    • sdui.manifest.json and its record are in no package's files.
    • The spec changeset covers the liveness change.

Clause-②: no — right.

  • Spec. It moves text only. The ledger flip changes what lint warns about, not what is accepted.
  • Console. Its exports publishes ./package.json alone, so its bundled manifest is shipped bytes, read by path. The manifest's widenings meet keys that spec rows already declare, which pulls back to the declared contract. object-pivot drillDown is objectui's own registration, in a minor changeset.
  • Where the line appears. The claim, the PR body and the spec changeset carry the same line.
  • The path limb. It hits on packages/spec/src/** non-test files, as the claim declared in advance. This record is the at-tier review that limb owes.

③ Boundary flags

open_questions: none in either report.

Deviations in the patch-round report:

  1. packages/spec/liveness/action.json and its count shard are outside the claim surface. Answered: the amendment (5973630403) adds them, and the first section judges the flip right and in scope. The revert route the report offers is not needed.
  2. origin/main was not merged. Answered: GitHub reports the PR mergeable. If the seat merges main before queueing, the head moves past 5eadf35375. This record then still governs only under the pure-regen exception, with a Regen-provenance: line on the PR. Otherwise a fresh record is owed.
  3. The worktree was re-created and removed. It has no contract reach.

out_of_scope_findings:

  1. The [spec(ui): the ComponentPropsMap rows still type renderer-read members as z.unknown() — navigation on object-map / object-gantt / object-tree and conditionalFormatting on object-kanban accept 42 — the family close-out after #21445 #21464] columns hold. At the pin, groupValueFormatter (ObjectGrid.tsx:2979) reads objectDefField?.options only (:3010): b0bf413 retired the column read.

  2. Four round-0 findings, carried unchanged. The card cites no carrier for any of them.

    • The grouping-field switch leaves phantom "(empty)" groups (class a, objectui).
    • action:button / action:icon do not declare outcomeMessages.
    • The PAGE_HEADER_COMPONENT_TYPES docblock is stale.
    • record:details read mode draws a multi-line textarea on one line.

    ESCALATED again, for the seat to file. The outcomeMessages row question is sharper now that the ActionSchema row is live. A registered action's outcome copy shows through action:bar, but the same key written on an action:button node is refused at save. Deciding that is a contract decision and needs its own card.

No blocking item remains.

Implemented-by: claude/issue-21616-objectui-pin-bump
Reviewed-by: session_01HRYqpqGcWpJuJkDmbRF75w

VERDICT: PASS


Generated by Claude Code

claude added 2 commits October 3, 2026 21:34
The os-regen driver kept this branch's side of the generated page in the
merge with origin/main 5b5e83f, which dropped main's object-metric
aggregate and trend rows (3f1bc81). gen:docs on the merged tree puts
them back. The page now differs from origin/main only by this branch's
keyboardNavigation row.

Claude-Session: https://claude.ai/code/session_01HRYqpqGcWpJuJkDmbRF75w
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4fc15b7e67f4d4f25e3886058e3eb507b2947bbc
Local-runs: none

① Derived judgments

Inputs.

This round is a sync, not new work: right.

  • The head is 5eadf35375, then the merge 501902fb09 of origin/main 5b5e83f446, then 4fc15b7e67, which regenerates content/docs/references/ui/component.mdx.
  • The net diff's added and removed lines are identical, line for line, to those of f97660cdd6...5eadf35375, the diff the PASS judged. The PR's own contribution did not move.
  • Against the merge base the diff is these 25 paths and nothing else, so main's side survived the merge. registry.ts changes only in the six migration-text hunks, and main's entries are intact. component.mdx differs from main only by the keyboardNavigation row. 4fc15b7e67 restores main's object-metric aggregate / trend rows (3f1bc816a2), which the os-regen driver had dropped by keeping the branch side.
  • origin/main has since moved to 36e4647520, four commits later. None of them touches a path of this PR, and GitHub reports mergeable_state: clean.

Spec accept set: right. It does not move.

  • Twelve non-test packages/spec/src/** files change. Outside a docblock, a // comment or a migration-entry string, only two lines change, both .describe() text: ObjectGridPropsSchema.keyboardNavigation in ui/component.zod.ts and FormField.span in ui/view.zod.ts. No key, default, enum member, refusal or export moves.
  • The three test files change in comments only. component.mdx and view.mdx are the generated twins of the two describes.

Re-measured records, each checked against objectui at ab1879721595: right.

  • keyboardNavigation.
    • ObjectGrid.tsx:5458 hands schema.keyboardNavigation ?? inlineEditable to the table, and :1812 defines inlineEditable as (schema.editable ?? false) && objectInlineEditable.
    • data-table.tsx defaults the key to false (:787) and sets role="grid" (:2235). It keeps one roving tabIndex over the data cells (:2700), and navigationTarget (:1964) moves it with the arrow keys, Home / End and Ctrl+Home / Ctrl+End.
    • 154075ab1 is in 89cad75d5570..ab1879721595.
    • The new describe states exactly this.
  • emptyState. ObjectGrid.tsx:6468-6469 resolves title and message with resolveInlineI18nLabel against the display locale, and draws them at :6485-6486. 6158e4c93 is in the range.
  • The outcomeMessages liveness row, planned to live.
    • ActionRunner.ts composeSuccessMessage (:1497) picks the own-property outcomeMessages[outcome], then successMessage, then the runner default, and fills ${result.*}. handlePostExecution calls it.
    • All four renderers forward the key: action-button.tsx:261, action-icon.tsx:189, action-group.tsx:363 and action-menu.tsx:307. c476be0e0 is in the range.
    • authorWarn and authorHint are dropped in the same edit, as the row prescribed. The count shard reads 46 live and 0 planned, and Spec property liveness is green.
  • Anchors spot-read, every one as cited:
    • action-button.tsx: :215, :318, :346, :382 and :394, the comment at :422-426 that keeps the input unpublished, and inputs at :427-572.
    • ObjectView.tsx:2299 case 'map':, objectql.zod.ts:2370 and :1419, and ListView.tsx:85 and :146.
    • WIDE_FIELD_TYPES is unchanged. json now maps to field:object, which is not a wide type.
    • The ObjectGrid.tsx:4810-4837 selection block still hashes to c88443302d40….
    • The corpus is 10267 files, with 16377 objectstack hits and 6665 @objectstack/spec hits, as the six migration texts state.

Console surface through .objectui-sha: right.

  • I re-diffed sdui.manifest.json against main. It has 107 components, none added or removed, and 14 with changed inputs, as the body says.
  • Widenings.
    • object-grid gains description, emptyState and keyboardNavigation, and record:line_items gains ten keys. The spec rows already declare each of them, and RecordLineItemsProps declares all ten.
    • object-pivot gains drillDown. It is objectui's own registration and has no ComponentPropsMap row.
  • Narrowings.
    • grid, flex, stack and container narrow. None of these four is a PageComponentType member or a ComponentPropsMap row.
    • page:header drops breadcrumb, which PageHeaderProps already tombstones with retiredKey.
    • Every example value sits inside the new sets:
      • the metadata grid nodes use columns 3 and 4 with gap 4;
      • the JSX grid uses columns 3 and 4 with gap 5;
      • every JSX gap is 1 to 6 or 8, inside all three gap sets;
      • no example sets a node padding;
      • the numeric gap and padding values above 8 sit in CSS style objects, not on node inputs.
  • Description-only changes: conditionalFormatting on the grid and kanban, successMessage on the button and icon, and fields on the three form blocks.
  • Behaviour carried, not a contract item. objectui#11344 drops the server's message from the success toast. The spec never declared that rung, and its successMessage and outcomeMessages describes already state the new order (ruling A on cloud#2315). No example action handler returns a message. Non-blocking.
  • Docs drift advisory. Both hand-written pages it lists name symbols that change here only in docblock or describe text. No hand-written page outside releases and references names a retired objectui key or a layout value outside the new sets.

ADR-0087 disposition on .changeset/console-ab1879721595.md: right.

  • It carries one marker, not-required (no-migration-prescription). The diff adds, removes and renames no ObjectStack-authorable key.
  • Its ObjectStack-facing claims hold:
    • none of the retired node keys is a PageComponentType member or a ComponentPropsMap key;
    • DashboardWidgetSchema.dataset is required (ui/dashboard.zod.ts:1270);
    • percentScaleOf is the spec's own export (data/percent-scale.ts:61);
    • deleteBehavior is the lookup field's key.
  • Check Changeset is green.

Lockstep and manifest records: right.

  • objectui-lockstep.json moves only rev, revDate and recordedAgainstPin. The grammar is unchanged, so no port is owed.
  • sdui-manifest.record.json moves only objectuiSha, modulesRoot, generatedAt and sha256.
  • Console Pin Gate, Lint & Repo Gates and Type Check · source gates (which runs check:objectui-pin-citations) are green.

Not flagged by the dev; non-blocking.

  • New with this round's merge. Main's 3f1bc816a2 (spec(ui): the ComponentPropsMap rows still type renderer-read members as z.unknown() — navigation on object-map / object-gantt / object-tree and conditionalFormatting on object-kanban accept 42 — the family close-out after #21445 #21464 stage 4) brings in object-metric records dated "at the .objectui-sha pin 89cad75d55". At this head's pin their line numbers have moved.
    • aggregate, trend and compareTo: the reads hold in substance.
      • In ObjectMetricWidget.tsx, :199 types groupBy as the chart aggregate's, :354 is answersInFieldUnit, :481 is the '_all' floor and :689 is derivedTrend ?? trend.
      • MetricWidget.tsx:262-265 is the trend type, and compare-to.ts is byte-identical.
    • The HELD drillDown docblock says report "is drawn as a report body when it carries columns or objectName".
      • objectui#11506 retired that object-bound form at ab1879721595: DrillDownDrawer.tsx:115 draws a report only when it is dataset-bound (isDatasetBoundReport).
      • The hold's conclusion, the fork with ChartDrillDownSchema, still stands: the tile still draws a report and still never reads drillDown.filter. Its describe stays true.
    • This is the columns hold's class. The record is dated and not an asserting citation (the citation gate is green), and its exit is a ruling into a narrowing, which a pin bump must not make. See ③.
  • ui/component-object-grid-declared-keys.pin.test.ts:4-6 still says the grid "is being built to read" keyboardNavigation. It is a test file and does not ship. The fix can ride any later push.

② Semver level

  • @objectstack/spec patch: matches. The package publishes describe text, docblocks in its shipped src/**/*.zod.ts, migration-entry text and one row in its shipped liveness/. The ledger flip withdraws one advisory lint warning. Nothing is accepted or refused differently.
  • @objectstack/console minor: matches. minor is the bump script's automatic level for a range that both widens and narrows, and the launch-window no-major rule holds it there. Each of the 43 upstream breaking entries is marked BREAKING.
  • No further changeset is owed.
    • packages/sdui-parser/objectui-lockstep.json is outside that package's files (dist, README.md, CHANGELOG.md).
    • The root sdui.manifest.json and its record are in no package's files. The console's dist copy rides the console changeset.
    • The sync round publishes nothing that the two changesets do not already state.

Clause-②: no — right.

  • Spec. It moves text only. The ledger flip changes what lint warns about, not what is accepted.
  • Console. Its exports publishes ./package.json alone, and its dist manifest is shipped bytes the CLI's JSX gate falls back to.
    • The manifest's widenings meet keys the spec rows already declare.
    • object-pivot drillDown is objectui's own registration. The spec reaches that type only through the open string arm of PageComponentSchema.type.
    • Read even as a widening, it would change nothing here: the console changeset is already minor, and this record is the at-tier review.
  • Where the line appears. The claim, the PR body and the spec changeset carry the same line.
  • The path limb. It hits on packages/spec/src/** non-test files, as the claim declared in advance. This record is the review that limb owes on this head.

③ Boundary flags

open_questions: none in either report.

Deviations:

  1. Patch round: packages/spec/liveness/action.json and its count shard are outside the original claim surface. Answered: the amendment (5973630403) adds them, and ① judges the flip right. The revert route the report offers is not needed.
  2. Sync round: the os-regen driver kept the branch side of component.mdx and dropped main's rows. This is stated in the PR body; there is no os-dev-report on the card for this round. 4fc15b7e67 regenerated the page.
    • Answered: the net diff shows main's rows intact, with only the keyboardNavigation row added.
    • The head moved past the PASS head by a commit that is not a merge, so a fresh record was owed. This is it.
  3. The browser run was taken at pre-merge 87d7c51424. It stands: the merges since carry no console asset.
  4. The A4 falsification and the housekeeping. The A4 falsification (Node 22 sufficed), the shared-checkout note and the worktree notes have no contract reach.

The domain:spec pointer (5973713695). #20471 is not carried: check-objectui-pin-citations.ts is not among the 25 paths. #20471 would quote the first lines of the anchors in the six ComponentPropsMap rows and raise ASSERTED_ANCHOR_FLOOR. Answered: right for this card, whose scope bars ride-alongs beyond the bump's own gates. #20471 re-holds on its own Restart-when: for the next bump. Non-blocking.

out_of_scope_findings:

  1. The [spec(ui): the ComponentPropsMap rows still type renderer-read members as z.unknown() — navigation on object-map / object-gantt / object-tree and conditionalFormatting on object-kanban accept 42 — the family close-out after #21445 #21464] columns hold. At this pin groupValueFormatter reads the object field's options only (b0bf413). The card routes the hold to spec(ui): the ComponentPropsMap rows still type renderer-read members as z.unknown() — navigation on object-map / object-gantt / object-tree and conditionalFormatting on object-kanban accept 42 — the family close-out after #21445 #21464's seat, and its exit narrows an accept set. Non-blocking.

  2. New, the same class: the object-metric drillDown hold merged from 3f1bc816a2 (① above). Non-blocking.

  3. Carried from round 0. The card cites no carrier for any of these.

    • The grouping-field switch leaves phantom "(empty)" groups (class a, objectui).
    • action:button / action:icon do not declare outcomeMessages. Deciding that is a contract decision for its own card, and the question is sharper now that the ActionSchema row is live.
    • record:details read mode draws a multi-line textarea on one line (objectui).

    ESCALATED again, for the seat to file. A fourth round-0 finding, the stale PAGE_HEADER_COMPONENT_TYPES docblock, is non-blocking: it is an internal, unexported constant in conversions/registry.ts.

  4. object-kanban conditionalFormatting is still z.unknown(). It is correctly routed to spec(ui): the ComponentPropsMap rows still type renderer-read members as z.unknown() — navigation on object-map / object-gantt / object-tree and conditionalFormatting on object-kanban accept 42 — the family close-out after #21445 #21464, and objectui's resolver still paints stored native rules. Non-blocking.

No blocking item remains.

Implemented-by: claude/issue-21616-objectui-pin-bump
Reviewed-by: session_01HRYqpqGcWpJuJkDmbRF75w

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

✅ ACCEPT — PR #21625 (#21616), head 4fc15b7e67f4d4f25e3886058e3eb507b2947bbc · domain:devx seat 2 (session_01HRYqpqGcWpJuJkDmbRF75w) · 2026-10-03T22:26Z

Reviewed against GitHub and the diff, not the reports (os-dev-reports 5972904218, 5973614379, 5974033523).

  • CI on this head: 42 check runs, 39 success and 3 skipped; Lint & Repo Gates and TypeScript Type Check are success. mergeable_state is clean. origin/main moved 4 commits after the sync, and none touches a path of this PR.
  • Contract record: 5974121906, PASS at this exact head, with Served-tier: CONTRACT_REVIEW_TIER and Local-runs: none.
    • It owes because the clause-② path limb hits packages/spec/src/** non-test. Clause-②: no is judged right.
    • History: FAIL 5973239645 on b93b5fec00, fixed in the patch round; PASS 5973690821 on 5eadf35375. The pre-enqueue sync moved the head, and the carry test refused because component.zod.ts and registry.ts moved under main. So a fresh record was taken.
  • Own contribution across the sync: I compared it myself. For all 25 paths, the -U0 added/removed lines of f97660cdd6..5eadf35375 and 5b5e83f446..4fc15b7e67 are identical, and the file lists are equal.
  • Route: check-governed-merges.mjs --pr 21625 on the final 25 paths: NOT GOVERNED. 1,306 changed lines.
  • Scope: no content/docs/releases/** path. The changesets are @objectstack/console minor and @objectstack/spec patch; there is no major.
  • Closing line: Fixes #21616 only. I scanned the whole body; no closing keyword sits next to any other card number.
  • Docs drift bot: it names analytics.mdx (DatasetMeasureSchema) and layout-dsl.mdx (ComponentPropsMap). Both symbols change in comments only, with no accept-set move, so neither page is falsified.

Out-of-scope findings, one line each:

Landing now: pr_ready and automerge_enable through the relay.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 3, 2026 22:27
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 3, 2026 22:27
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 3, 2026
Merged via the queue into main with commit 1cbe165 Oct 3, 2026
44 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21616-objectui-pin-bump branch October 3, 2026 23:04
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…/spec/src to the commits that decided them (objectstack-ai#21642)

Part of objectstack-ai#20234

Clause-②: no

The next stage of the dead-citation sweep under ruling C+D (record
`5749154545` on objectstack-ai#19123): the six comment sites the census pointer
`5923089344` names, claimed in `5973700515`. Each cited a tracker number
that answers 404. Each now cites, in form C, the commit on `main` that
decided the rule, and in every case that is the anchor an earlier stage
of this card already landed for the same number in a sibling site.
Comment text only: no string literal, `.describe()` text, schema, type,
export or test changes.

## The rule applied

Form C, as triage's first grade (`5856637615`) states it and stages 1 to
11 applied it: "the ADR or ruling record, else the commit sha, with a PR
number allowed only beside it as a link", and "Each rewritten line says
what the cited object decided … Where nothing answers, keep the
sentence's reason in words and drop the citation." Stage 10's ACCEPT
(`5902947213`) adds: "Where the number alone carried the meaning, the
decision is also said in words." Live numbers stay (stage 9's ACCEPT
`5899135835`: "its claims forbid removing a lit citation"). A slash
group keeps its live numbers and takes the commit beside them, as stage
5 did for `objectstack-ai#9881/objectstack-ai#9972` in `component.test.ts` (now `the objectstack-ai#9881 / commit
60e0f90 records`).

## What changes (line numbers at base `5b5e83f446`)

| site | dead number | now reads | the same anchor already on `main` |
|:--|:--|:--|:--|
| `data/datasource.zod.ts:701` | `objectstack-ai#9040` in `objectstack-ai#8082/objectstack-ai#8336/objectstack-ai#9040` |
`objectstack-ai#8082/objectstack-ai#8336, or the refusal of a credential in mongo's options
passthrough (commit 2420641)` |
`data/datasource-credential-redaction.ts`, `data/driver/common.zod.ts`
(stage 3) |
| `data/filter.zod.ts:1067` | `@see` URL of `objectstack-ai#17590` | `@see commit
e04a0af (the membership reading, landed with the SQL family)` |
`data/filter.zod.ts:979` (stage 3) |
| `data/value-roundtrip-conformance.ts:100` | `@see` URL of `objectstack-ai#12380` |
`@see commit 4045b95 (the measured boundary set)` | the same file's
family table and `v_json` paragraph (stage 3) |
| `ui/component.zod.ts:675` | `objectstack-ai#6276` in `objectstack-ai#5611/objectstack-ai#5775/objectstack-ai#6276` | `objectstack-ai#5611 /
objectstack-ai#5775 / commit 78f0be8` | `ui/component.zod.ts:71`, `:207`, `:682`
(stage 5) |
| `ui/component.zod.ts:751` | `objectstack-ai#6276` in `objectstack-ai#5611/objectstack-ai#5775/objectstack-ai#6276` | `objectstack-ai#5611 /
objectstack-ai#5775 / commit 78f0be8` | as above |
| `ui/component.zod.ts:4555` | `objectstack-ai#9972` in `objectstack-ai#9881/objectstack-ai#9972` | `objectstack-ai#9881 /
commit 60e0f90` | `ui/component.zod.ts:2609`,
`ui/component.test.ts:407` (stage 5) |

The datasource line is the one site whose sentence did not say what the
dead number decided, so it now says it in words, from the code: the
mongo `options` passthrough refusal is part of the config gate
(`credentialFreeMongoOptions` on `MongoConfigSchema.options`). That
comment reflows from two lines to three. Every other edit is one line
for one line, and each of those sentences already stated its decision in
words.

## Evidence

**The six are dead, measured with the gate's own instrument.** `node
scripts/check-issue-citations.mjs --census --json` (read-only, board
enumerated, 195 pages) at base `5b5e83f446` lists exactly these six plus
the two literal-read sites stage 6 kept on purpose
(`data/api-derivation.ts:163` `[objectstack-ai#6259]`, `identity/identity.zod.ts:230`
`objectstack-ai#8715`). Lines match the claim. No seventh dead number in
`packages/spec/src`. REST `issues/N`, redirects not followed: `objectstack-ai#9040`,
`objectstack-ai#17590`, `objectstack-ai#12380`, `objectstack-ai#6276`, `objectstack-ai#9972` answer 404; the lit controls
`objectstack-ai#8082`, `objectstack-ai#8336`, `objectstack-ai#5611`, `objectstack-ai#5775`, `objectstack-ai#9881`, `objectstack-ai#5286`, `objectstack-ai#12624` answer
200.

**After, at head `dbacec5f91`:** the same census reads
`allocated-but-absent` 119 → 113 repo-wide and 8 → 2 in
`packages/spec/src`. Gone: exactly the six. New: none.

**Each anchor is on `main` and names its number.** REST
`compare/SHA...5b5e83f` answers `ahead`, `behind_by: 0` for all five
shas (controls: `5b5e83f446~1` answers `ahead`, `behind_by: 0`; the
off-main branch commit `25fb56f1b7` answers `diverged`, `behind_by: 3`).
The local `merge-base --is-ancestor` exits 1 for all five on this
shallow checkout, which is the shallow false negative, so the REST
reading is the one cited. Each commit's own text names the number it
replaces: `24206416a` (subject: refuse a credential in the mongo options
passthrough at publish, `objectstack-ai#9040`); `e04a0aff2` (its body names `objectstack-ai#17590`
as the card it delivers, and its diff wrote the very `@see` line
rewritten here); `4045b954d` ("Part of objectstack-ai#12380", with the live 17-value
measurement across three dialects); `78f0be872` (subject names `objectstack-ai#6276`);
`60e0f900a` (subject names `objectstack-ai#9972`).

**Comment-only, proved two ways.**
- A parser-driven token comparison (TypeScript parser, leaf tokens off
the syntax tree, JSDoc nodes skipped): base and head token streams are
identical in all four files (2,231 / 6,169 / 1,098 / 16,019 tokens),
with 0 parse diagnostics on either side. Controls, 7 of 7 behave: an
identifier edit, a `.describe()` string edit, a template-literal edit
and a regex-literal edit each report DIFFERS; a line-comment edit, a
JSDoc edit and an `@see`-line edit each report IDENTICAL.
- Every added or removed line in the source diff is a comment line: 15
of 15 (+8 / −7).

**Generated artifacts.** After a spec build, `check:generated` reports
15 of 15 up to date. No reference page renders any of the six comments
(`content/docs/references/` holds neither the old nor the new text).
Nothing was regenerated.

**Changeset: `@objectstack/spec` patch.** Measured after the build:
`datasource.zod.ts`, `filter.zod.ts` and `component.zod.ts` ship
verbatim through the package's `files` entry `src/**/*.zod.ts`; in
`dist`, the three `component.zod.ts` comments are in 14 `.js` / `.mjs`
bundles and the `value-roundtrip-conformance.ts` `@see` is in
`dist/data/index.d.ts` and `.d.mts`. The datasource and filter comments
reach no `dist` file. Positive control: the `maxVisible` `.describe()`
text is in 14 `dist` files. So the change publishes text, and
`skip-changeset` does not apply.

**Tests and gates, at head `dbacec5f91`.**
- `pnpm --filter @objectstack/spec build`: exit 0. Spec `local` vitest:
609 files, 18,057 passed, 1 todo. `pnpm --filter @objectstack/spec
typecheck`: exit 0.
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 79 families; `--ran` reconciles 78
run, all exit 0, and 1 NOT MEASURED, 0 unrun.
- NOT MEASURED: `check:dual-build-cjs-loads`, reason: it refuses (exit
3) until every publishable package is built, and a full monorepo build
was not run locally. Narrowed and declared: the diff is comment-only in
`packages/spec`, and of the gate's 106 `require` entries (its own
`--list`), spec's 19 all load at head. CI runs the full gate.
- Two gates first refused for unbuilt prerequisites (`@objectstack/lint`
`check:doc-formula-expressions`, `check:lean-entry-closure`). After
building `formula`, `lint` and `objectql` (14 of 14 turbo tasks), both
exit 0.

**Overlap.** Open PR objectstack-ai#21632 and PR objectstack-ai#21625 touch `ui/component.zod.ts` in
other regions. Neither had landed at `origin/main` `36e4647520`,
re-fetched before this PR opened, so no merge was taken. A `merge-tree`
of this head onto that tip exits 0, and none of the five changed paths
is routed to the regeneration merge driver, so the local answer is
GitHub's.

## Acceptance notes

- `docs/audits/2026-07-unknown-key-strictness-ledger.md:676` still names
the `objectstack-ai#5611/objectstack-ai#5775/objectstack-ai#6276` rule. It is outside this stage's file surface
and outside the census surface. Carrier: none.
- For `objectstack-ai#17590` the commit rung was taken, although `e04a0aff2`'s message
names a director-seat ruling record. That matches stage 3's anchor for
the same number at `filter.zod.ts:979`, reviewed PASS.
- Not governed: no path under `docs/adr/`, `.claude/`, `skills/`,
`AGENTS.md` or `CLAUDE.md`. 29 changed lines.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…eld — the console derives the line-position field from the child object (objectstack-ai#21589) (objectstack-ai#21632)

Fixes objectstack-ai#21589
Clause-②: no (narrowing)

## What this does

Retires `sortField` from the `object-master-detail-form` detail entry
(`ComponentPropsMap['object-master-detail-form'].details[]`). The
console reads no authored value for it. At the `.objectui-sha` pin
`89cad75d5570` (and unchanged at the current pin `ab1879721595`:
`MasterDetailForm.tsx` and `deriveMasterDetail.ts` are byte-identical
between the two), `MasterDetailDetailConfig` has no `sortField` member
(`plugin-form/src/MasterDetailForm.tsx:83`). The field the line grid
stamps with each line's position is the one `deriveDetail` derives from
the child object (`deriveMasterDetail.ts:540`), and it reaches the grid
as `sort_field` (`:874`). The spec still declared the key, so an
authored `sortField` went through `os validate` clean and was then
dropped.

This executes the direction triage confirmed (`5969870827`), the one
recorded on objectstack-ai#21220's landing (`5937457620`) and mirrored on
objectstack-ai/objectui#11396 ③: a `retiredKey()` tombstone, one
ADR-0087 D2 conversion and one D3 entry, with no staged window because
the writer census is zero. There is no objectui edit here: the
objectui#11396 mirror follows once a published `@objectstack/spec`
carries this.

## The retirement kit

- **Tombstone** on the strict detail entry
(`packages/spec/src/ui/component.zod.ts`). The refusal is the
prescription, and the key's input type is the retired-key mark, so `tsc`
refuses it too. The prescription opens with the fully qualified key:
`object-master-detail-form` property `details[].sortField`. It ends with
the house `os migrate meta --from 17` sentence.
- **One declaration of the derived sort-field names.**
`INLINE_GRID_SORT_FIELDS` (the same six names in the same order as the
renderer's `SORT_FIELD_NAMES`) moves out of
`data/inline-grid-columns.ts` into `data/inline-grid-sort-fields.ts`, a
module reached only by relative import, like
`ui/action-target-aliases.ts`. Exported from `inline-grid-columns.ts`,
it would have reached the published API through the `data` barrel.
`inline-grid-columns.ts` imports it, and the tombstone's prescription
and `record:line_items`' answer print the list from it
(`INLINE_GRID_SORT_FIELD_LIST`). `check:api-surface` is unchanged, as
expected.
- **D2 conversion
`object-master-detail-form-detail-sort-field-removed`** (step 18,
`retiredFromLoadPath`, `retiredAfter: '17.6.0'`, order 60). It is a
lossless delete of `sortField` from each `properties.details[]` entry of
an `object-master-detail-form`, scoped by component type and by
position. Its fixture covers region, nested-card and slotted positions,
an open-namespace control with the same shape, and an entry that is not
an object.
- **`RETIRED_KEYS_BY_MAJOR[18]`** registers the nested key
`ui/ObjectMasterDetailFormProps:details.sortField` (an array member,
spelled without its `[]`). It has no `authorable-surface/` line, so
checks (b2) and (b3) resolve it against the emitted schema, and
`check:authorable-surface` is green on it.
- **D3 entry `object-master-detail-form-detail-sort-field-retired`**,
plus a `STEP18_RATIONALE` fragment at order 70.
- **Texts that became false, changed in this PR:**
- the entry docblock: "exactly the twelve keys" becomes eleven plus the
tombstone, re-read at the pin, and "the three it adds … and `sortField`"
now says the line-position field is derived;
- `record:line_items`' guidance for `sortField`, which used to say the
key "belongs to an `object-master-detail-form` detail entry" and now
says no block takes it;
- the generated reference page row
(`content/docs/references/ui/component.mdx`);
- the carriers test's every-key control (`FULL_DETAIL_ENTRY`, now 11
keys);
- two landed step-18 D3 entries.
`ui-object-master-detail-form-details-closed` listed `sortField?` among
the keys to write, and `ui-record-line-items-props-closed` sent
`sortField` to the detail entry. Both entries shipped in 17.6.0's
`registry.ts`. Step 18 is still open, so the 17 → 18 upgrade guide will
be generated from these texts. Reviewer, please confirm that this
correction belongs in this PR.
- **Changeset** `@objectstack/spec` `minor`, with a BREAKING banner, the
FROM → TO table and the ADR-0087 `registered` disposition.

## Premise checks (measured on origin/main 9a4182a, objectui at the
pin 89cad75d5570)

- Locations: the key was at `component.zod.ts:5566`, the docblock at
`:5539` and the `record:line_items` guidance at `:2201`–`:2202`, as the
claim said.
- Renderer: `MasterDetailForm.tsx:83` says "⛔ No `sortField` member".
The only `sortField` on that path is the derived one (`:372`, `:874`,
`:1066`, `:1081`; `deriveMasterDetail.ts:55` and `:540`). A fully
configured entry (FK plus every column typed) loads no child schema
(`:977`–`:979`), so it stamps no line position. The prescription says
this.
- Writer census, `git grep -n -w sortField` over `examples apps packages
skills content/docs`, excluding `packages/spec/**` and CHANGELOGs: 1
hit, the generated reference page. Same instrument, control `addLabel`:
6 hits, including the showcase project workspace's detail entry. At the
objectui pin, the only detail entries that write the key are probes
asserting that nothing reads it
(`masterDetailDetailsMembers-8071.test.tsx` rows 2c and `:295`/`:304`).
No objectui source assigns it through a spec type.
- Landing site: `packages/spec` is the producer of the declared key. No
other package was involved.

## Evidence

All on the final head `f765e8caaf` unless noted. The branch merged
`origin/main` three times: at `e367002e11` (which carries the sibling
element:text landing `36ad3210d4`), at `5b5e83f446` (which carries
objectstack-ai#21622's landing `3f1bc816a2`), and at `15fe567c9c` (which carries
objectstack-ai#21625's pin bump `1cbe165bfc` and objectstack-ai#21642's `15fe567c9c`). Readings
marked `69f42d393d` predate the second merge.

- `@objectstack/spec` local project at `f765e8caaf`: 609 files and 18059
tests passed (1 todo), with no skip.
- `@objectstack/spec` repo project at `f765e8caaf`: the three
merge-shape and retirement files (`step18-rationale-merge`,
`conversions-major18-merge` and
`master-detail-detail-sort-field-retirement`) passed 41 tests. The wider
repo-project run is a `69f42d393d` reading: 52 of 53 files passed. NOT
MEASURED: `scripts/build-schemas-check-mode.test.ts`, which alone
exceeds the 590 s foreground cap. `check:authorable-surface`, the gate
whose mode it tests, is green at `f765e8caaf`.
- `pnpm --filter @objectstack/spec typecheck`: exit 0, including
`check:test-typecheck`. The new pin's `@ts-expect-error` is compiled
there, so an unused directive would be red.
- Consumer packages that author an `object-master-detail-form`
`details[]` entry:
- `@objectstack/lint` at `f765e8caaf`:
`validate-component-props.test.ts` passed 51 tests (including the new
advisory pin). At `69f42d393d`, all 119 files and 5621 tests passed, and
its typecheck passed;
- `@objectstack/example-showcase` at `69f42d393d`: its typecheck passed,
and 32 files and 399 tests passed.
- `pnpm --filter @objectstack/spec check:generated`: all 15 artifacts
are up to date.
- Gates: `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 114 commands at `f765e8caaf` (the
same set as at `a1b0552acd`), and all 114 exited 0 on the first attempt
after a full build. `--ran` reconciled them as 114 derived, 114 run, 0
NOT-MEASURED, 0 UNRUN. At `a1b0552acd`, the round report that first
quoted the same reading had stale provenance; the seat's note
`5974199078` on the card corrects it.
- ESLint, as a proven narrowing rather than the repo-wide `pnpm lint`:
- Population: `eslint.config.mjs` lints
`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` minus `NEVER_LINTED`.
- `--no-inline-config --format json` over the 12 changed lintable files
at the final head `f765e8caaf` reported 12 files, 0 errors and 0
warnings (no ignored-file warnings, so all 12 are in the population). An
earlier run, before the two D3 entry text edits, read 10 files with the
same result.
- Invariance: the config never enables type-aware linting (no
`parserOptions.project` and no `projectService`), and the diff touches
neither the config nor its baselines, so no verdict on an untouched file
can move.
- Ablations: one-shot runs through `scripts/ablation-replace.mjs`,
wrapped in a trap. Each restore is proven by blob == HEAD and an empty
`git diff HEAD`.
- The tombstone reverted to `z.string().optional()`: 5 of the pin's
tombstone rows went red (refusal, list, props-lint row,
tombstone-not-strict-arm, tsc channel), and 15 stayed green.
- `sortField: 'position'` planted on the showcase workspace's detail
entry: the tree-scoped absence walk went red, naming
`examples/app-showcase/src/ui/pages/project-workspace.page.ts authors
sortField: 'position'`.

## New pins

-
`packages/spec/src/ui/master-detail-detail-sort-field-retirement.test.ts`,
in the repo project because it walks the tree. It covers:
- the tombstone: issue `code` `invalid_type`, path
`details.0.sortField`, the prescription, and the derived names drawn
from the one declaration;
  - that the props-lint row is the same schema;
  - the tombstone against the strict-arm control;
  - the walked shape keeping the key;
  - the tsc channel;
  - that the page parse never refuses;
  - `record:line_items`' answer;
- the D2 strip: stored row, built artifact in every position,
by-reference control, type and position scoping, idempotence, retired
from the load path;
  - the ledger row and the D3 entry;
- a tree-scoped absence walk over the radius `@objectstack/spec` already
declares. `check:cross-package-test-inputs` is green, and the test is
listed in `vitest.repo-tests.json`.
- `packages/lint/src/validate-component-props.test.ts`: a detail-entry
`sortField` is a `component-props-invalid` warning at
`properties.details.1.sortField`, carrying the prescription.

## Acceptance notes

- The D3 entry's `replacement` spells out the six derived names
literally. The generator copies entry values into `registry.ts` and
keeps no imports, so it cannot read the declaration. The pin asserts
that every member of `INLINE_GRID_SORT_FIELDS` appears in it.
- Deployed metadata is NOT MEASURED. Stored pages are converted by the
D2 strip when they are read, and the props lint reports live sources.
- Merge order with this seat's siblings: the element:text variant
retirement (`36ad3210d4`) landed first at conversion order 59 and
rationale order 68. objectstack-ai#21622 (`3f1bc816a2`) landed next with rationale
order 69. This PR takes 60 and 70. All three merges went through
`scripts/pm/os-regen-merge.sh`. Each time, the os-regen driver kept this
branch's copy of `component.mdx` and dropped main's rows: the variant
row, then objectstack-ai#21622's `object-metric` rows, then objectstack-ai#21625's `object-grid`
`keyboardNavigation` row. Each time, the page was regenerated from the
merged source in its own commit (the last is `f765e8caaf`). Measured by
comparing change lines, the head's delta against `main` is this PR's own
delta at `a1b0552acd`, file by file; and the head's delta against
`a1b0552acd` is `main`'s own delta. objectstack-ai#21625 did not touch the detail
entry or its docblock.
- CI at `f765e8caaf`: complete and green, 33 success and 2 skipped
(`Console Pin Gate` and the opt-in packed-tarball smoke, by their own
filters). `Lint & Repo Gates`, red at `a1b0552acd` only because GitHub's
issues API answered HTTP 503 in `check-issue-citations`, is success on
this head.

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…ui#11466, objectstack-ai#11574, objectstack-ai#11578, objectstack-ai#11581 and objectstack-ai#11583) (objectstack-ai#21710)

Fixes objectstack-ai#21696
Clause-②: no

This moves the bundled Console's objectui pin from `ab1879721595`
(objectstack-ai#21625) to objectui `main` as of the write time,
`2e818d0b51ecdf8fdd9fcbf4b916bcd7fe9a9cf6`, which is past `973fc20f3`.
The Console now carries objectui#11574 (the phantom "(empty)" groups
fix), objectui#11578, objectui#11581, objectui#11576, objectui#11583 and
objectui#11466 (V1 declared node slot). It also carries objectui's own
17.7.0 package release.

⛔ This is not a release act. Version PR objectstack-ai#21352 is untouched, and no tag,
publish or Release was made. Whether 17.7.0 ships with this pin is the
maintainer's call.

## Range

`ab1879721595..2e818d0b51ec` has 22 non-merge commits. `git merge-base
--is-ancestor` exits 0 against objectui `origin/main` for `973fc20f3`,
and `2e818d0b51ec` was objectui `main` when it was read (`git ls-remote`
just before this PR).

From the changesets objectui declared over the range:
- 17 releasing changesets, 1 release-nothing, and 6 commits that carry
no changeset.
- 12 of the 18 changesets were already consumed by objectui's release
`b493919c7`. Each was read from the commit that added it.
- 2 breaking entries, both annotated by the author and both from one
commit.

The range was listed by measurement, not copied from the card. Exactly
one commit subject in the range carries `!`:

`git log --format='%h %s' ab187972..2e818d0b5 | grep -E '^[0-9a-f]+
[a-z]+(\([^)]*\))?!:'` gives `83e3f8377
feat(types,react,core,plugin-dashboard)!: … (objectui#11466)`.

| objectui landing | commit | note |
|---|---|---|
| objectui#11466: node slot and `SchemaRenderer`'s `schema` take
`DeclaredNode`; the dashboard producers name declared nodes; an
envelope's object-metric retires | `83e3f8377` | **declared breaking**,
both breaking entries |
| objectui#11574: a grouping-field switch paints only the server's
groups | `2f54dca53` | smoke below |
| objectui#11581 / objectstack-ai#11578 / objectstack-ai#11576: Create View doors share one builder;
a refused save is said; chart views bind an ADR-0021 dataset |
`b65aa5e65`, `f1c937966`, `d0097af2e` | |
| objectui#11583: a refused metadata write on the view, report and draft
surfaces is said | `e8c0b9614` | past the card's `973fc20f3` |
| objectui#11598: dashboard slot-entry and chart-producer reads compile
without `BaseSchema`'s index signature | `2e818d0b5` (the pin itself) |
|
| objectui#11572 / objectstack-ai#10380: interface and object page relay stored view
config | `09036173c`, `068564691` | |
| objectui#11588: grid summary footer reads currency/precision from the
object field only | `d768c3178` | |
| objectui#11216: `object-kanban` declares `grouping` as the spec's
`GroupingConfig` | `a7557a7d4` | |
| objectui#11573, objectstack-ai#11564: `AnyComponentSchema` / flex child typing |
`bdc9049ed`, `b10c68e58` | |
| objectui release 17.7.0 | `b493919c7` | removes 2726 consumed
`.changeset/*.md` files |

## ADR-0087 disposition of the declared-breaking entries

Both entries are `not-required (no-migration-prescription)`. The
disposition is in `.changeset/console-2e818d0b51ec.md`, which replaces
the script's `adr-0087: TODO` placeholder.

| objectui PR | commit | entry | disposition |
|---|---|---|---|
| objectui#11466 (PR objectui#11512) | `83e3f8377` | A node slot and
`SchemaRenderer`'s `schema` prop take `DeclaredNode` | not-required
(no-migration-prescription). This is objectui's own TypeScript face
(`@object-ui/types` / `@object-ui/react`). No ObjectStack schema
declares or references it. |
| objectui#11466 (PR objectui#11512) | `83e3f8377` | An `object-metric`
node in a dashboard widget's legacy `component` envelope draws the
retired-format prompt | not-required (no-migration-prescription). An
ObjectStack author cannot write that envelope: `ui/dashboard.zod.ts`
refuses a widget's `component` key by name as objectui-internal, and no
example authors one. The showcase's `object-metric` page tiles still
draw their numbers (smoke below). |

`check-adr-0087-registration --base origin/main` gives "1
declared-breaking changeset(s), each carrying an ADR-0087 disposition".
`check-changeset-no-major --base origin/main` gives "This diff
introduces no `major` bump".

## What changed here

- **`.objectui-sha` and `.changeset/console-2e818d0b51ec.md`** were
written by `scripts/bump-objectui.sh
2e818d0b51ecdf8fdd9fcbf4b916bcd7fe9a9cf6 --no-commit` with
`OBJECTUI_ROOT` set to a read-only clone of objectui. The level was
auto-set to `minor`. No `../objectui` sibling exists in this container.
- **`scripts/sdui-manifest.record.json`** was re-recorded by `node
scripts/gen-sdui-manifest-node.mjs` over the tree that `pnpm
objectui:build` built at the pin. **`sdui.manifest.json` is
byte-identical**: 107 components, sha256 `0ead67c1111d…` at both pins,
so no component input moved. The record moves its pin, its `modulesRoot`
and objectui's workspace version (17.6.0 to 17.7.0).
- **`packages/sdui-parser/objectui-lockstep.json`** was re-recorded with
`pnpm gen:sdui-lockstep` against
`OBJECTUI_ROOT=.cache/objectui-2e818d0b51ec`. It records 214 grammar
lines (blob `0131f27cf86d`), 25 codes and containment predicate
`76c18fb95d1f`. All are unchanged, so no port is owed. objectui's
`packages/sdui-parser/src` is byte-identical across the range; only its
CHANGELOG and package.json moved.
- **The 54 asserting pin citations in `packages/spec/src`** were
re-measured at the new pin, not restamped:
- Every anchor in each asserting record was resolved against objectui's
tree at the old pin and mapped through `git diff -U0 ab187972
2e818d0b5`.
  - Four cited files changed:
- `plugin-dashboard/src/index.tsx` (+13/-1, objectui#11466). Lines were
added above the `object-metric` registration, so its start moves `244`
to `256` and the icon input moves `269` to `281`. Both lines are
byte-identical, still `{ name: 'icon', type: 'string' }`.
- `packages/types/src/objectql.ts`. objectui#11216's `grouping` member
landed below the cited `limit` member, so `:4720` did not move.
- `content/docs/plugins/plugin-kanban.mdx`. A `grouping` row landed
below the `limit` row, and the page still teaches `limit: 250`.
- `packages/react/src/SchemaRenderer.tsx` (+21/-20, objectui#11466).
Only the type import, the `SchemaRendererProps` docblock and the
`schema` type changed. The `properties.*` hoist, its strip list and the
`createElement` spread did not, so `object-timeline`'s prop channel
still carries `data`.
  - Every other cited file is byte-identical across the hop.
- Each record gains a dated 2026-10-04 hop sentence and keeps its
`ab1879721` history.
- **The six migration entries' corpus counts** were re-taken with `git
grep -o -F`. That method first reproduced every `ab1879721` number:
10267 files, `objectstack` 16377, `@objectstack/spec` 6665, `timeout`
1348, `useState` 2476, `TTL` 180, `tenant` 1238, `Span` 491.
- The new numbers are 7579 files, 17227, 7134, 1351, 2477, 182, 1317 and
505. The file count drops because objectui's 17.7.0 release removed 2726
consumed changesets.
- Every zero is still zero: 98 tokens were checked, the export lists of
the three cited spec files plus every named key. The only non-zero
tokens are the expected `Span` / `SpanSchema`. The 14 new `Span` hits
are `Spanish` prose.
- `packages/spec/src/migrations/registry.ts` was regenerated with
`gen:migration-registry`.
- **`.changeset/objectui-pin-citations-2e818d0b51ec.md`** is a
`@objectstack/spec` patch, because the `FormField.span` describe and six
migration descriptions name the pin.
`content/docs/references/ui/view.mdx` was regenerated by
`check:generated --fix`.
- **`main` was merged** (`7e0066af7a`) with
`scripts/pm/os-regen-merge.sh` in merge commit `a0a35cb30f`, with no
conflict hunk. Afterwards:
  - `check:generated` reports all 15 artifacts current.
- main's three new semantic entries
(`ui-object-form-fields-names-typed`, `ui-object-gantt-markers-typed`,
`ui-object-timeline-mapping-typed`) are present in the regenerated
registry.
- The pin-citation gate still counts 54 asserting citations. main added
historical ones only (108 to 118).

No example, test or gate needed adapting, and no code changed outside
generated records, citations and changesets.

## Browser smoke: `examples/app-showcase` with the Console built at
`2e818d0b51ec`

The Console was built by `pnpm objectui:build`. The build log reports:
"Console dist matches the objectui pin (objectui@2e818d0b51ec)",
"Single-zod canary: exactly one zod version literal
{major:4,minor:6,patch:5}" and "Console bundle carries THIS tree's
@objectstack/spec, and only it".

The server ran `pnpm dev -- --fresh --ui --no-watch -p 41853` (with
`OS_PORT=41853`) on its own ephemeral DB with the seeded admin. Headless
Chromium (`/opt/pw-browsers/chromium`) drove it, with console messages,
page errors and every non-2xx response captured. Only the PIDs this run
started were stopped.

**Dashboards.** objectui#11466 touches the dashboard producers. There
are no page errors and no "retired" prompt on any of the four
dashboards.

| dashboard | metric tiles (value read in the DOM) | charts drawn |
console errors |
|---|---|---|---|
| Delivery Operations `showcase_ops_dashboard` | Active Projects 2,
At-Risk (Red) 1, Awaiting Review 2, Total Budget 1,090,000 | 4 | 404
`/api/v1/usage/storage`, 404 `/favicon.ico` |
| Revenue Pulse `showcase_revenue_pulse` | Invoices 7, Invoiced Subtotal
1,920, Accounts Signed 3, Accounts (all time) 14, Paid Rate 42.9% | 4 |
404 `/api/v1/usage/storage` |
| Chart Gallery `showcase_chart_gallery` | Total Tasks 10, Avg Progress
47.0, Total Spent 616,000 | 13 | 404 `/api/v1/usage/storage` |
| System Overview `system_overview` (setup) | Total Users 3, Active
Sessions 3, Login Events 3, Config Changes 0 | 2 | 404
`/api/v1/usage/storage` |

The Delivery Operations tiles match the REST data on the same server:
- `showcase_project?status=active` returns 2 rows, `?health=red` returns
1, and the budget sum is 1,090,000.
- `showcase_task?status=in_review` returns 2 rows.
- `showcase_task` has 10 rows, matching the Chart Gallery's Total Tasks.
- `showcase_account` has 14 rows, matching Revenue Pulse's all-time
count.

The `object-metric` page tiles that this repo authors also draw:
- My Work shows Open Tasks 8, In Review 2 and At-Risk Projects 1.
- Command Center (大屏) shows 2 / 8 / 2 / 1 / 14 / 1.1M.

Neither page has a page error.

**Grouping-field switch (objectui#11574).** The drive was Tasks list,
then Group, then Add group field (it selects Title). The select then
switched to Priority, Status and Priority. The painted groups are
counted from the DOM, and the server answers are each `POST
/api/v1/data/showcase_task/query` with `groupBy`, all 200:

| step | painted groups | server rows | "(empty)" groups | stuck
"Loading grid…" |
|---|---|---|---|---|
| Title | 10 (one per task) | 10 | 0 | 0 |
| Priority | 4: High 3, Low 1, Medium 4, Urgent 2 | 4 | 0 | 0 |
| Status | 5: Backlog 2, Done 2, In Progress 2, In Review 2, To Do 2 | 5
| 0 | 0 |
| Priority | 4: High 3, Low 1, Medium 4, Urgent 2 | 4 | 0 | 0 |

The same sequence painted 13, 8 and 8 groups (9, 3 and 4 phantom) at
`ab1879721595`, as objectstack-ai#21625's acceptance notes recorded. **No phantom
"(empty)" groups appear at this pin.**

**Console errors across the run.** The only ones are the browser's
"Failed to load resource: 404" lines for `/api/v1/usage/storage` and
`/favicon.ico`, and the run has 0 page errors. The `usage/storage` 404
was already recorded in objectstack-ai#21625's smoke.

## objectstack-ai#21671 lockstep reading

- objectui's `packages/sdui-parser/src/validate.ts` at `2e818d0b5` is
byte-identical to `ab1879721595`.
- `checkType` (`:450-466`) still grades `type-mismatch` with `severity:
arms.includes('enum') ? 'error' : 'warning'`. `checkMemberTypes`
(`:407-419`) grades `member-type-mismatch` the same way.
- This repo grades both `error` since PR objectstack-ai#21678
(`packages/sdui-parser/src/validate.ts:446`, `:506`). **The gap still
stands at this pin.**
- This repo's "Known lead" note
(`packages/sdui-parser/src/validate.ts:336-340`) names objectui's copy
"at the `.objectui-sha` pin" without a sha, so it is still true and
needs no update. The bump route regenerates nothing there, and
`validate.ts` is not edited.
- `check:sdui-lockstep` does not compare severity, so this is a reading,
not a gate result.

## Gates and tests (head `a0a35cb30f`)

- `node scripts/pm/dispatch-gates.mjs --commands` derived 127 commands
from the 21-path diff. That is the same set before and after the merge.
- At `378d424915` (pre-merge) and again at `a0a35cb30f`, all 127 exited
0. `--ran` reports "127 derived, 127 run, 0 NOT-MEASURED, 0 UNRUN", with
every exit code recorded.
- Also exit 0:
- `check:objectui-pin-citations --verify-anchors` with objectui at the
pin: 54 asserting citations match `2e818d0b5`, and 7 content assertions
are verified.
- `check:objectui-bump` (20 assertions across 5 cases),
`check:sdui-lockstep`, `check-sdui-manifest`, `check:console-sha`,
`check:console-injection`, `check:migration-registry`.
- `@objectstack/spec check:generated`: all 15 artifacts current after
the merge.
- `pnpm --filter @objectstack/spec test`: 612 files, 18185 passed, 1
todo.
- `pnpm --filter @objectstack/spec typecheck`: exit 0.
- `pnpm --filter @objectstack/sdui-parser test`: 225 passed.
- `eslint --no-inline-config` on the 15 changed TS files: 0 errors and 0
warnings. The lint population is `**/*.{ts,…}`
(`eslint.config.mjs:971`). The config enables no type-aware linting
(`:328`), so this diff cannot move a judgment on an untouched file.
Repo-wide `pnpm lint` is left to CI.

## Acceptance notes

- `ui/component.zod.ts`'s `object-grid` `columns` record (the ⚠️
paragraph on `plugin-grid/src/useColumnSummary.ts:558-568`) says, about
`ab1879721595`, that the footer summary reads `currency` /
`defaultCurrency` / `precision` / `scale` off an authored column.
objectui#11588 (`d768c3178`, inside this pin) retires that read. The
record is in the historical spelling and stays true of the pin it names,
so it is left as is.
- Two anchors in records that cite no sha are outside the pin-citation
gate's population and were not re-measured:
- `ObjectView.tsx:882`, in `ui/view.zod.ts`'s [objectstack-ai#5074] block and two view
tests.
- `plugin-dashboard/src/index.tsx:204`, in the `ObjectMetricPropsSchema
icon liveness` test.
- Both already read an unrelated line at `ab1879721595`, and they shift
to `:936` and `:214` here.
- Writes: no PR assignee or label was written. This dispatch's write
budget names neither.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…ui#11611, objectstack-ai#11614 and objectstack-ai#11619) (objectstack-ai#21800)

Fixes objectstack-ai#21772
Clause-②: no

This moves the bundled Console's objectui pin from `2e818d0b51ec`
(objectstack-ai#21710) to objectui `main` as of the write time,
`9dfaca654311cddd81714153c4f82c241d7cdc54`, which is past `c096f03`. The
Console now carries objectui#11611 (the `ref:dataset` spec-form widget),
objectui#11614 (the grid widget's camelCase keys) and objectui#11619
(the record chrome's picture). Those are the three merges objectstack-ai#21714, objectstack-ai#21768
and objectstack-ai#21765 wait on.

⛔ This is not a release act. Version PR objectstack-ai#21352 is untouched, and no tag,
publish or Release was made. The card's "release first" hold is lifted
by the maintainer's order recorded in the claim:
「浏览器测试已经在运行,还有很多问题在处理,服务端开发不应该被阻塞」.

## Range

- objectui `git ls-remote origin refs/heads/main` read
`9dfaca654311cddd81714153c4f82c241d7cdc54` at 2026-10-05T00:51:29Z, just
before the bump, and the same sha again before this PR was opened.
- `git merge-base --is-ancestor` exits 0 against `9dfaca654311` for all
three carried merges: `b508ac50d9` (objectui#11611), `2abec3a96c`
(objectui#11614) and `c096f03279` (objectui#11619). The objectui clone
is shallow, but exit 0 proves itself there.
- `2e818d0b51ec..9dfaca654311` has 17 commits and 0 merges.
- objectui declared 24 changesets over the range, and all 24 release.
There are 0 release-nothing changesets and 0 commits without a
changeset. None declares `major`. Four carry the author's breaking
annotation, and the highest declared level is `minor`, so the console
changeset is `minor`. These counts come from `scripts/objectui-range.mjs
--from 2e818d0b51ec --to 9dfaca654311 --json` and from the bump's own
digest. They were measured, not copied from the card.
- Four commit subjects carry `!`. `git log --format='%h %s'
2e818d0b5..9dfaca654 | grep -E '^[0-9a-f]+ [a-z]+(\([^)]*\))?!:'` gives
`9db9ff3f9`, `8b14aecbd`, `2abec3a96` and `b403bb36f`.

| objectui commit | landing | changesets | declared disposition |
|---|---|---|---|
| `9dfaca654` | objectui#11615: the default `simple` form draws a
self-describing inline section entry; `ObjectFormSection.fields` gains
the form view's `{ field }` arm | 1, minor, **BREAKING** (for TypeScript
readers) | releasing; declared breaking, answered below |
| `15f67025b` | objectui#11345: the inline grid's default columns derive
through the spec rule `deriveInlineGridColumns` | 1, patch | releasing |
| `4c127cdef` | objectui#11613: `record:related_list` stops requiring
`columns` | 1, minor | releasing; moves the manifest |
| `c096f0327` | objectui#11383 (PR objectui#11619): the record chrome
draws the record's picture from the object's `imageField` | 1, minor |
releasing; smoke below; unlocks objectstack-ai#21765 |
| `6e9090c26` | objectui#11336: an object document's served listViews
count as served views | 1, patch | releasing |
| `9db9ff3f9` `!` | objectui#11266: a form view's `subforms[].columns`
entry is the spec's `InlineGridColumnSchema`, by reference | 1, minor,
**BREAKING** | releasing; declared breaking, answered below |
| `7c9a6b194` | objectui#11340: the docs portal renders the book
resolver's answer | 1, patch | releasing |
| `8b14aecbd` `!` | objectui#11608: `PartialSchema` is retired from
`@object-ui/types` | 1, minor, **BREAKING** | releasing; declared
breaking, answered below |
| `2abec3a96` `!` | objectui#11610 (PR objectui#11614): the grid
widget's eight field-level keys are camelCase; the snake_case spellings
are refused by name | 1, minor, **BREAKING** | releasing; declared
breaking, answered below; unlocks objectstack-ai#21768 |
| `b403bb36f` `!` | objectui#8347: `BaseSchema` loses its index
signature | 1, minor (`Clause-②: yes (narrowing)`, no BREAKING word) |
releasing; declared breaking by its `!`, answered below |
| `fd060f076` | objectui#11605: bound registrations stop requiring
`objectName`, and a node with neither shows a no-object hint | 8, minor
| releasing; moves the manifest |
| `b508ac50d` | objectui#11601 (PR objectui#11611): the `ref:dataset`
spec-form widget, fed by the report inspector's dataset catalog | 1,
minor | releasing; unlocks objectstack-ai#21714 |
| `d2e859936` | objectui#11002: the console asks `GET /usage/storage`
only when the runtime serves `features.storageUsage` | 1, minor |
releasing; smoke below |
| `b92329c89` | objectui#11591: the Organization flows page copy | 1,
patch | releasing |
| `902ebab63` | objectui#11569: `record:line_items` stops requiring
`childObject` | 1, minor | releasing; moves the manifest |
| `278d2444e` | objectui#11546: a node click on a read-only flow canvas
opens the inspector read-only | 1, patch | releasing |
| `b61c116b2` | objectui#11577: `record:details` read mode keeps a
textarea's line breaks | 1, patch | releasing |

## Declared-breaking changes, and how this repo answers each

The ADR-0087 disposition is `not-required (no-migration-prescription)`.
It replaces the bump's `adr-0087: TODO` placeholder in
`.changeset/console-9dfaca654311.md`, and it covers all five entries
below. `check-adr-0087-registration --base origin/main` gives "1
declared-breaking changeset(s), each carrying an ADR-0087 disposition".
`check-changeset-no-major --base origin/main` gives "This diff
introduces no `major` bump".

1. **objectui#11610 / PR objectui#11614 (`2abec3a96`): the grid widget's
eight keys.** `min_rows`, `max_rows`, `allow_add`, `allow_delete`,
`allow_reorder`, `total_field`, `add_label` and `sort_field` become
`minRows` … `sortField`. objectui has no dual read: its zod faces refuse
a snake_case key by name, and its `GridField` draws an inline alert
instead of the grid.
- **This repo, today.** `@objectstack/spec`'s runtime form field
(`buildObjectFormRuntimeField`, `packages/spec/src/ui/component.zod.ts`)
already refuses the eight snake_case keys by name, with guidance that
names objectui#11610. The camelCase keys are not declared there yet.
- **The interim, stated plainly.** Between this PR and objectstack-ai#21768, an
ObjectStack-authored `grid` form field can spell the keys neither way.
Measured on this head's built spec: `object-form` with `customFields: [{
name: 'items', type: 'grid', min_rows: 1 }]` is refused with
`unrecognized_keys` at `customFields.0` and the grid-key guidance. The
same field with `minRows: 1` is refused with `unrecognized_keys` at
`customFields.0`, as an undeclared key. So a `grid` field takes its
`columns` and the widget's own defaults. The refusal's prescription says
these keys "come in once the widget reads a camelCase spelling". At this
pin the widget does, and objectstack-ai#21768 declares the keys and retargets that
prescription.
- **Nothing here carries the retired spelling.** `git grep` over
`examples/` and `packages/` finds no authored snake_case grid key. The
only hits are the spec's own refusal list and its pin test, migration
prose, and one historical note about objectui's internal master-detail
adapter.
2. **objectui#11615 (`9dfaca654`): the `simple` form's inline section
entries.** For TypeScript readers of `ObjectFormSection.fields`, the
type gains the spec's `FormFieldInput` arm. No code in this repo imports
`@object-ui/types`; the only `@object-ui/*` import is
`scripts/gen-sdui-manifest-node.mjs` reading `@object-ui/core` from the
built tree. The behaviour change is that the default `simple` form now
draws an inline `{ name, … }` entry, as the other five form types
already did. The showcase's `object-form` nodes are `wizard`, `drawer`
and `modal` forms whose sections list field names, so none of them draws
differently.
3. **objectui#11266 (`9db9ff3f9`): `subforms[].columns`.** objectui's
validator now judges a column by `@objectstack/spec`'s own
`InlineGridColumnSchema`. This repo has enforced that closed shape since
objectstack-ai#20927. objectui's verdict now matches `os validate`, and the
ObjectStack accept set does not move.
4. **objectui#11608 (`8b14aecbd`): `PartialSchema`.** It leaves
`@object-ui/types`. Nothing in this repo names it (`git grep
PartialSchema`: 0).
5. **objectui#8347 (`b403bb36f`): `BaseSchema` loses its index
signature.** This narrows the TypeScript face of objectui's node types,
and nothing here compiles against them. objectui's zod faces keep their
accept sets for every key except `visibleWhen`. That key widens to the
`{ dialect, source }` envelope this repo's own parse writes.

## The three cards this unblocks

- **objectstack-ai#21714**: objectui#11601's `ref:dataset` widget (PR objectui#11611,
`b508ac50d`) is now in the pinned build, so `report.form.ts`'s
joined-block `dataset` row can declare `widget: 'ref:dataset'`.
- **objectstack-ai#21768**: objectui#11610's camelCase keys (PR objectui#11614,
`2abec3a96`) are now what the pinned widget reads, so the spec's runtime
form field can declare them.
- **objectstack-ai#21765**: objectui#11383's record picture (PR objectui#11619,
`c096f0327`) is now in the pinned build, so `object.imageField`'s
liveness row can move to `live`. The smoke below observes that read in
the browser. The reader is
`packages/components/src/renderers/layout/containers.tsx`, in the
`page:header` record chrome.

## What changed here

- **`.objectui-sha` and `.changeset/console-9dfaca654311.md`.**
`scripts/bump-objectui.sh 9dfaca654311cddd81714153c4f82c241d7cdc54
--no-commit` wrote both, with `OBJECTUI_ROOT` set to the container's
objectui clone after `git fetch origin main`. The range walked
completely without a deepen, and the level was auto-set to `minor`.
- **`sdui.manifest.json` and `scripts/sdui-manifest.record.json`.**
`node scripts/gen-sdui-manifest-node.mjs` regenerated them over the tree
that `pnpm objectui:build` built at the pin. There are 107 components at
both pins, and the sha256 moves from `0ead67c1111d…` to `6f921896ffac…`.
**This time the manifest moves**:
- Eleven inputs lose `required: true` and gain a description. Nine are
`objectName`, on `object-grid`, `list-view`, `object-form`,
`embeddable-form`, `object-master-detail-form`, `object-kanban`,
`object-metric`, `object-chart` and `object-pivot` (objectui#11605). The
other two are `record:related_list` `columns` (objectui#11613) and
`record:line_items` `childObject` (objectui#11569).
- `object-master-detail-form`'s `fields` description is rewritten for
objectui#11615's inline entries.
- Where the spec has a `ComponentPropsMap` row, these inputs are already
optional there. Measured on the built spec: `object-grid`,
`object-form`, `object-kanban`, `object-metric` and
`object-master-detail-form` `objectName`, `record:related_list`
`columns` and `record:line_items` `childObject`. So the manifest now
agrees with the spec rows. The JSX page compile reads the manifest, and
it stops refusing a node whose `dataSource` binding names the object.
- The record moves its pin and `modulesRoot`. objectui's workspace
version stays 17.7.0.
- **`packages/sdui-parser/objectui-lockstep.json`.** `pnpm
gen:sdui-lockstep` re-recorded it against
`OBJECTUI_ROOT=.cache/objectui-9dfaca654311`. It records 214 grammar
lines (blob `0131f27cf86d`), 25 codes and containment predicate
`76c18fb95d1f`. All are unchanged, and objectui's `packages/sdui-parser`
has no diff over the range, so no port is owed.
- **The 54 asserting pin citations in `packages/spec/src`.** They were
re-measured at the new pin, not restamped:
- Each asserting record's anchors were resolved in objectui at
`2e818d0b5`, mapped through `git diff -U0 2e818d0b5 9dfaca654`, and
re-read at the new pin. Each record gains a dated 2026-10-05 hop
sentence and keeps its earlier history.
- In every cited file that changed, the cited lines moved with their
text byte-identical:
- objectui#8347 re-worded docblocks in `ObjectGrid.tsx`,
`ObjectTree.tsx`, `ObjectGantt.tsx`, `ObjectCalendar.tsx`,
`SchemaRenderer.tsx`, `plugin-view/src/ObjectView.tsx`,
`plugin-map/src/index.tsx` and `plugin-gantt/src/index.tsx`.
- In `ObjectKanban.tsx`, objectui#8347 added a private
`GateBoundKanbanSchema` read type, so its fetch, navigation reads and
spread moved +30.
- objectui#11605 touched `plugin-kanban/src/index.tsx`,
`plugin-dashboard/src/index.tsx` and `ElementDataSourceGate.tsx`. The
`object-metric` icon input moved `281` → `299` and is still `{ name:
'icon', type: 'string' }`.
- objectui#11619 moved the `page:tabs` and `page:accordion` icon anchors
in `containers.tsx` by +3.
- objectui#11615, objectui#11266 and objectui#8347 moved
`ObjectKanbanSchema.limit`, `ObjectMapConfigSchema` and
`LIST_VIEW_LOCAL_OVERRIDES` in `objectql.ts` and `objectql.zod.ts`.
- objectui#11605 added `view.noObject` to the `en`, `zh` and `de` locale
packs. Their cited `calendar.configRequired` strings did not change or
move.
- One cited line changed content. `ObjectKanban.tsx:10`, the type
import, gained `SortConfig` beside the `ObjectKanbanSchema` the record
cites.
- Two counts were re-taken by their records' own methods, and both read
the same: the `keyboardNavigation` hit lines (15, against the
`schema.editable` control's 3) and the `ElementDataSourceGate`
occurrences in five `src/index.tsx` shells (0, 3, 3, 3 and 4).
- The three quoted anchors in `ui/view.zod.ts`'s map record redded at
this pin, and each was re-read and re-pointed: `case 'map':` moved
`2299` → `2300`, `ObjectMapConfigSchema` `2370` → `2388`, and
`LIST_VIEW_LOCAL_OVERRIDES` `1419` → `1437`.
- **The six migration entries' corpus counts** were re-taken with `git
grep -o -F`. That method first reproduced every `2e818d0b5` number: 7579
files, `objectstack` 17227, `@objectstack/spec` 7134, `timeout` 1351,
`useState` 2477, `TTL` 182, `tenant` 1317 and `Span` 505.
- The new numbers are 7632 files, 17313, 7186, 1360, 2477, 182, 1318 and
508. The other controls read `RuntimeConfig` 276 → 293, `resourceLimits`
2 → 2, `window` 4175 → 4193, `period` 238, `interval` 195 and `metrics`
374 → 401.
- Every zero is still zero: 98 tokens were checked, the export lists of
the three cited spec files plus every named key. The only non-zero
tokens are the expected `Span` (508) and `SpanSchema` (57). The three
new `Span` hits are `colSpan` in objectui's
`object-form-section-field-entry-11615.test.ts`. Both `resourceLimits`
hits are still prose in `packages/app-shell`.
- `packages/spec/src/migrations/registry.ts` was regenerated with
`gen:migration-registry`.
- **`.changeset/objectui-pin-citations-9dfaca654311.md`** is a
`@objectstack/spec` patch, because the `FormField.span` describe and six
migration descriptions name the pin.
`content/docs/references/ui/view.mdx` was regenerated by
`check:generated --fix`.

No example, test or gate needed adapting, and no code changed outside
generated records, citations and changesets.

## Console build and canaries

`build-console.sh` ran locally under the verify lock, after `turbo run
build --filter=@objectstack/client...`, the same two steps the `Console
Pin Gate` job runs. Exit 0, 9m02s on a shared four-core box. The build
log reports:
- "Bundle canary 'import/jobs' present".
- "Single-zod canary: exactly one zod version literal
{major:4,minor:6,patch:5}".
- "Console bundle carries THIS tree's @objectstack/spec, and only it".
- "@objectstack/console dist ready (64192 KB) from
objectui@9dfaca654311".

`check:console-sha` and `check:console-injection` exit 0 against that
dist.

## Browser smoke: `examples/app-showcase` with the Console built at
`9dfaca654311`

The server ran `pnpm dev -- --fresh --ui --no-watch --compile -p 41877`
with `OS_PORT=41877`, on its own ephemeral DB with the seeded admin.
Headless Chromium (`/opt/pw-browsers/chromium`) drove it, signing in
through the console's own login form, with console messages, page errors
and every 4xx/5xx response captured. Only the PIDs this run started were
stopped.

**No showcase object declares `imageField`, so the record picture needs
one to exist.** `git grep imageField examples` gives 0 hits. To exercise
objectui#11619's read, the smoke made a temporary local edit:
- It added `imageField: 'f_image'` to `showcase_field_zoo`, through
`node scripts/ablation-replace.mjs --hold`.
- It compiled and booted the server.
- It restored the file at once with `--restore`. The tool reports the
blob back to HEAD's `4130858b8f8b` and `git diff HEAD` empty, and `git
status --porcelain` is empty.
- After the run, the showcase `dist/` was restored from turbo's cache
for the clean source: sha256 `cc1034dd9d23…`, as before the smoke. The
stray runtime bundle of the edited compile was deleted.

Nothing of the edit is in this diff. A real `sys_file` was uploaded
through `/api/v1/storage/upload/presigned` → `PUT` → `/upload/complete`
(a 64×64 red PNG) and written to "Specimen — Full"'s `f_image`.

| record page | `[data-record-picture]` | image |
|---|---|---|
| Field Zoo "Specimen — Full" (`f_image` set) | 1, beside the title in
the record header, `rounded-md` (an `image` field, not `avatar`) |
`src="/api/v1/storage/files/b1f4e384-…"`, `alt=""`, loaded,
`naturalWidth` 64 |
| Field Zoo "Specimen — Minimal" (`f_image` empty) | 0 | none, as
objectui#11619 specifies for an empty value |

**Verdict:** at this pin the record header draws the record's picture
from the object's `imageField`, drawing the stored file through
`/api/v1/storage/files/:id`, and draws nothing when the value is empty.
`showcase_task`'s record page is the custom `task-detail` page, which
has no `page:header`, so it draws no record chrome to put a picture in.
That page was smoked too: it renders its path bar, highlights and
sections with 0 page errors.

**Console messages across the run:** 0 page errors. The only failed
loads are a 401 on `GET /api/v1/auth/get-session` (the pre-login probe)
and a 404 for `/favicon.ico`. There is no `/api/v1/usage/storage` 404.
objectstack-ai#21625's and objectstack-ai#21710's smokes both recorded one, and objectui#11002
(`d2e859936`) stopped the request on a runtime that does not serve it.

## Gates and tests (head `4b9519ea23`)

- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` derived 127 commands from the 22-path diff, and all were run
at `4b9519ea23`. `--ran` reports "127 derived, 127 run, 0 NOT-MEASURED,
0 UNRUN", with every exit code recorded.
- Two exited 3 (`PREREQUISITE NOT MET`) on the first pass:
`check:skill-examples` (no `client-react` dist) and
`check:dual-build-cjs-loads` (no dist for 34 packages).
- Both exited 0 after `turbo run build --filter=!@objectstack/docs` (71
of 72 tasks were cache hits), and those are the codes recorded.
- Also exit 0:
- `check:objectui-pin-citations --verify-anchors` with objectui at the
pin: 54 asserting citations match `9dfaca654`, and 7 anchor content
assertions are verified.
- `check:objectui-bump` (20 assertions across 5 cases),
`check:sdui-lockstep`, `check-sdui-manifest`, `check:console-sha`,
`check:console-injection`, `check:migration-registry`.
- `@objectstack/spec check:generated`: all 15 artifacts current after
the `--fix`.
- `pnpm --filter @objectstack/spec exec vitest run` (both projects,
`local` and `repo`): 668 files, 19259 passed, 1 todo. `pnpm --filter
@objectstack/spec typecheck`: exit 0.
- `@objectstack/sdui-parser` test (14 files, 225 passed) and typecheck:
exit 0.
- These suites read the regenerated manifest:
- `@objectstack/lint` test: 119 files, 5627 passed. It declares
`sdui.manifest.json` as a test input.
- `@objectstack/metadata-protocol`
`src/protocol.runtime-authoring-gate.test.ts`: 70 passed.
- `@objectstack/cli` unit tier `src/utils/sdui-manifest.test.ts` and
`test/validate-build-gate-parity.test.ts`: 2 files, 79 passed.
- `test/jsx-gate-manifest-notice.e2e.test.ts` belongs to neither CLI
tier; it runs nightly, so it is NOT MEASURED here and is left to CI.
- `eslint --no-inline-config --format json` on the 15 changed TS files:
15 files, 0 errors and 0 warnings. The lint population is
`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` (`eslint.config.mjs:971`). The
config enables no type-aware linting (`:327-328`), so this diff cannot
move a verdict on an untouched file. Repo-wide `pnpm lint` is left to
CI.

## Acceptance notes

- `main` moved two commits past this branch's base (objectstack-ai#21783, objectstack-ai#21780).
Neither touches a path this diff touches, so no merge was made. The
merge queue rebuilds on the current `main`.
- Anchors in records that cite no asserting sha are outside the
pin-citation gate's population and were not re-measured. Several point
into files that changed here: `containers.tsx` anchors in
`ui/component.zod.ts` docblocks with no sha, and `component.test.ts`'s
`plugin-dashboard/src/index.tsx:204` (the `ObjectMetricPropsSchema icon
liveness` test, already reading an unrelated line at `ab1879721595`, as
objectstack-ai#21710 noted).
- After this pin, the spec's snake_case grid-key prescription ("these
come in once the widget reads a camelCase spelling") describes a
condition that now holds. objectstack-ai#21768 retargets it when it declares the
camelCase keys. It is left as is here, because the pin bump changes no
accept set.
- Writes: one draft PR through the relay and the report comment. No
label, no PR assignee, no ready flag and no auto-merge were written,
because this dispatch's write budget names none of them.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…objectstack-ai#21994)

Fixes objectstack-ai#21989
Clause-②: no

## What this is

This PR adds the curated release page for 17.7.0,
`content/docs/releases/v17/17-7.mdx` (1,402 lines). It was written after
the publish: npm `latest` moved on 2026-10-06, and
`@objectstack/spec@17.7.0` went out at 12:22:14Z. It also wires the page
in:

- `content/docs/releases/v17/meta.json`: `17-7` comes ahead of `17-6`.
- `content/docs/releases/v17/index.mdx`: the status blockquote, the
minors warning, the per-release list and the checklist links now name
17.7.0 as current. This is the same shape objectstack-ai#21362 used for 17.6.0.
- `scripts/docs-audit/handwritten-docs.json`: the page joins the
docs-accuracy audit scope.
- `content/docs/releases/v17/17-6.mdx`: one dated correction
(2026-10-06) on the anonymous-endpoints known issue. objectstack-ai#21158 was closed
as not planned on 2026-10-04.

The page follows the 17.6 page's structure:

1. Highlights.
2. What's new: the counts, and the runtime changes that happen silently.
3. Breaking changes and migration, triaged by door and subject. It
includes a coverage table that names the section carrying the migration
for every ADR-0087 entry added since 17.6.0 (8 conversions, 41 D3
entries).
4. New capabilities.
5. Notable fixes. Security fixes are described only as classes and
doors.
6. New in Console: each objectui declared-breaking change, with this
repo's answer.
7. The code that shipped but is not listed.
8. The upgrade checklist. Every line is marked *Not exercised.*

## Sources and counts

- The version commit `4e4e881427` (objectstack-ai#21352) consumed 323 changesets. 322
are new. One, `748b240` (objectstack-ai#21270), shipped in 17.6.0 and is listed again;
the page explains this in its own section.
- 69 CHANGELOGs carry a 17.7.0 section, and 48 of them have entries.
Their 444 entries (194 minor, 250 patch) de-duplicate to the 323
changesets.
- Two commits landed on `main` after the Version Packages PR's last
refresh and before it merged:
  - `8a399b2b15` (objectstack-ai#21977, for objectstack-ai#21923)
  - `04e776b39a` (objectstack-ai#21976, for objectstack-ai#21968)

Both are ancestors of the version commit (exit 0 for each), so the
17.7.0 packages carry their code. But the version commit did not consume
their changesets, so no 17.7.0 CHANGELOG line names them. The
release-integrity audit named both in a warning.
- objectui: the pin moved five times and ends at `0abd4f9f8769`:
  - `89cad75d5570` (objectstack-ai#21380)
  - `ab1879721595` (objectstack-ai#21625)
  - `2e818d0b51ec` (objectstack-ai#21710)
  - `9dfaca654311` (objectstack-ai#21800)
  - `0abd4f9f8769` (objectstack-ai#21827)

Across 173 commits, 254 changesets were added and 229 of them release
something. 65 are declared breaking, plus one commit marked `!`. The
Console table answers each.
- `PROTOCOL_VERSION` is still 17.0.0.

## Premise corrections

- The dispatch named two pin moves ending at `9dfaca654311`. The tree
has five, ending at `0abd4f9f8769` (`8832655`, objectstack-ai#21827). The page covers
all five.
- One new D3 id contains a word that `check:role-word` refuses on docs
pages, and release pages are not in its baseline. The coverage table
therefore names that entry by its subject and points at `os migrate meta
--from 17`.

## Fact-check

A second pass checked every cited SHA, PR number, key name and
behavioural claim against the commits, changesets and registry entries.
It corrected **31 claims** (commit `e4a6280b46`).

Two more corrections came earlier, while drafting:
- objectstack-ai#21361 is closed; it is not tracking the issue.
- There are seventeen `ui-object-*` members, not eighteen.

Mechanical checks on the final page:

- All 276 cited SHAs resolve, in objectstack or in an objectui clone
carrying the final pin's history.
- Each of the 216 distinct sha–PR pairs matches its commit subject.
- Every printable new D3 id (40) and all 8 conversions appear on the
page.
- The MDX for 17-7, 17-6 and index compiles with @mdx-js/mdx 3.1.1 and
remark-gfm 4.0.1.

After the fact-check, `main` gained `1abfc58` (objectstack-ai#21985), which lands the
read half of objectstack-ai#21922. It is not an ancestor of the version commit: the
test returned exit 1, and the control commit `753e7a1` returned exit 0.
So in 17.7.0, the metadata door's reads still serve a stored row under a
code-defined datasource name. Commit `8347e0d172` says so in the
datasource migration bullet.

## Independent fact-check and fix round

An independent, read-only fact-check of head `8347e0d172` returned
**FAIL** with 13 findings (record: objectstack-ai#21989 comment 6018402030): 2 wrong
facts (a flow's `get_record` node still reads the stored-metadata
tables, in projected form), 1 security line that named the filter shapes
and depth threshold evading 17.6.0's refusal, 1 breaking change missing
from the Breaking section (`0fc8087`, objectstack-ai#21626), 1 link whose label did
not match its target, 4 overstatements, 1 missing security fix
(`49524f6`, objectstack-ai#21420), 1 missing rollback caveat on `os secret rewrap
--apply`, and 2 minor wording issues.

All 13 were re-verified against their sources and applied in
`a53c972fa7`; none was refuted. A scan for any other line naming a
bypass shape of a fixed issue reduced two more lines to their class
(`0728cbf`, `fb69825`).

## Measured on `a53c972fa7`

- Derived gates: `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` derives 57 commands; all 57
exited 0 (`--ran`: "57 run, 0 NOT-MEASURED (a DERIVED zero)"). The
verdicts include:
  - check-doc-anchors: 458 links resolve.
- check-issue-citations: 305 resolve as a PR, 9 resolve, 15 are
cross-repo; every citation this change adds resolves.
- `check:role-word`, `check:release-notes` and
`check:release-page-status`: OK.
- check-release-section-coverage: OK, both plain and with `--strict` (10
minors).
  - The docs-audit scope check and `check:nul-bytes`: OK.
- Docs production build: `TURBO_FORCE=true pnpm turbo run build
--filter=@objectstack/docs`, run under the verify lock, reports `Tasks:
2 successful, 2 total` and `Cached: 0 cached`. The built
`releases/v17/17-7.html` carries the corrected text and none of the
removed text.
- Mechanical checks: 231 distinct sha–PR pairs, 0 unresolved, 0 subject
mismatches; the MDX of 17-7, 17-6 and index compiles.
- Not measured locally: the repo-wide lint and the CI-only families. CI
owns them.

## Not in this PR

- No changeset. The PR touches only docs and a docs-audit list, nothing
a package ships (`skip-changeset`).
- Nobody has walked the 17.6.0 → 17.7.0 upgrade. The checklist says so
on each line.

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

---------

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

Projects

None yet

2 participants