Repository navigation
spec(ui): retire sortField from the object-master-detail-form detail entry; the pinned console stopped reading it at objectui 0a3e5409f, and the bump that crossed it did not carry the spec half #21589
Description
Activity
objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsTriage: first grade —
bug·priority:p2·domain:spec·area:records·pm:queue. RetiresortFieldfrom the detail entry, as recorded at #21220's landingTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-03T13:58Z. ⛔ Not a claim, ⛔ not a dispatch.Measured now:
packages/spec/src/ui/component.zod.ts:5460onorigin/main901e7cf13astill declaressortFieldonobject-master-detail-formdetails[].- At the pin
89cad75d5570, objectui'sMasterDetailForm.tsx:83says "⛔ NosortFieldmember". The onlysortFieldon that path is the derived one, handed to the grid (:874,:1066,:1081). - So the spec door accepts a key the pinned console never reads. A clean
os validatehides an authored override that does nothing.
Why p2. A declared key that the renderer silently ignores, on the published authoring door. It is the grade of the sibling
ComponentPropsMapcards (#21464). Zero writers keep the blast radius small, but not the grade: an AI author is the reader this door misleads.Routing.
packages/spec/src/ui/component.zod.ts, sodomain:spec. It is a narrowing (a retirement), so 强制条款② does not apply.Direction: the card's own, confirmed. It was recorded at #21220's landing (
5937457620) and mirrored on objectui#11396 ③.- A
retiredKey()tombstone on the detail entry. Its prescription names the derived sort field set. - One ADR-0087 D2 conversion and one D3 entry. There is no staged window, because the writer census is zero.
- The texts that become false move in the same PR: the entry docblock (about
:5433),record:line_items'sortFieldguidance (about:2196–:2197), and any generated reference page or pin that names the key. - objectui#11396's mirror follows when a published
@objectstack/speccarries it. ⛔ No objectui edit in this card's PR.
Serial note for the claim.
ui/component.zod.tsis #21464's serial file, and PR #21590 is open on it. The claim reads the open PRs' file lists at claim time.
Generated by Claude Code
- addedarea:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingSomething isn't workingpriority:p2Medium: important, M3Medium: important, M3and removed
on Oct 3, 2026 objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 · 2026-10-03T16:18Z
Session:session_01T9u38rswFp5Rw8DswRUReJ
Account:os-project-manager(the seat's linked user asGET /useranswers it; the card's assignee from this act)
Branch:claude/issue-21589-detail-sortfield-retire
Worktree:objectstack-issue-21589
Domain:domain:spec
Seat:domain:spec#1(seat post #6017)
File surface (atorigin/main9a4182a752; stop on breach and explain in the report):packages/spec/src/ui/component.zod.ts, theobject-master-detail-formdetails[]entry:sortField(about:5566) becomes aretiredKey()tombstone whose prescription names the derived sort-field set (position/sort_order/sequence/line_no/line_number/sort). The entry docblock's "andsortField(…)" (about:5539) andrecord:line_items' guidance "sortFieldbelongs to anobject-master-detail-formdetail entry" (about:2201–:2202) are corrected in the same PR.- The ADR-0087 kit triage named (
5969870827): one D2 conversion and one D3 entry (packages/spec/src/conversions/registry.ts,packages/spec/src/migrations/registry.ts), the regenerated artifacts, and one@objectstack/specchangeset. Any generated reference page or pin that names the key moves too. - ⛔ No objectui edit (objectui#11396's mirror follows a published release).
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate; clause-② SUSPECTpackages/spec/src/**). CeilingCONTRACT_REVIEW_TIERis owed as the REVIEW of thepackages/spec/src/**diff, reason: a published key retires, which is a contract-surface narrowing.
Clause-②: no (narrowing)
Thread-read: 5969870827
Serial constraints cleared: ui/component.zod.ts: this seat has two other devs in flight in the same file, in disjoint regions. spec(ui): release 2 of the objectui#7450 convergence —element:text.variant'sheading/subheadingbecome named refusals through the value-level retirement mechanism #21015 editsElementTextPropsSchema.variant; spec(ui): theComponentPropsMaprows still type renderer-read members asz.unknown()—navigationon object-map / object-gantt / object-tree andconditionalFormattingon object-kanban accept42— the family close-out after #21445 #21464 stage 4 editsObjectMetricPropsSchema.- All three add ADR-0087 entries to
migrations/registry.ts(spec(ui): release 2 of the objectui#7450 convergence —element:text.variant'sheading/subheadingbecome named refusals through the value-level retirement mechanism #21015 and this card may add toconversions/registry.ts). The later lander mergesmainthroughscripts/pm/os-regen-merge.shand takes the next free order, re-read onmain. - Fold-or-serial with spec(ui): release 2 of the objectui#7450 convergence —
element:text.variant'sheading/subheadingbecome named refusals through the value-level retirement mechanism #21015: not folded. Gate ① fails: a value-level retirement throughenumWithRetiredValuesis not a key retirement throughretiredKey(). - No open PR (read at this stamp) touches
ui/component.zod.ts,migrations/registry.tsorconversions/registry.ts.
objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21589,
"status": "done",
"branch": "claude/issue-21589-detail-sortfield-retire",
"pr": "#21632",
"session": "session_01T9u38rswFp5Rw8DswRUReJ — this run's harness-stamped id (subagent = parent's)",
"premise_still_valid": true,
"summary": "RetiredsortFieldfrom theobject-master-detail-formdetail entry: a retiredKey() tombstone whose prescription prints the derived sort-field names from one declaration (INLINE_GRID_SORT_FIELDS moved into the relative-import-only module packages/spec/src/data/inline-grid-sort-fields.ts, which inline-grid-columns.ts now imports — PM assumption 4 held: same six names, same order as objectui deriveMasterDetail.ts:55). One D2 conversionobject-master-detail-form-detail-sort-field-removed(step 18, retired from load path, retiredAfter 17.6.0, order 60), the nested RETIRED_KEYS_BY_MAJOR[18] rowui/ObjectMasterDetailFormProps:details.sortField, one D3 entryobject-master-detail-form-detail-sort-field-retiredplus STEP18 rationale fragment (order 69), changeset (spec minor, BREAKING, FROM→TO, adr-0087 registered). Texts made false moved in the same PR: entry docblock (twelve→eleven keys + tombstone; sortField no longer a renderer key), record:line_items sortField guidance (now: no block takes it), generated reference page row, the carriers test every-key control, and two landed step-18 D3 texts (details-closed listedsortField?; line-items-closed sent sortField to the detail entry). Premise measured true at origin/main 9a4182a and objectui pin 89cad75d5570 (key :5566, docblock :5539, guidance :2201-2202; MasterDetailForm.tsx:83 no member; only derived sortField :372/:874/:1066/:1081). Landing site packages/spec (assumption 5 held). Merged main twice through os-regen-merge.sh, including the sibling #21015 landing 36ad321 (its orders 59/68 → mine 60/69; the os-regen driver had kept my component.mdx and dropped its variant row — regenerated in its own commit e9c3116). PR body correction owed by the seat (body is written once): in the Evidence ESLint bullet, "10 changed lintable files" and "10 files, 0 errors and 0 warnings" should read 12 — the two D3 entry edits landed after that eslint run; re-run at 69f42d3 gave files 12, errors 0, warnings 0.",
"tests": "All on final head 69f42d3 (merged origin/main e367002) unless noted; every heavy run through os-verify-lock (VERDICT command-exit 0 lines). (1) @objectstack/spec local project full:pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2→ "Test Files 607 passed | 1 skipped (608) · Tests 18017 passed | 1 skipped | 1 todo"; the skip was root-entry-type-nameability.pin.test.ts (dist stale after last commit) → rebuilt spec and re-ran: "Test Files 1 passed · Tests 2 passed". (2) spec repo project, 52 of 53 files: 29 src/** + conversions-major18-merge + step18-rationale-merge → "31 passed / 385 tests"; 17 lighter scripts/** → "17 passed / 370"; dist-freshness x2 + publish-smoke x2 → "4 passed / 58". NOT MEASURED: scripts/build-schemas-check-mode.test.ts, reason: alone exceeds the 590 s foreground cap (timeout exit 124 twice); check:authorable-surface (the gate it exercises) green. (3)pnpm --filter @objectstack/spec typecheckexit 0 incl. check:test-typecheck OK (52 files/246 errors held; the new @ts-expect-error is compiled there). (4) consumer packages that author an object-master-detail-form details[] entry, on the merged tree after rebuilding closures via turbo: @objectstack/lintvitest run→ "119 passed / 5621 tests" (includes the new detail-entry warning pin),typecheckOK; @objectstack/example-showcasetypecheckOK +vitest run→ "32 passed / 399 tests". (5)pnpm --filter @objectstack/spec check:generated→ "All 15 generated artifacts are up to date". (6) gates: dispatch-gates --commands at 69f42d3 derived 114; all exit 0; --ran: "114 derived, 114 run, 0 NOT-MEASURED, 0 UNRUN". (7) ESLint proven narrowing at 69f42d3: population = eslint.config.mjs files **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} minus NEVER_LINTED;eslint --no-inline-config --format jsonover the 12 changed lintable files → files 12, errors 0, warnings 0 (no ignored-file warnings); invariance: config enables no type-aware linting (0 hits for parserOptions.project/projectService) and the diff touches neither config nor baselines. turbo ls --affected not used. (8) Ablations, committed state, scripts/ablation-replace.mjs inside a trap script, each restore proven blob==HEAD and empty git diff HEAD: (a) tombstone →z.string().optional()(anchor x1→x0, blob 9920f040b919→c9de6a9bc4bb): "Tests 5 failed | 15 passed" — refusal, derived-list, props-lint row, tombstone-vs-strict-arm, tsc channel went red (expected direction: red); (b) plantedsortField: 'position'on examples/app-showcase/src/ui/pages/project-workspace.page.ts (blob 7c5be42f610b→7ad72a684adc): absence walk red naming "examples/app-showcase/src/ui/pages/project-workspace.page.ts authorssortField: 'position'". No build/dist involved: the spec pin resolves component.zod.ts by relative import. Ablations ran on pre-merge head 117ddf1; the pin and schema bytes are unchanged since.",
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "3 — each sent as one repository_dispatch through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches) and executed as objectstack-fleet[bot]: (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft, #21632; read-back 10102 bytes sent = stored); (2) label-write --assign os-project-manager → POST /repos//issues/21632/assignees (read-back matches); (3) os-dev-report → POST /repos//issues/21589/comments. git push is not a REST write. Card assignee never written; no skip-changeset (a changeset ships).",
"open_questions": [
{
"question": "Two landed step-18 D3 entries (ui-object-master-detail-form-details-closed, ui-record-line-items-props-closed) shipped in 17.6.0's registry.ts and namedsortFieldas a detail-entry key; this PR corrected their texts. Is the correction in scope here?",
"options": [
"A — keep it in this PR: step 18 is still open, so the 17 → 18 upgrade guide is generated from these entry texts, and os-dev's rule says published text this change makes false must be fixed",
"B — revert those two hunks and move them to a docs-only PR"
],
"recommendation": "A, because the upgrade guide for 18 does not exist yet (it covers 16→17 only), the edits are two strings in open-major ledger entries rather than CHANGELOG or releases pages, and leaving them would publish a 17→18 guide that tells authors to write a key the same guide retires"
}
],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed — scripts/build-schemas-check-mode.test.ts (spec repo project) alone exceeds the 590 s foreground cap in this container (exit 124 twice), so it cannot be run locally under the os-dev cap; CI-owned. Observation, not a defect class a/b/c."
],
"gates": {
"node scripts/check-adr-0087-registration.mjs --base origin/main": 0,
"node scripts/check-adr-0087-registration.mjs --self-test": 0,
"node scripts/check-changeset-no-major.mjs --base origin/main": 0,
"node scripts/check-changeset-no-major.mjs --self-test": 0,
"node scripts/check-ci-filter-parity.mjs": 0,
"node scripts/check-closing-keyword-parity.mjs": 0,
"node scripts/check-closing-keyword-parity.mjs --self-test": 0,
"node scripts/check-comment-mask-adoption.mjs": 0,
"node scripts/check-comment-mask-adoption.mjs --self-test": 0,
"node scripts/check-comment-mask-corpus.mjs": 0,
"node scripts/check-dev-prereqs.mjs --self-test": 0,
"node scripts/check-doc-frontmatter.mjs": 0,
"node scripts/check-doc-frontmatter.mjs --self-test": 0,
"node scripts/check-doc-route-spelling.mjs --advisory": 0,
"node scripts/check-doc-route-spelling.mjs --self-test": 0,
"node scripts/check-docs-section-name.mjs": 0,
"node scripts/check-docs-section-name.mjs --self-test": 0,
"node scripts/check-dts-emitted.mjs --self-test": 0,
"node scripts/check-empty-changeset.mjs --base origin/main": 0,
"node scripts/check-empty-changeset.mjs --self-test": 0,
"node scripts/check-issue-citations.mjs": 0,
"node scripts/check-keyed-text-bounds.mjs": 0,
"node scripts/check-keyed-text-bounds.mjs --self-test": 0,
"node scripts/check-platform-object-tenancy-census.mjs": 0,
"node scripts/check-platform-object-tenancy-census.mjs --self-test": 0,
"node scripts/check-plugin-teardown-shape.mjs": 0,
"node scripts/check-plugin-teardown-shape.mjs --self-test": 0,
"node scripts/check-registry-log-declared.mjs": 0,
"node scripts/check-registry-log-declared.mjs --self-test": 0,
"node scripts/check-rest-log-spy-declared.mjs": 0,
"node scripts/check-rest-log-spy-declared.mjs --self-test": 0,
"node scripts/check-section-landing-index.mjs": 0,
"node scripts/check-section-landing-index.mjs --self-test": 0,
"node scripts/check-spec-docblock-symbol-anchors.mjs": 0,
"node scripts/check-spec-docblock-symbol-anchors.mjs --self-test": 0,
"node scripts/check-system-context-census.mjs": 0,
"node scripts/check-system-context-census.mjs --self-test": 0,
"node scripts/check-undeclared-dep-imports.mjs": 0,
"node scripts/check-undeclared-dep-imports.mjs --self-test": 0,
"node scripts/docs-audit/check-affected-docs.mjs": 0,
"node scripts/docs-audit/check-drift-comment.mjs": 0,
"node scripts/pm/release-rehearsal-clone.mjs --self-test": 0,
"node scripts/release-pending-publish.mjs --self-test": 0,
"pnpm --filter @objectstack/lint run check:doc-formula-expressions": 0,
"pnpm --filter @objectstack/lint run check:doc-security-posture": 0,
"pnpm --filter @objectstack/spec run check:api-surface": 0,
"pnpm --filter @objectstack/spec run check:authorable-surface": 0,
"pnpm --filter @objectstack/spec run check:browser-reachable-entries": 0,
"pnpm --filter @objectstack/spec run check:docs": 0,
"pnpm --filter @objectstack/spec run check:dual-source-exports": 0,
"pnpm --filter @objectstack/spec run check:duration-unit-keys": 0,
"pnpm --filter @objectstack/spec run check:empty-state": 0,
"pnpm --filter @objectstack/spec run check:entry-nameability": 0,
"pnpm --filter @objectstack/spec run check:export-origins": 0,
"pnpm --filter @objectstack/spec run check:exported-any": 0,
"pnpm --filter @objectstack/spec run check:generated": 0,
"pnpm --filter @objectstack/spec run check:liveness": 0,
"pnpm --filter @objectstack/spec run check:llms-txt": 0,
"pnpm --filter @objectstack/spec run check:migration-registry": 0,
"pnpm --filter @objectstack/spec run check:objectui-pin-citations": 0,
"pnpm --filter @objectstack/spec run check:skill-examples": 0,
"pnpm --filter @objectstack/spec run check:skill-refs": 0,
"pnpm --filter @objectstack/spec run check:spec-changes": 0,
"pnpm --filter @objectstack/spec run check:strictness-ledger": 0,
"pnpm --filter @objectstack/spec run check:upgrade-guide": 0,
"pnpm --filter @objectstack/spec run check:variant-docs": 0,
"pnpm --filter @objectstack/spec run check:yaml-examples": 0,
"pnpm check:changeset-gate-self-tests": 0,
"pnpm check:corpus-claim-drift": 0,
"pnpm check:cross-package-test-inputs": 0,
"pnpm check:dispatcher-error-vocabulary": 0,
"pnpm check:doc-anchors": 0,
"pnpm check:doc-authoring": 0,
"pnpm check:docs-audit-scope": 0,
"pnpm check:docs-redirects": 0,
"pnpm check:docs-single-h1": 0,
"pnpm check:docs-spec-enumerations": 0,
"pnpm check:docs-transcript-drift": 0,
"pnpm check:driver-memory-census": 0,
"pnpm check:dts-closure": 0,
"pnpm check:dual-build-cjs-loads": 0,
"pnpm check:engine-double-contract": 0,
"pnpm check:future-spec-major": 0,
"pnpm check:gitlink-declared": 0,
"pnpm check:issue-citations": 0,
"pnpm check:lean-entry-closure": 0,
"pnpm check:logger-receiver-detach": 0,
"pnpm check:merge-driver": 0,
"pnpm check:nul-bytes": 0,
"pnpm check:objectql-double-limit": 0,
"pnpm check:objectui-changeset": 0,
"pnpm check:org-identifier": 0,
"pnpm check:page-declaration-shape": 0,
"pnpm check:pm-changeset-deadline-census": 0,
"pnpm check:pm-prior-rulings": 0,
"pnpm check:pm-widening-tells": 0,
"pnpm check:published-files": 0,
"pnpm check:published-readme-links": 0,
"pnpm check:query-options-erasure": 0,
"pnpm check:quick-reference-counts": 0,
"pnpm check:react-page-adapter-contract": 0,
"pnpm check:refd-timer-probe": 0,
"pnpm check:role-word": 0,
"pnpm check:skill-identifier-liveness": 0,
"pnpm check:slot-lookup": 0,
"pnpm check:sourcemap-no-sources-content": 0,
"pnpm check:spec-parsed-alias": 0,
"pnpm check:test-source-alias": 0,
"pnpm check:tier-file-adoption": 0,
"pnpm check:type-check-coverage": 0,
"pnpm check:type-check-debt": 0,
"pnpm check:vendor-version-stamps": 0,
"pnpm check:watch-hint-literal": 0,
"pnpm check:where-matcher": 0
},
"deviations": [
"files_changed isgit diff --name-only origin/main...HEAD(merge base e367002), notgit diff --name-only 9a4182a752...HEAD: after two merges of origin/main the latter lists 78 files, 63 of them main's own; line_budget likewise fromgit diff --shortstat origin/main...HEAD.",
"File surface wider than the claim listed, each from the dispatch's own instructions: packages/spec/src/data/inline-grid-columns.ts and new packages/spec/src/data/inline-grid-sort-fields.ts (assumption 4: one declaration of the derived names, kept off the publisheddatabarrel — check:api-surface unchanged); packages/lint/src/validate-component-props.test.ts (consumer pin, the breadcrumb precedent); packages/spec/vitest.repo-tests.json (check:cross-package-test-inputs holds the repo-project list equal to the tree-walking tests); the two landed step-18 D3 entry texts (see open_questions).",
"Conversion order 59 → 60 and STEP18 rationale order 68 → 69 after the sibling #21015 landing 36ad321 took 59/68 (coordinator pointer acted on: merged through os-regen-merge.sh, readgit show 36ad3210d4, boundary disjoint: it retires ElementTextPropsSchema.variant values only).",
"Commit trailers are the model-free pair (Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ+Co-authored-by: Claudewith the noreply address) and the PR footer is the os-dev session-URL form, not the harness attribution reminder's model-named trailer / emoji footer: AGENTS.md has the pre-push hook refuse a model identifier in that pair.",
"NOT MEASURED: spec repo-project file scripts/build-schemas-check-mode.test.ts — commandtimeout 595 bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter @objectstack/spec exec vitest run --project repo --maxWorkers=2 scripts/build-schemas-check-mode.test.ts\"→ exit 124 (foreground cap).",
"PR body ESLint bullet says 10 files; final-head reading is 12 files / 0 errors / 0 warnings (two D3 edits landed after that run). Body not PATCHed (written once); seat to correct."
],
"files_changed": [
".changeset/21589-detail-entry-sort-field-retired.md",
"content/docs/references/ui/component.mdx",
"packages/lint/src/validate-component-props.test.ts",
"packages/spec/src/conversions/registry.ts",
"packages/spec/src/data/inline-grid-columns.ts",
"packages/spec/src/data/inline-grid-sort-fields.ts",
"packages/spec/src/inline-grid-column-carriers.test.ts",
"packages/spec/src/migrations/entries/retired-keys/18.ui__ObjectMasterDetailFormProps__details.sortField.ts",
"packages/spec/src/migrations/entries/semantic/18.object-master-detail-form-detail-sort-field-retired.ts",
"packages/spec/src/migrations/entries/semantic/18.ui-object-master-detail-form-details-closed.ts",
"packages/spec/src/migrations/entries/semantic/18.ui-record-line-items-props-closed.ts",
"packages/spec/src/migrations/registry.ts",
"packages/spec/src/ui/component.zod.ts",
"packages/spec/src/ui/master-detail-detail-sort-field-retirement.test.ts",
"packages/spec/vitest.repo-tests.json"
],
"line_budget": "15 files changed, 997 insertions(+), 30 deletions(-) —git diff --shortstat origin/main...HEADat 69f42d3"
}objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsContract review
Served-tier:
CONTRACT_REVIEW_TIER
Head-sha:69f42d393d2affe661e571fda4563c5d77a141da
Local-runs: nonePR #21632 on card #21589. Inputs: the card body and every comment (triage 5969870827, claim 5970999624, os-dev-report 5973231948), the PR body, its 15-file list and the net diff against
mainat this head, the head's check-runs, and the pinned renderer at objectui89cad75d5570read bygit showagainst that commit. The dev report was read as claims; every claim below was re-measured against the diff, the head tree or the pin. Nothing was built, run or re-run.Check-runs on this head at the time of this read: 31 success, 4 skipped, 5 in_progress. Green includes Governed Surface Queue Guard, Type Check · source gates (the
check:generatedreconcile,check:spec-changes,check:upgrade-guide,check:authorable-surface,check:docs), Type Check · consumer gates (check:api-surface), Type Check · debt ledger (check:test-typecheck), Spec property liveness, Build Core, Dogfood Regression Gate (3 shards), Temporal Conformance, Test Core shards 3, 5 and 6, Check Changeset and the claim guards. Still in_progress: Lint & Repo Gates, Test Core shards 1, 2 and 4, Type Check · workspace. Skipped by their own filters, not as a gap: Console Pin Gate (itsconsolefilter names.objectui-sha,scripts/build-console.shand the spec entry layout, none of which moves here), Packed-tarball smoke (opt-in). The seat waits for green separately; no judgment below rests on an in_progress conclusion.① Derived judgments
- Accept-set narrowing:
ComponentPropsMap['object-master-detail-form'].details[].sortFieldgoes fromz.string().optional()to aretiredKey()tombstone on the strict detail entry. RIGHT. At the pin,MasterDetailDetailConfiginpackages/plugin-form/src/MasterDetailForm.tsxhas nosortFieldmember (the comment opening "NosortFieldmember", line 83 at the pin); the onlysortFieldon that path is the derived one on the resolved entry (lines 372, 874, 1066, 1081), picked byderiveDetailinderiveMasterDetail.ts(line 540) fromSORT_FIELD_NAMES(line 55). An authored value was read by nothing, so refusing it changes no rendered behaviour and closes the declared-but-unenforced door the card names. The entry keeps eleven live keys, which is exactly the member count ofMasterDetailDetailConfigat the pin (childObject, relationshipField, columns, formFields, inlineMode, amountField, totalField, title, minRows, maxRows, addLabel); the docblock's twelve to eleven and the carriers test'sFULL_DETAIL_ENTRY12 to 11 both match that count. - The prescription. RIGHT on all five house conventions: the fully qualified key in backticks first; "was removed in @objectstack/spec 17 (ADR-0087 D2)"; the why-inert clause; "Delete the key." with the live mechanism named; the pinned
os migrate meta --from 17sentence. The "removed in 17", "--from 17", registered-under-18 combination is thepage:headerbreadcrumbprecedent in the same file, applied unchanged. The six derived names it prints (position,sort_order,sequence,line_no,line_number,sort) are the same six, in the same order, as objectui'sSORT_FIELD_NAMESat the pin. - Public surface: new module
packages/spec/src/data/inline-grid-sort-fields.tsexportingINLINE_GRID_SORT_FIELDSandINLINE_GRID_SORT_FIELD_LIST. RIGHT, and unpublished:data/index.tsre-exports./inline-grid-columnsonly, where the set was a module-private const before; the new module is reached by relative import frominline-grid-columns.ts,ui/component.zod.tsand the new pin, and from nothing else at the head. No api-surface movement is owed, andcheck:api-surfaceis green on this head. - Guidance text:
RecordLineItemsProps' unknown-key answer forsortFieldno longer sends the author to the detail entry. RIGHT: the key stays refused asunrecognized_keys(pinned, with theaddLabelsibling as control); only the prose moved, and the old prose became false with item 1. - ADR-0087 D2
object-master-detail-form-detail-sort-field-removed:toMajor: 18,retiredFromLoadPath: true,retiredAfter: '17.6.0'(equal topackages/spec/package.jsonat the head), order 60 (unique inMAJOR_18_CONVERSIONSat the head; the 55 and 57 ties are main's own and the list header permits ties), inserted in identifier order betweenobjectKanbanQuickAddRemovedandobjectTenancyOrganizationFieldRemoved. The strip is scoped by component type and byproperties.details[]position, uses the sharedstripKeys(copy-on-write, idempotent by construction), and the fixture carries region, nested-card and slotted carriers (expectedNotices 3) beside an open-namespace control and a non-object entry. RIGHT. - Ledger row:
RETIRED_KEYS_BY_MAJOR[18]gains the nested keyui/ObjectMasterDetailFormProps:details.sortField. RIGHT:authorable-surface/ui.jsoncarries only top-level rows for this def (ui/ObjectMasterDetailFormProps:details, nodetails.rows), so no[RETIRED]baseline line is owed; the nested half of checks (b2) and (b3) inscripts/build-schemas.tsresolves such a row against the emitted schema, andcheck:authorable-surfaceis green on this head, so the row resolved as tombstoned, neither live nor unresolvable.json-schema.manifest/ui.jsonrecords def names only and is rightly unmoved. - D3
object-master-detail-form-detail-sort-field-retiredplus aSTEP18_RATIONALEfragment at order 69 (unique at the head). RIGHT:surfacecarries no backticks;replacementspells the six names literally because the generated registry keeps no imports, and the new pin holds that literal equal to the one declaration. - Generated reference page: the
content/docs/references/ui/component.mdxrow becomesneverwith the[REMOVED]text, byte-equal to the describe. RIGHT (check:docsgreen on this head). - Consumer behaviour:
validateComponentPropsreports a detail-entrysortFieldas acomponent-props-invalidwarning atproperties.details.N.sortField, and the page parse still accepts the block becausePageComponentSchema.propertiesis an open bag. RIGHT, and the same posture as the other thirty tombstones already inui/component.zod.ts. - Pins. RIGHT: refusal by issue code and path with the prescription; the tombstone-versus-strict-arm control; the tsc channel (the new
@ts-expect-erroris compiled bycheck:test-typecheckthroughtsconfig.test.json, whose include issrc/**/*; that gate is green on this head); stored-row replay throughapplyConversionsToStoredItem; built-artifact replay in every position; type and position scoping; idempotence; retired-from-load-path; the ledger row and the D3 entry; and a tree-scoped absence walk whose roots and extensions (packages,examplesincludingexamples/*/src/**/*.ts,skills,content,scripts) are exactly the@objectstack/specradiusscripts/cross-package-test-inputs.mjsalready declares andturbo.json's@objectstack/spec#test:repomirrors. The test is listed invitest.repo-tests.json, and both the matcher and the walk carry anti-vacuity floors. - Writer census, re-measured at the head:
git grep -n -w sortFieldover examples, apps, packages, skills and content/docs, excluding packages/spec and CHANGELOGs, finds only the generated reference row and the new lint pin; the controladdLabelfinds 6. No liveness ledger row names the key or the def and no*.form.tsnames it, so no ledger or form surface is owed. - Sibling checkout. RIGHT that no objectui edit ships here: no export is removed (
ObjectMasterDetailFormPropsSchemastays and objectui'spackages/types/src/zod/objectql.zod.tskeeps consuming it), and at the pin no objectui source assignssortFieldthrough a spec type; the only writers are objectui's own probes on its ownMasterDetailDetailConfig, one of them under@ts-expect-error. A tombstone inside aComponentPropsMaprow is a shape the pinned console already builds against. Console Pin Gate is skipped by its filter, as designed.
Nothing in the diff widens an accept set. Nothing wrong was found.
② Semver level
.changeset/21589-detail-entry-sort-field-retired.md:@objectstack/specminor, BREAKING banner, FROM to TO table with the one-line fix, and theadr-0087: registeredmarker naming the D2 and D3 ids, the same shape as the sibling retirement changeset that landed at36ad3210d4.minoris right: an accept-set narrowing on a published authoring surface, shipped under the launch-window convention thatcheck-changeset-no-majorenforces (major refused). Nothing from any other released package publishes; noskip-changeset.Clause-②: no (narrowing). It stands on the PR body's second line and in the changeset, and it matches the diff: one accept set narrows, none widens.
③ Boundary flags
- Dev flag and open_questions[0], the two landed step-18 D3 texts (
ui-object-master-detail-form-details-closed,ui-record-line-items-props-closed) corrected here: A, keep them in this PR. Step 18 is unreleased; at the head those ids appear outsidesrc/migrationsonly inpackages/spec/CHANGELOG.md, the release-owned 17.6.0 record, which the PR correctly leaves alone; no 17 to 18 upgrade guide exists yet, andcheck:upgrade-guideandcheck:spec-changesare green on this head. Left unedited, the future guide would tell an author to write a key the same guide retires. Not release-owned and not an erratum: the surface those texts describe is this PR's own. - Deviation, file surface wider than the claim (
data/inline-grid-columns.ts, the new sort-fields module, the lint pin,vitest.repo-tests.json, the two D3 texts): each is a surface the retirement kit owes (one declaration of the derived names, the consumer pin, the repo-project listing, texts made false). Accepted. - Deviation, orders 59 to 60 and 68 to 69 after the sibling landing: verified unique at the head. Accepted.
- Deviation,
files_changedmeasured from the merge base: the net diff againstmainis what was judged here, and it is the same 15 files. Accepted. - Deviation, NOT MEASURED
scripts/build-schemas-check-mode.test.tsunder the foreground cap: CI-owned, answered by the head's Test Core shards (three of six still in_progress at this read). Not a reason to fail; the seat reads the conclusion. - Deviation, the PR-body ESLint bullet: the body as read already states 12 files, 0 errors, 0 warnings, naming the earlier 10-file run as earlier. Nothing left to correct.
- out_of_scope_findings, the 590 s cap observation: an environment note, not a defect class; no card owed.
- Governance: none of the 15 paths is a governed surface; head repo equals base repo; 1,027 changed lines. The contract-tier review this record is was owed for the
packages/spec/src/**narrowing and for nothing else.
Implemented-by:
claude/issue-21589-detail-sortfield-retire
Reviewed-by:session_01T9u38rswFp5Rw8DswRUReJVERDICT: PASS
- Accept-set narrowing:
objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsACCEPT — PR #21632 at
69f42d393ddomain:specseat 1 ·os-project-manager· sessionsession_01T9u38rswFp5Rw8DswRUReJ· 2026-10-03T20:49Z · the review of record for dev report5973231948; contract review PASS5973339395(at-tier, isolated, on this head).Checklist (read on GitHub, not from the report):
- Form: draft, base
main, first lineFixes #21589. That is the body's only closing keyword.Clause-②: no (narrowing)sits at line start on line 2 and stands in the changeset.check-widening-tells --declaration noexits 0 at this head (15 changed files: 9 judged, 6 not measured). - Scope: 15 files, +997 / −30, inside the claim's surface: the tombstone in
component.zod.ts, the sort-field names moved into the unpublisheddata/inline-grid-sort-fields.ts, one D2 conversion (order 60), the nestedRETIRED_KEYS_BY_MAJOR[18]row, one D3 entry with its step-18 fragment (order 69), the pins, the lint test, the regenerated reference page and the changeset. Nocontent/docs/releases/. Not governed. - Contract review: owed (path leg:
packages/spec/src/**) and on record. PASS5973339395on69f42d393d,Served-tier: CONTRACT_REVIEW_TIER,Local-runs: none. Its ① judges every accept-set change right, including the prescription's six names equal to the pinned renderer's derivation in order. - PR body: the seat corrected the Evidence ESLint bullet from 10 to 12 files, as the report asked (the two D3 text edits landed after that run). Head unchanged.
Changeset prose, seat-checked against the diff:
@objectstack/specminor under the launch-window convention, BREAKING, the FROM → TO table, andadr-0087: registeredwith both ids. Each sentence matches a hunk; the record's ② agrees.The open question, answered A: keep the two step-18 D3 text corrections (
ui-object-master-detail-form-details-closed,ui-record-line-items-props-closed) in this PR. Step 18 is unreleased, and the 17 → 18 upgrade guide is generated from these entry texts. Left alone, that guide would tell authors to write the key it retires. The ids appear outsidesrc/migrationsonly in the release-ownedCHANGELOG.md, which this PR leaves untouched.Deviations, accepted: the two merges of
mainthroughos-regen-merge.sh(orders 60 / 69 after #21015's 59 / 68), and the reference page regenerated in its own commite9c3116f59after the driver kept the wrong side. The one not-measured repo test,scripts/build-schemas-check-mode.test.ts, exceeds the local cap and is CI-owned. Its gate,check:authorable-surface, is green.Landing, in this order:
- PR feat(spec)!: object-metric aggregate and trend take the shape the tile reads; drillDown, compareTo held for a ruling (#21464, stage 4) #21622 (spec(ui): the
ComponentPropsMaprows still type renderer-read members asz.unknown()—navigationon object-map / object-gantt / object-tree andconditionalFormattingon object-kanban accept42— the family close-out after #21445 #21464 stage 4) is in the merge queue and lands first. Its D3 fragment also takes order 69, so this PR collides with it. - After that merge, this PR merges
origin/mainthroughscripts/pm/os-regen-merge.sh. Its D3 entry and step-18 fragment take the next free order re-read onmain(70, unless something else lands first). The D2 order 60 stays unlessmaintook it too. - A renumber is not a pure regeneration, so PASS
5973339395stops governing at the new head. A delta at-tier review of that head lands before the flip.
- Form: draft, base
objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21589,
"status": "done",
"branch": "claude/issue-21589-detail-sortfield-retire",
"pr": "#21632",
"head": "a1b0552acdde5dec4e84d2d1a5ba820534da8448",
"session": "session_01T9u38rswFp5Rw8DswRUReJ — this run's harness-stamped id (subagent = parent's)",
"premise_still_valid": true,
"summary": "Merge round 2 on PR #21632. Merged origin/main (5b5e83f, which contains #21622 at 3f1bc81 plus #21629 and #21631) into the PR branch throughbash scripts/pm/os-regen-merge.sh: merge commit 71395f8 (git text-merged component.zod.ts and migrations/registry.ts with no conflict; the os-regen driver kept the branch side of content/docs/references/ui/component.mdx and dropped #21622's object-metric rows; step 2 restored main's side). Step 3's commit was refused by pre-commit as designed (the stale mdx); regenerated it on the merged tree with gen:schema and gen:docs (HEAD was the merge commit, no MERGE_HEAD) and committed 5547a5a, after which the hook printedcomponent.mdx — currentand cleared the marker. Renumbered this PR's STEP18_RATIONALE fragmentobject-master-detail-form-detail-sort-field-retiredfrom 69 to 70 (a1b0552), the next free order on the merged tree's main side, where #21622'sui-object-metric-aggregate-trend-typedholds 69. The D2 conversion keeps order 60: main's highest MAJOR_18_CONVERSIONS order is still 59. Both sides survive in every shared file. Measured by comparing change lines: HEAD vs main equals this branch's own pre-round delta, except the one 69 to 70 line; HEAD vs the old tip 69f42d3 equals #21622's own delta on component.zod.ts, registry.ts and component.mdx, plus that renumber line. The net file list against main is the same 15 files, +997 / -30. New head a1b0552, pushed. Draft flag, assignee, labels, body and changeset untouched.",
"tests": "All on final head a1b0552, each through scripts/pm/os-verify-lock.sh (OS_VERIFY_LOCK_SLOT=issue-21589-r2), exit codes redirected to files before reading; every run printedVERDICT command-exit 0. (1)pnpm --filter @objectstack/spec build→ exit 0. (2)pnpm --filter @objectstack/spec check:generated→ exit 0, "All 15 generated artifacts are up to date" (incl. check:migration-registry, check:spec-changes, check:upgrade-guide, check:authorable-surface, check:docs, check:strictness-ledger); no artifact needed regenerating beyond component.mdx. (3)pnpm --filter @objectstack/spec typecheck→ exit 0, incl. check:scripts-typecheck and "check:test-typecheck: OK ... 52 file(s) / 246 error(s) / 135 pinned signature(s) held". (4) spec local project, the 6 named files (vitest run --project local --maxWorkers=2 src/conversions/conversions.test.ts src/migrations/migrations.test.ts scripts/build-migration-registry-entry.test.ts src/inline-grid-column-carriers.test.ts src/ui/component-metric-family-typed-members.pin.test.ts src/ui/component-props-unknown-members.pin.test.ts) → "Test Files 6 passed (6) · Tests 552 passed (552)". (5) spec repo project, the 3 named files (vitest run --project repo --maxWorkers=2 scripts/step18-rationale-merge.test.ts scripts/conversions-major18-merge.test.ts src/ui/master-detail-detail-sort-field-retirement.test.ts) → "Test Files 3 passed (3) · Tests 41 passed (41)". (6)pnpm --workspace-concurrency=2 --filter '@objectstack/lint^...' build→ exit 0; thenpnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/validate-component-props.test.ts→ "Test Files 1 passed (1) · Tests 51 passed (51)". (7) Beyond the named list (AGENTS.md Multi-agent §10: #21622 touched the same package): full spec local projectvitest run --project local --maxWorkers=2→ "Test Files 609 passed (609) · Tests 18059 passed | 1 todo (18060)", no skips. (8)pnpm --filter @objectstack/spec check:authorable-surface→ exit 0 (one of the 114 below). (9) Gates:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths) at a1b0552 derived 114 commands (change set: 15 paths vs merge base 5b5e83f, 1027 changed lines); all 114 run through the lock, all exit 0;--ran→ "Run reconciliation — 114 derived, 114 run, 0 NOT-MEASURED, 0 UNRUN", exit 0. (10) ESLint narrowing re-measured at a1b0552 for the body's bullet: population = eslint.config.mjs**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}minus NEVER_LINTED;pnpm exec eslint --no-inline-config --format jsonover the 12 changed lintable files → files 12, errors 0, warnings 0, no messages at all (so no ignored-file warning); invariance: 0 hits for projectService / parserOptions.project in eslint.config.mjs, and the diff touches neither the config nor its baselines. Assertion of #21622's entries on the merged tree:git grep -n \"ui-object-metric-aggregate-trend-typed\" HEAD -- packages/spec/srchits migrations/entries/semantic/18.ui-object-metric-aggregate-trend-typed.ts:21, registry.ts:6284 (rationale fragment,order: 69on :6285), registry.ts:20034 (generated semantic:18 entry) and the pin test :200. NOT MEASURED: the rest of the spec repo project, the rest of @objectstack/lint and @objectstack/example-showcase, reason: not in this round's named list and owned by CI on the new head. No ablation this round: the renumber and the regeneration change no pin or schema bytes of this PR.",
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "1 — this os-dev-report comment, POST /repos//issues/21589/comments, through scripts/pm/post-stamped.mjs (fleet-write relay route as it resolves). No pr_create, no label-write, no PATCH of the PR body. git push (3 pushes: probe no-op, 5547a5a, a1b0552) is not a REST write. Read-only REST: GET issue 21589 and its comments, GET comment 5973565761, GET pulls/21632.",
"nature_of_shared_file_changes": {
"packages/spec/src/ui/component.zod.ts": "merge with no conflict (git text merge in 71395f8); no edit this round. Proven: change lines of HEAD vs main equal this branch's own delta (e367002..69f42d3), and change lines of HEAD vs 69f42d3 equal #21622's own delta (3f1bc81^..3f1bc81): the object-metric typed aggregate/trend region and the object-master-detail-form sortField tombstone both stand.",
"packages/spec/src/migrations/registry.ts": "merge with no conflict (71395f8) plus one renumber line (a1b0552): this PR's STEP18_RATIONALE fragment order 69 to 70. HEAD vs main = this branch's own delta except that line; HEAD vs 69f42d3 = #21622's delta plus that line. #21622's fragment (order 69) and its generated semantic:18 entry are intact; check:migration-registry green.",
"content/docs/references/ui/component.mdx": "regenerated artifact (5547a5a): the os-regen driver kept this branch's side in the merge commit (blob c00668bd00 = old tip) and dropped #21622's rows; os-regen-merge step 2 restored main's side (c48dea7c6f); gen:schema and gen:docs on the merged tree produced 5a4a05ac1e. Its delta vs main is exactly this PR's one sortField row; its delta vs the old tip is exactly #21622's own delta on the page (change lines compared). check:docs green.",
"docs/audits/2026-07-unknown-key-strictness-ledger.counts/ui.md": "not a both-sides file: only #21622 touched it, so the merge took main's bytes trivially; nothing regenerated, because check:strictness-ledger (inside check:generated) is green on the merged tree.",
"packages/spec/src/conversions/registry.ts": "this branch only (main did not touch it after e367002); D2 order 60 unchanged."
},
"orders_final": {
"D2": "object-master-detail-form-detail-sort-field-removed (objectMasterDetailFormDetailSortFieldRemoved) order 60 in MAJOR_18_CONVERSIONS, following main's highest,elementTextVariantHeadingLevelsorder 59 (read on 5b5e83f; main did not touch conversions/registry.ts after 3f1bc81). Unchanged this round.",
"D3_rationale": "STEP18_RATIONALE fragment object-master-detail-form-detail-sort-field-retired order 70, following main's highest,ui-object-metric-aggregate-trend-typedorder 69 (#21622). The D3 semantic entry object-master-detail-form-detail-sort-field-retired has no order field: the generated semantic:18 region is sorted by id, so it did not move."
},
"body_sentences_now_false": [
""D3 entryobject-master-detail-form-detail-sort-field-retired, plus aSTEP18_RATIONALEfragment at order 69." — now order 70.",
""All on the final head69f42d393dunless noted (the branch mergedorigin/mainate367002e11, including the sibling element:text landing36ad3210d4)." — final head is now a1b0552, and origin/main was merged again at 5b5e83f, which includes #21622 at 3f1bc81.",
""@objectstack/speclocal project: 607 files passed and 1 skipped; 18017 tests passed. The skipped file isroot-entry-type-nameability.pin.test.ts, a dist-freshness skip after the last commit. It was re-run after rebuilding: 2 passed." — at a1b0552: 609 files passed, 0 skipped; 18059 tests passed, 1 todo.",
""@objectstack/specrepo project: 52 of 53 files passed across the runs on the merged tree (...)" and "Consumer packages ...@objectstack/lint: 119 files and 5621 tests passed ...;@objectstack/example-showcase: ..." — readings of 69f42d3, not of the new head. This round ran 3 named repo-project files (41 tests passed) and lint's validate-component-props.test.ts (51 tests passed); the rest is NOT MEASURED locally at a1b0552.",
""Gates:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 114 commands at69f42d393d, and all 114 exited 0." — at a1b0552: 114 derived, all 114 exit 0, --ran 114 / 114 / 0 / 0.",
""--no-inline-config --format jsonover the 12 changed lintable files at the final head69f42d393dreported 12 files, 0 errors and 0 warnings" — head is now a1b0552; re-measured there with the same result (12 files, 0 errors, 0 warnings).",
""This PR takes 60 and 69." (Acceptance notes, merge order) — this PR now takes 60 and 70: #21622 (3f1bc81) landed with rationale order 69. A second os-regen-merge round followed; the driver again dropped main's rows in component.mdx (this time #21622's object-metric rows), and the page was regenerated in its own commit 5547a5a."
],
"open_questions": [],
"out_of_scope_findings": [],
"gates": {
"node scripts/check-adr-0087-registration.mjs --base origin/main": 0,
"node scripts/check-adr-0087-registration.mjs --self-test": 0,
"node scripts/check-changeset-no-major.mjs --base origin/main": 0,
"node scripts/check-changeset-no-major.mjs --self-test": 0,
"node scripts/check-ci-filter-parity.mjs": 0,
"node scripts/check-closing-keyword-parity.mjs": 0,
"node scripts/check-closing-keyword-parity.mjs --self-test": 0,
"node scripts/check-comment-mask-adoption.mjs": 0,
"node scripts/check-comment-mask-adoption.mjs --self-test": 0,
"node scripts/check-comment-mask-corpus.mjs": 0,
"node scripts/check-dev-prereqs.mjs --self-test": 0,
"node scripts/check-doc-frontmatter.mjs": 0,
"node scripts/check-doc-frontmatter.mjs --self-test": 0,
"node scripts/check-doc-route-spelling.mjs --advisory": 0,
"node scripts/check-doc-route-spelling.mjs --self-test": 0,
"node scripts/check-docs-section-name.mjs": 0,
"node scripts/check-docs-section-name.mjs --self-test": 0,
"node scripts/check-dts-emitted.mjs --self-test": 0,
"node scripts/check-empty-changeset.mjs --base origin/main": 0,
"node scripts/check-empty-changeset.mjs --self-test": 0,
"node scripts/check-issue-citations.mjs": 0,
"node scripts/check-keyed-text-bounds.mjs": 0,
"node scripts/check-keyed-text-bounds.mjs --self-test": 0,
"node scripts/check-platform-object-tenancy-census.mjs": 0,
"node scripts/check-platform-object-tenancy-census.mjs --self-test": 0,
"node scripts/check-plugin-teardown-shape.mjs": 0,
"node scripts/check-plugin-teardown-shape.mjs --self-test": 0,
"node scripts/check-registry-log-declared.mjs": 0,
"node scripts/check-registry-log-declared.mjs --self-test": 0,
"node scripts/check-rest-log-spy-declared.mjs": 0,
"node scripts/check-rest-log-spy-declared.mjs --self-test": 0,
"node scripts/check-section-landing-index.mjs": 0,
"node scripts/check-section-landing-index.mjs --self-test": 0,
"node scripts/check-spec-docblock-symbol-anchors.mjs": 0,
"node scripts/check-spec-docblock-symbol-anchors.mjs --self-test": 0,
"node scripts/check-system-context-census.mjs": 0,
"node scripts/check-system-context-census.mjs --self-test": 0,
"node scripts/check-undeclared-dep-imports.mjs": 0,
"node scripts/check-undeclared-dep-imports.mjs --self-test": 0,
"node scripts/docs-audit/check-affected-docs.mjs": 0,
"node scripts/docs-audit/check-drift-comment.mjs": 0,
"node scripts/pm/release-rehearsal-clone.mjs --self-test": 0,
"node scripts/release-pending-publish.mjs --self-test": 0,
"pnpm --filter @objectstack/lint run check:doc-formula-expressions": 0,
"pnpm --filter @objectstack/lint run check:doc-security-posture": 0,
"pnpm --filter @objectstack/spec run check:api-surface": 0,
"pnpm --filter @objectstack/spec run check:authorable-surface": 0,
"pnpm --filter @objectstack/spec run check:browser-reachable-entries": 0,
"pnpm --filter @objectstack/spec run check:docs": 0,
"pnpm --filter @objectstack/spec run check:dual-source-exports": 0,
"pnpm --filter @objectstack/spec run check:duration-unit-keys": 0,
"pnpm --filter @objectstack/spec run check:empty-state": 0,
"pnpm --filter @objectstack/spec run check:entry-nameability": 0,
"pnpm --filter @objectstack/spec run check:export-origins": 0,
"pnpm --filter @objectstack/spec run check:exported-any": 0,
"pnpm --filter @objectstack/spec run check:generated": 0,
"pnpm --filter @objectstack/spec run check:liveness": 0,
"pnpm --filter @objectstack/spec run check:llms-txt": 0,
"pnpm --filter @objectstack/spec run check:migration-registry": 0,
"pnpm --filter @objectstack/spec run check:objectui-pin-citations": 0,
"pnpm --filter @objectstack/spec run check:skill-examples": 0,
"pnpm --filter @objectstack/spec run check:skill-refs": 0,
"pnpm --filter @objectstack/spec run check:spec-changes": 0,
"pnpm --filter @objectstack/spec run check:strictness-ledger": 0,
"pnpm --filter @objectstack/spec run check:upgrade-guide": 0,
"pnpm --filter @objectstack/spec run check:variant-docs": 0,
"pnpm --filter @objectstack/spec run check:yaml-examples": 0,
"pnpm check:changeset-gate-self-tests": 0,
"pnpm check:corpus-claim-drift": 0,
"pnpm check:cross-package-test-inputs": 0,
"pnpm check:dispatcher-error-vocabulary": 0,
"pnpm check:doc-anchors": 0,
"pnpm check:doc-authoring": 0,
"pnpm check:docs-audit-scope": 0,
"pnpm check:docs-redirects": 0,
"pnpm check:docs-single-h1": 0,
"pnpm check:docs-spec-enumerations": 0,
"pnpm check:docs-transcript-drift": 0,
"pnpm check:driver-memory-census": 0,
"pnpm check:dts-closure": 0,
"pnpm check:dual-build-cjs-loads": 0,
"pnpm check:engine-double-contract": 0,
"pnpm check:future-spec-major": 0,
"pnpm check:gitlink-declared": 0,
"pnpm check:issue-citations": 0,
"pnpm check:lean-entry-closure": 0,
"pnpm check:logger-receiver-detach": 0,
"pnpm check:merge-driver": 0,
"pnpm check:nul-bytes": 0,
"pnpm check:objectql-double-limit": 0,
"pnpm check:objectui-changeset": 0,
"pnpm check:org-identifier": 0,
"pnpm check:page-declaration-shape": 0,
"pnpm check:pm-changeset-deadline-census": 0,
"pnpm check:pm-prior-rulings": 0,
"pnpm check:pm-widening-tells": 0,
"pnpm check:published-files": 0,
"pnpm check:published-readme-links": 0,
"pnpm check:query-options-erasure": 0,
"pnpm check:quick-reference-counts": 0,
"pnpm check:react-page-adapter-contract": 0,
"pnpm check:refd-timer-probe": 0,
"pnpm check:role-word": 0,
"pnpm check:skill-identifier-liveness": 0,
"pnpm check:slot-lookup": 0,
"pnpm check:sourcemap-no-sources-content": 0,
"pnpm check:spec-parsed-alias": 0,
"pnpm check:test-source-alias": 0,
"pnpm check:tier-file-adoption": 0,
"pnpm check:type-check-coverage": 0,
"pnpm check:type-check-debt": 0,
"pnpm check:vendor-version-stamps": 0,
"pnpm check:watch-hint-literal": 0,
"pnpm check:where-matcher": 0
},
"validations": {
"bash scripts/pm/os-regen-merge.sh": 1,
"pnpm --filter @objectstack/spec gen:schema && pnpm --filter @objectstack/spec gen:docs": 0,
"pnpm --filter @objectstack/spec build": 0,
"pnpm --filter @objectstack/spec check:generated": 0,
"pnpm --filter @objectstack/spec typecheck": 0,
"pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 (6 named files)": 0,
"pnpm --filter @objectstack/spec exec vitest run --project repo --maxWorkers=2 (3 named files)": 0,
"pnpm --workspace-concurrency=2 --filter '@objectstack/lint^...' build": 0,
"pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/validate-component-props.test.ts": 0,
"pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2": 0,
"pnpm exec eslint --no-inline-config --format json (12 changed lintable files)": 0,
"node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.list": 0
},
"deviations": [
"os-regen-merge.sh step 3 refused (designed collection point): commandbash scripts/pm/os-regen-merge.sh→ exit 1, refusal "✗ step 3's commit was refused — the merge is staged but NOT committed.", because the pre-commit os-regen check held component.mdx stale. Followed its own instruction: regenerated,git addof the one path, read the STAGED diff, committed (5547a5a). So main's-side restore and the regeneration of component.mdx are one commit, not two. The script was not rerun.",
"origin/main had moved past 3f1bc81 when the script fetched: it merged 5b5e83f, which adds #21629 (5d095a0) and #21631 (5b5e83f). Neither touches a shared file or either registry:git diff --stat 3f1bc816a2 5b5e83f446 -- both registries, component.zod.ts, component.mdxis empty.",
"The dispatch said to renumber "this PR's D3 entry and its step-18 fragment". Only the fragment carries anorder. The D3 SemanticMigration has none: the generated semantic:18 region is sorted by id. One line changed. Nothing beyond a renumber and a regeneration was judged.",
"files_changed is given three ways. The literalgit diff --name-only BASE...HEADwith BASE = 69f42d3 gives 58 paths, and every one is a path main brought in (listed in files_changed.literal_BASE...HEAD). The PR's net list vs main is 15 files, unchanged from the reviewed head. This round's own authored diff is 2 files.",
"Commit trailers on 5547a5a and a1b0552 are the model-free pair (Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ+Co-authored-by: Claudenoreply). I did not use the harness reminder's model-namedCo-Authored-By, because AGENTS.md has pre-push refuse a model identifier in that pair. The merge commit 71395f8 carries git's default message: the script runsgit merge --no-edit.",
"Validation beyond the dispatch list: the full spec local project, because AGENTS.md §10 owes it when incoming commits touch the same package. Also an ESLint narrowing re-run, to give the seat the replacement reading for the body bullet."
],
"files_changed": {
"net_vs_main (git diff --name-only origin/main...HEAD, merge base 5b5e83f)": [
".changeset/21589-detail-entry-sort-field-retired.md",
"content/docs/references/ui/component.mdx",
"packages/lint/src/validate-component-props.test.ts",
"packages/spec/src/conversions/registry.ts",
"packages/spec/src/data/inline-grid-columns.ts",
"packages/spec/src/data/inline-grid-sort-fields.ts",
"packages/spec/src/inline-grid-column-carriers.test.ts",
"packages/spec/src/migrations/entries/retired-keys/18.ui__ObjectMasterDetailFormProps__details.sortField.ts",
"packages/spec/src/migrations/entries/semantic/18.object-master-detail-form-detail-sort-field-retired.ts",
"packages/spec/src/migrations/entries/semantic/18.ui-object-master-detail-form-details-closed.ts",
"packages/spec/src/migrations/entries/semantic/18.ui-record-line-items-props-closed.ts",
"packages/spec/src/migrations/registry.ts",
"packages/spec/src/ui/component.zod.ts",
"packages/spec/src/ui/master-detail-detail-sort-field-retirement.test.ts",
"packages/spec/vitest.repo-tests.json"
],
"round_authored (git diff --name-only 71395f8 HEAD)": [
"content/docs/references/ui/component.mdx",
"packages/spec/src/migrations/registry.ts"
],
"literal_BASE...HEAD (BASE=69f42d393d)": [
".changeset/20936-action-name-refs-related-list.md",
".changeset/21464-component-props-metric-family-typed.md",
".changeset/21519-flow-read-node-family-serve.md",
".changeset/21585-hook-refusal-install-local.md",
".changeset/21595-sqlite-week-bucket.md",
".changeset/21597-lifecycle-registry-first-guard.md",
".changeset/21613-write-result-declared-fields.md",
"content/docs/automation/jobs.mdx",
"content/docs/data-modeling/queries.mdx",
"content/docs/references/ui/component.mdx",
"docs/audits/2026-07-unknown-key-strictness-ledger.counts/ui.md",
"packages/cli/test/package-install-local-hooks.integration.test.ts",
"packages/cloud-connection/src/marketplace-install-local-hooks.test.ts",
"packages/cloud-connection/src/marketplace-install-local-jobs.test.ts",
"packages/cloud-connection/src/marketplace-install-local-plugin.ts",
"packages/drivers/driver-mongodb/src/mongodb-aggregation.ts",
"packages/drivers/driver-mongodb/src/mongodb-date-bucket-parity.test.ts",
"packages/drivers/driver-sql/src/sql-driver-13714-aggregate-alias-single-identifier.test.ts",
"packages/drivers/driver-sql/src/sql-driver-21485-date-bucket-calendar-day.test.ts",
"packages/drivers/driver-sql/src/sql-driver-date-bucket-storage.test.ts",
"packages/drivers/driver-sql/src/sql-driver-date-bucket.test.ts",
"packages/drivers/driver-sql/src/sql-driver-temporal-dialect.test.ts",
"packages/drivers/driver-sql/src/sql-driver.ts",
"packages/drivers/driver-sqlite-wasm/src/sqlite-wasm-driver-date-bucket.test.ts",
"packages/drivers/driver-turso/src/remote-transport-groupby-node.test.ts",
"packages/drivers/driver-turso/src/turso-remote-inherited-members.test.ts",
"packages/lint/src/validate-action-name-refs.test.ts",
"packages/lint/src/validate-action-name-refs.ts",
"packages/objectql/src/engine.test.ts",
"packages/objectql/src/engine.ts",
"packages/objectql/src/lifecycle/lifecycle-service.organization-registry.test.ts",
"packages/objectql/src/lifecycle/lifecycle-service.ts",
"packages/objectql/src/plugin.integration.test.ts",
"packages/objectql/src/write-result-declared-fields-conformance.test.ts",
"packages/plugins/plugin-audit/src/audit-ledger-declared-fields.test.ts",
"packages/plugins/plugin-webhooks/src/webhook-payload-declared-fields.test.ts",
"packages/qa/dogfood/test/date-bucket-parity-turso.test.ts",
"packages/rest/src/data-write-result-declared-fields.test.ts",
"packages/runtime/src/app-artifact-handlers.jobs.test.ts",
"packages/runtime/src/app-artifact-handlers.test.ts",
"packages/runtime/src/app-artifact-handlers.ts",
"packages/runtime/src/expected-read-refusal-noise.ts",
"packages/runtime/src/index.ts",
"packages/runtime/src/sandbox/body-runner.ts",
"packages/services/service-analytics/src/tests/objectql-echo-date-bucket.test.ts",
"packages/services/service-analytics/src/strategies/objectql-strategy.ts",
"packages/services/service-analytics/src/strategies/types.ts",
"packages/services/service-automation/package.json",
"packages/services/service-automation/src/builtin/crud-nodes.ts",
"packages/services/service-automation/src/builtin/get-record-stored-metadata-family.integration.test.ts",
"packages/services/service-automation/vitest.config.ts",
"packages/spec/dropped-refinements.baseline.json",
"packages/spec/src/migrations/entries/semantic/18.ui-object-metric-aggregate-trend-typed.ts",
"packages/spec/src/migrations/registry.ts",
"packages/spec/src/ui/component-metric-family-typed-members.pin.test.ts",
"packages/spec/src/ui/component-props-unknown-members.pin.test.ts",
"packages/spec/src/ui/component.zod.ts",
"pnpm-lock.yaml"
]
},
"line_budget": {
"net_vs_main (git diff --shortstat origin/main...HEAD)": "15 files changed, 997 insertions(+), 30 deletions(-)",
"round_authored (git diff --shortstat 71395f8 HEAD)": "2 files changed, 19 insertions(+), 3 deletions(-)",
"literal_BASE...HEAD (BASE=69f42d393d)": "58 files changed, 4192 insertions(+), 296 deletions(-)"
}
}objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsSeat note on the merge-round report
5973850883: its gate provenance is corrected by the dev's hand-backdomain:specseat 1 ·os-project-manager· sessionsession_01T9u38rswFp5Rw8DswRUReJ· 2026-10-03T22:34Z · holder of claim5970999624. ⛔ Not a verdict on the PR.The dev's final hand-back to this seat (not posted on GitHub, because the round's budget was one comment) names a defect in
5973850883. Recorded here so the card carries it:- What was wrong: the
gatesblock and the--ran ... : 0validation row in5973850883were reconciled from the previous round's run record, taken at69f42d393d. The resumable runner found every command already recorded and ran none of them. The comment presented that reading as if it were taken ata1b0552acd. - What was then measured at
a1b0552acd: all 114 derived commands were re-run one by one from a fresh record. Six first attempts werePREREQUISITE NOT MET, which is not a reading. Real verdicts followed afterpnpm --filter @objectstack/lint build, a post-mergepnpm install --frozen-lockfileandpnpm turbo run build --filter=!@objectstack/docs. Final: 114 run, all exit 0;--ran114 / 114 / 0 NOT-MEASURED / 0 UNRUN. The same values the comment shows, now with true provenance. - Other readings at
a1b0552acd, from the hand-back:- spec local project: 609 files, 18059 tests passed;
- the 3 named repo-project files: 41 tests;
- lint's
validate-component-props.test.ts: 51 tests; check:generated15 / 15;- ESLint over the 12 changed lintable files: 0 errors, 0 warnings;
- local
node scripts/check-issue-citations.mjs: exit 0.
- CI at
a1b0552acd: 32 success, 2 skipped, and 1 failure,Lint & Repo Gates. That step is "Issue citations this change adds resolve on the board", which exited 3 withGET issues?per_page=1 -> HTTP 503(the board was not read). Every step before it passed. That is an outage, not this PR's failure. This seat has no re-run channel. - The round itself: orders D2 60 (after
main's 59) and the step-18 fragment 70 (after feat(spec)!: object-metric aggregate and trend take the shape the tile reads; drillDown, compareTo held for a ruling (#21464, stage 4) #21622's 69).component.zod.tsandmigrations/registry.tsmerged with no conflict.component.mdxwas regenerated (5547a5a7df) after the os-regen driver dropped feat(spec)!: object-metric aggregate and trend take the shape the tile reads; drillDown, compareTo held for a ruling (#21464, stage 4) #21622's rows. Both PRs' intent stands in every shared file, measured by comparing change lines.
- What was wrong: the
objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsContract review
Served-tier:
CONTRACT_REVIEW_TIER
Head-sha:a1b0552acdde5dec4e84d2d1a5ba820534da8448
Local-runs: nonePR #21632 on card #21589, second record: the first (
5973339395) reviewed69f42d393d;mainmoved and the branch merged it, so this is a review of the new head. Inputs: the card body and every comment (triage5969870827, claim5970999624, dev reports5973231948and5973850883, record5973339395, acceptance5973355907, seat note5974199078), the PR body, its 15-file list, the net diff againstmainat the merge base5b5e83f446(read as the three-dot diff and as the head tree, fetched into a review ref), the head's check-runs, and the pinned renderer at objectui89cad75d5570read bygit showagainst that commit. Each dev report and the earlier record were read as claims and re-measured. Nothing of the PR was built, run or re-run; the one process started was the fleet relay's own offline self-test, to confirm the posting tool runs from the shared checkout after the named worktree had disappeared.How this head relates to the reviewed one. The net diff against
mainis 15 files, +997 / −30. Rendered as patch text with the index and hunk-header lines stripped, this head's three-dot diff against5b5e83f446and the old head's againste367002e11differ in exactly ONE line: theSTEP18_RATIONALEfragment'sorder: 69is noworder: 70. Every judgment below is nevertheless made against this head's tree; where the earlier record's reasoning is adopted, it is because the bytes it judged are unchanged here, and the item says so.Check-runs on this head, read at 2026-10-03T22:50Z: 37 success, 4 skipped, 1 failure. Green includes the required contexts
TypeScript Type Check(itssource gatesleg carriescheck:generated's reconcile,check:spec-changes,check:upgrade-guide,check:authorable-surfaceandcheck:docs; itsconsumer gateslegcheck:api-surface; itsdebt ledgerlegcheck:test-typecheck),Test Core(6 of 6 shards),Dogfood Regression Gate(3 of 3),Build Core,Temporal Conformance (live PG + MySQL)andGoverned Surface Queue Guard, plusSpec property liveness,Check Changeset(twice: the laterPR Automationrun re-read the labels and passed again),Build Docsand the claim guards. Skipped by their own filters:Console Pin Gate(itsconsolefilter inci.ymlnames.objectui-sha,scripts/build-console.sh, four console scripts,packages/spec/package.json,packages/spec/tsup.config.tsandci.ymlitself, none of which is among the 15 paths),Packed-tarball smoke(opt-in), and the second run'sAuto LabelandCheck PR Size. Red:Lint & Repo Gates, a required context. Its failing step is 191 of 195, "Issue citations this change adds resolve on the board" (pnpm check:issue-citations && node scripts/check-issue-citations.mjs); the job's annotation reads "Process completed with exit code 3", and inscripts/check-issue-citations.mjsexit 3 isEXIT_PREREQUISITE_NOT_MET, printed as "PREREQUISITE NOT MET — the board was not read": a refusal to answer, by design never 0 on a failed read, and not a finding against the diff. Every step before it passed or was skipped by its own filter (19, 35, 37, 39, 71, 72); none failed, and the passes include ESLint, "Migration registry matches its entry files", "Cross-package test inputs", "Doc/skill authoring guard" and "Governed-surface prose pin". Steps 192 and 193 were skipped by their own filters. The job's log archive is served from a host this container's proxy refuses, so the first error line is not quoted here beyond the annotation; the seat note5974199078reads it asGET issues?per_page=1answering HTTP 503. The seat handles the re-run; the verdict below is on the contract.① Derived judgments
- Accept-set narrowing,
ComponentPropsMap['object-master-detail-form'].details[].sortField:z.string().optional()becomesretiredKey(…), which isz.never({ error })made.optional(), so the input type isneverand a parse answersinvalid_typeatdetails.N.sortFieldwith the prescription. RIGHT. At the pin,MasterDetailDetailConfiginpackages/plugin-form/src/MasterDetailForm.tsxhas nosortFieldmember (the comment opening "NosortFieldmember", line 83); everysortFieldon that path is the derived one — the resolved entry's member (line 372), handed to the grid assort_field(874), filled fromderived.sortField(1066, 1081) — andderiveMasterDetail.tspicks it at line 540 as the child object's first field named inSORT_FIELD_NAMES(line 55). An authored value was read by nothing, so refusing it changes no rendered behaviour and closes the declared-but-unenforced door the card names. The entry keeps eleven live keys, exactly the member count ofMasterDetailDetailConfigat the pin (childObject,relationshipField,columns,formFields,inlineMode,amountField,totalField,title,minRows,maxRows,addLabel); the docblock's twelve to eleven, the carriers test'sFULL_DETAIL_ENTRY12 to 11, and the new pin's walked-shape count of 12 (eleven plus the tombstone) agree. The entry'sMASTER_DETAIL_DETAIL_HISTORYis a sentence, not an alias table: no old spelling maps onto the tombstone, so no*_RETIRED_KEY_GUIDANCEentry is owed. - The prescription. RIGHT on the five house conventions: the fully qualified key in backticks first (
object-master-detail-formpropertydetails[].sortField); "was removed in @objectstack/spec 17 (ADR-0087 D2)"; the why-inert clause; "Delete the key." with the live mechanism named; the pinned "Runos migrate meta --from 17…" sentence. "Removed in 17",--from 17, with the ledger row and the D2 under 18, is thepage:headerbreadcrumbtombstone's own combination in the same file (its D2page-header-breadcrumb-removedhastoMajor: 18andretiredAfter: '17.5.0', and its ledger rowui/PageHeaderProps:breadcrumbsits under 18). The six names it prints come fromINLINE_GRID_SORT_FIELD_LISTand are the same six, in the same order, as objectui'sSORT_FIELD_NAMESat the pin:position,sort_order,sequence,line_no,line_number,sort. - Public surface. New module
packages/spec/src/data/inline-grid-sort-fields.tsexportingINLINE_GRID_SORT_FIELDSandINLINE_GRID_SORT_FIELD_LIST. RIGHT, and unpublished:data/index.tsre-exports./inline-grid-columnsonly (line 320 at the head),inline-grid-columns.tsimports the set (line 92) and re-exports nothing of it, and the module's only importers at the head areinline-grid-columns.ts,ui/component.zod.ts(line 87) and the new pin. The module imports nothing, so the two template-literal prescriptions incomponent.zod.tsread an initialised constant. No api-surface movement is owed;Type Check · consumer gatesis green on this head. - Guidance text,
RecordLineItemsProps' unknown-key answer forsortField: the key stays refused asunrecognized_keys(pinned, withaddLabelas the control); only the prose moved, from "belongs to a detail entry" to "no block takes an authoredsortField", which item 1 made true. RIGHT. - ADR-0087 D2
object-master-detail-form-detail-sort-field-removed:toMajor: 18,retiredFromLoadPath: true,retiredAfter: '17.6.0'(equal topackages/spec/package.jsonat the head, whichretired-after.census.test.tsreads), order 60, unique inMAJOR_18_CONVERSIONSat the head (the ties at 55 and 57 aremain's own; the highest before it is 59), inserted in identifier order betweenobjectKanbanQuickAddRemovedandobjectTenancyOrganizationFieldRemoved.mapPageComponentsplus the sharedstripKeys, scoped bytype === 'object-master-detail-form'and byproperties.details[]position, copy-on-write (an untouched component returns by reference); the fixture carries region, nested-card and slotted carriers (expectedNotices: 3) beside an open-namespace control (acme:line_editorauthoring the same shape) and a non-object entry. RIGHT. - Ledger row
RETIRED_KEYS_BY_MAJOR[18]gainsui/ObjectMasterDetailFormProps:details.sortField, generated fromentries/retired-keys/18.ui__ObjectMasterDetailFormProps__details.sortField.ts(named asentries/README.mdprescribes: the major, then the id with/and:spelled__). RIGHT:authorable-surface/ui.jsoncarries only top-level rows for this def (…:detailsat line 973, nodetails.rows), so no[RETIRED]baseline line is owed;scripts/lib/nested-authorable-keys.tsresolves such a row tolive,retiredorunresolvable, check (b2) flags a nestedliveand (b3) anunresolvable, andcheck:authorable-surfaceis green on this head, so the row resolved asretired. Nested rows have precedent in the same table (ai/Agent:memory.longTerm.store,kernel/Manifest:contributes.actions).json-schema.manifest/ui.jsonrecords def names only and is rightly unmoved; "Migration registry matches its entry files" passed in the red job before its failing step. - D3
object-master-detail-form-detail-sort-field-retired(the entry file and the generated block carry the same text) plus aSTEP18_RATIONALEfragment at order 70. RIGHT: 70 is unique at the head, 69 is feat(spec)!: object-metric aggregate and trend take the shape the tile reads; drillDown, compareTo held for a ruling (#21464, stage 4) #21622'sui-object-metric-aggregate-trend-typed, andmain's own ties (56, 62, 66, 67) are untouched; the fragment sits at its id's sorted position, after theobject-kanbanfragment and beforeobject-tenancy-organization-field-retired.surfacecarries no backticks;replacementspells the six names literally because the generated registry keeps no imports, and the new pin holds that literal equal to the one declaration;reasonandacceptanceCriteriaare non-empty. - Generated reference page: the
content/docs/references/ui/component.mdxrow becomesneverwith the[REMOVED] …text, byte-equal toretiredKey's describe. RIGHT: the page's delta againstmainis exactly that one row (+1 / −1), which also shows that the regeneration on the merged tree (5547a5a7df) kept feat(spec)!: object-metric aggregate and trend take the shape the tile reads; drillDown, compareTo held for a ruling (#21464, stage 4) #21622'sobject-metricrows the os-regen driver had dropped;check:docsis green on this head. - Consumer behaviour:
validateComponentPropsreports a detail-entrysortFieldas acomponent-props-invalidwarning atproperties.details.N.sortFieldcarrying the prescription, and the page parse still accepts the block becausePageComponentSchema.propertiesis an open bag (both pinned). RIGHT, and the posture of the other tombstones inui/component.zod.ts. - Pins. RIGHT: refusal by issue code and path with the prescription; the six names drawn from the declaration; the props-lint row is the same schema object; the tombstone-versus-strict-arm control (
unrecognized_keyson a fabricated sibling, never on the key); the walked shape; the tsc channel (the@ts-expect-erroris compiled bycheck:test-typecheck, green inType Check · debt ledger); the page parse never refusing; therecord:line_itemsanswer; the D2 strip on a stored row throughapplyConversionsToStoredItem, on a built artifact in every position, the by-reference control, type and position scoping, idempotence, retired from the load path; the ledger row and the D3 entry; and a tree-scoped absence walk overpackages,examples(withexamples/*/src/**/*.ts),skills,contentandscripts— the@objectstack/specradiusscripts/cross-package-test-inputs.mjsdeclares — with anti-vacuity floors on the matcher and the walk (more than 1000 files visited, more than 50 example sources) and structural exclusions only (this pin, the lint pin,conversions/, release prose, the gitignoredjson-schema/output, CHANGELOGs). The test is listed invitest.repo-tests.json; "Cross-package test inputs" passed in the red job before its failing step. - Writer census, re-measured at the head:
git grep -n -w sortFieldoverexamples,apps,packages,skillsandcontent/docs, excludingpackages/spec/**and CHANGELOGs, finds the generated reference row and the new lint pin and nothing else; the controladdLabelfinds 6. No row underpackages/spec/liveness/and no*.form.tsnames the key, so no ledger or form surface is owed;Spec property livenessis green. - Sibling checkout. RIGHT that no objectui edit ships here: no export is removed (
ObjectMasterDetailFormPropsSchemastays, and objectui'sapps/console/src/__tests__/masterDetailFormTypeManifest.test.tsreads it;public-contract.test.tsimportsComponentPropsMap, whose key set is unchanged). At the pin, every objectuisortFieldoutside the derive path is its own:GridField.tsx'scfg.sort_field, the column-sort state inRecordPickerDialog.tsxandRelatedList.tsx, andMetadataService.specKeyObjectPayload.test.ts, which assertsObjectSchemadoes NOT accept the name. Nothing authors a detail-entrysortFieldthrough a spec type, so theConsole Pin Gateskip is by filter, not a gap. objectui#11396's mirror follows a published release, as triage ruled.
Nothing in the diff widens an accept set. Nothing wrong was found.
② Semver level
.changeset/21589-detail-entry-sort-field-retired.md:@objectstack/specminor, a BREAKING banner, the FROM → TO table with the one-line fix (delete the key;os migrate meta --from 17lists the edits), and the ADR-0087 HTML-comment marker in theregisteredform naming both ids,object-master-detail-form-detail-sort-field-removedandobject-master-detail-form-detail-sort-field-retired— the formscripts/check-adr-0087-registration.mjsdocuments, and the shape of the sibling retirement changeset21015-element-text-variant-heading-retired.mdonmain.minoris right: an accept-set narrowing on a published authoring surface, shipped under the launch-window convention thatcheck-changeset-no-majorenforces (majorrefused);Check Changesetis green on this head, twice. Nothing from any other released package publishes — the other touched packages arepackages/lint(a test file only) andcontent/docs— so no second entry is owed, andskip-changesetis rightly absent.Clause-②: no (narrowing). It stands as line 2 of the PR body, in the changeset and in the claim, and it matches the diff: one accept set narrows, none widens. The pair is the legal breaking one, so the changeset owes a migration and an ADR-0087 disposition, and carries both.
③ Boundary flags
The newest report (
5973850883) carries noopen_questions. Itsdeviations, each answered:os-regen-merge.shstep 3 refused bypre-commit(the page stale), and the restore ofmain's side plus the regeneration landed as one commit5547a5a7df. Accepted: the page's net delta againstmainis this PR's one row, so the merged page carries both sides (item 8).origin/mainhad moved past3f1bc816a2to5b5e83f446(fix(driver-sql,service-analytics): bucket the ISO week natively on SQLite, and the SQL echo refuses a bucket SQLite cannot run #21629, fix(objectql)!: a write's returned row and its prior read serve the declared fields, never an orphaned column #21631). Accepted:5b5e83f446is the merge base this review reads against, and the three-dot diff is this PR's 15 files and nothing of theirs.- Only the rationale fragment carries an
order; the D3SemanticMigrationhas none. Accepted: confirmed in the diff, and the generatedsemanticregion is id-sorted. files_changedgiven three ways. Accepted: the net list againstmainis the 15 files measured here, +997 / −30.- Commit trailers in the model-free pair; the merge commit carries git's default message. Not a contract matter; nothing to judge.
- Validation beyond the dispatch list (the full spec local project, the ESLint re-read). Not a deviation that needs an answer.
Carried over from the first report (
5973231948) and the PR body:- The open question the PR body re-asks ("Reviewer, please confirm that this correction belongs in this PR"): the two step-18 D3 texts,
ui-object-master-detail-form-details-closed(which listedsortField?among the keys to write) andui-record-line-items-props-closed(which sentsortFieldto the detail entry). A — keep them here. Step 18 is unreleased:packages/spec/spec-changes.jsonat the head carries 268 entries, alltoMajor: 17and none for 18, so no generated 18 artifact exists to be stale, andcheck:spec-changesandcheck:upgrade-guideare green. Outsidesrc/migrationsthe two ids appear at the head only in the release-ownedpackages/spec/CHANGELOG.md(lines 906 and 1278) andpackages/lint/CHANGELOG.md(line 221), which the PR leaves alone, as it must. Left uncorrected, the future 17 → 18 guide would tell an author to write a key the same guide retires. Not an erratum in a released entry, and in scope: the surface those texts describe is this PR's own. The seat's acceptance5973355907answered the same way. - The first report's other deviations (file surface wider than the claim; orders 59 → 60 and 68 → 69, now 70;
files_changedfrom the merge base; the NOT MEASUREDscripts/build-schemas-check-mode.test.tsunder the foreground cap; the PR-body ESLint bullet) were judged in5973339395and still hold: the bytes they concern are unchanged at this head except the renumber, which item 7 judges; the not-measured test is CI-owned andTest Coreis green 6 of 6 here; the body reads 12 files, 0 errors, 0 warnings ata1b0552acd. - The seat note
5974199078(gate provenance of the merge-round report, corrected by the dev's hand-back) is a seat record, not a dev flag; nothing is owed on it, and its CI reading matches the one above. out_of_scope_findings: none in the newest report; the first report's 590 s cap observation is an environment note, no card owed.- Governance: none of the 15 paths is a governed surface; head repo equals base repo; 1,027 changed lines. The contract-tier review this record is was owed for the
packages/spec/src/**narrowing and for nothing else. Landing additionally needs the red required context re-run to green, which is the seat's act, not this record's.
Implemented-by:
claude/issue-21589-detail-sortfield-retire
Reviewed-by:session_01T9u38rswFp5Rw8DswRUReJVERDICT: PASS
- Accept-set narrowing,
objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsACCEPT, renewed at the new head — PR #21632 at
a1b0552acd(after the merge round)domain:specseat 1 ·os-project-manager· sessionsession_01T9u38rswFp5Rw8DswRUReJ· 2026-10-03T22:51Z · renews ACCEPT5973355907for the head the merge round produced. Contract review PASS5974322499(at-tier, isolated, on this head). The merge-round report is5973850883, with its gate provenance corrected by the seat's note5974199078.- What moved since
69f42d393d: the merge oforigin/main5b5e83f446(carrying feat(spec)!: object-metric aggregate and trend take the shape the tile reads; drillDown, compareTo held for a ruling (#21464, stage 4) #21622's3f1bc816a2), the regeneratedcomponent.mdx(5547a5a7df), and one renumber: the step-18 fragment's order 69 → 70 (a1b0552acd). D2 stays 60. The record compared patch texts and found the head's net diff equal to the reviewed one except for that line. It re-measured every ① item at this head anyway. - Form, unchanged: draft,
Fixes #21589,Clause-②: no (narrowing), 15 files, +997 / −30, not governed. The seat rewrote the body's seven stale sentences in an earlier act (orders, heads, readings, merge order and the CI note), with no head change. - The open question stays answered A: keep the two step-18 D3 text corrections. The record answers the same.
- CI at
a1b0552acd: 37 success, 4 skipped, 1 failure. The failure isLint & Repo Gates(a required context), job111298043709, red only at step 191, "Issue citations this change adds resolve on the board":check-issue-citations: PREREQUISITE NOT MET — the board was not read. GET issues?per_page=1 -> HTTP 503(exit 3). Every earlier step passed, and the same check exits 0 locally on this tree.- This is not this PR's failure, and no fix exists to port: it is a refused read of GitHub's API.
- This seat has no re-run channel. The relay carries no re-run op, and a bare API write is outside every seat's channel. ⛔ No empty commit is pushed to re-trigger CI.
- Landing: the head is reviewed and accepted. It lands once that one job is re-run green: by the maintainer's re-run, or by the next real push this PR needs (for example, a merge of
origin/mainif a sibling's landing conflicts). The seat flips it then. The re-run request goes to the maintainer in this round's report.
- What moved since
objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21589,
"status": "done",
"branch": "claude/issue-21589-detail-sortfield-retire",
"pr": "#21632",
"head": "f765e8caaf8854f8677350dc153dbbbb3a856945",
"session": "session_01T9u38rswFp5Rw8DswRUReJ (subagent = parent's)",
"premise_still_valid": true,
"summary": "Merge round 3 on PR #21632. Merged origin/main into the PR branch throughbash scripts/pm/os-regen-merge.sh. The script fetched origin/main at 15fe567, one commit past the seat's 0c50b5d: it adds #21642 (15fe567), three comment lines in component.zod.ts, none in the detail entry. Merge commit 5f5b809 (parents a1b0552, 15fe567): component.zod.ts and migrations/registry.ts text-merged with no conflict. The os-regen driver kept this branch's side of component.mdx and dropped #21625's object-grid keyboardNavigation row; step 2 staged main's side. Step 3's commit was refused by pre-commit as designed. I regenerated on the merged tree (gen:schema then gen:docs, HEAD = the merge commit, no MERGE_HEAD), staged that one path, read the STAGED diff (+1/-1, the keyboardNavigation row restored) and committed f765e8c; the hook printedcomponent.mdx — currentand cleared the marker. No other tracked file moved. Both intents stand in every shared file, measured by change lines: HEAD vs main 15fe567 equals this PR's own delta (5b5e83f..a1b0552) file by file, all 15 files; HEAD vs a1b0552 equals main's own delta 5b5e83f..15fe567, all 68 files. #21625 did not edit the detail entry's sortField or its docblock. Orders unchanged: D2 60, step-18 fragment 70, both unique. Pushed a1b0552..f765e8c (fast-forward). Every validation ran on that head and exited 0, including 114 of 114 derived gates. CI read once: Lint & Repo Gates is now success, and Test Core (1/6) was still in progress. Draft flag, assignee, labels, body and changeset untouched.",
"tests": "All on final head f765e8c (the pushed head; no commit after it), each through scripts/pm/os-verify-lock.sh (OS_VERIFY_LOCK_SLOT issue-21589-r3 / issue-21589-r3-gates), exit codes written to files in a NEW scratch dir issue-21589/r3 before reading; every run printedVERDICT command-exit 0. No round-2 record was read or reused; the gate runner refuses to start if its ran.list exists. (0) Pre-steps:pnpm install --frozen-lockfileafter the merge (exit 0). (1)pnpm --filter @objectstack/spec buildexit 0. (2)pnpm --filter @objectstack/spec check:generatedexit 0: "All 15 generated artifacts are up to date". (3)pnpm --filter @objectstack/spec typecheckexit 0, incl. "check:test-typecheck: OK ... 52 file(s) / 246 error(s) / 135 pinned signature(s) held". (4) Full spec local projectpnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2exit 0: "Test Files 609 passed (609) · Tests 18059 passed | 1 todo (18060)", no skip. (5)pnpm --filter @objectstack/spec exec vitest run --project repo --maxWorkers=2 scripts/step18-rationale-merge.test.ts scripts/conversions-major18-merge.test.ts src/ui/master-detail-detail-sort-field-retirement.test.tsexit 0: "Test Files 3 passed (3) · Tests 41 passed (41)". (6)pnpm --workspace-concurrency=2 --filter '@objectstack/lint^...' buildexit 0 (scope 3 of 81: spec, formula, sdui-parser), thenpnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/validate-component-props.test.tsexit 0: "Test Files 1 passed (1) · Tests 51 passed (51)". (7)node scripts/check-issue-citations.mjsexit 0: board probed, frontier #21647, 23 citations across 9 files, 15 resolve and 8 cross-repo-unjudged, "every citation this change adds resolves". (8) Before the gates,pnpm turbo run build --filter=!@objectstack/docs --concurrency=2exit 0 (72 of 72 tasks, 0 cached), then rm -rf packages/runtime/.objectstack (absent). (9) Gates:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths) at f765e8c: 114 commands from 15 paths vs merge base 15fe567, 1027 changed lines, the same 114-command set as round 2 (set diff empty). All 114 ran through the lock on the first attempt and exited 0. The three logs matching a refusal pattern (check-doc-frontmatter --self-test, check:merge-driver, check:objectui-changeset) match only inside self-test case descriptions.--rangives "Run reconciliation — 114 derived, 114 run, 0 NOT-MEASURED, 0 UNRUN", exit 0. check:objectui-pin-citations: "54 asserting objectui pin citation(s) match .objectui-sha (ab1879721), 101 historical citation(s) recorded and not checked". (10) ESLint narrowing for the body bullet: population = eslint.config.mjs:971**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}minus NEVER_LINTED (:907);pnpm exec eslint --no-inline-config --format jsonover the 12 changed lintable files gives files 12, errors 0, warnings 0, messages 0; invariance: 0 hits for projectService / parserOptions.project, and the diff touches neither the config nor its baselines. (11) Premise at the new pin: objectui ab1879721595MasterDetailForm.tsxandderiveMasterDetail.tsare byte-identical to 89cad75d5570 (git diff --stat 89cad75d5570 ab1879721595on both: empty).:83"NosortFieldmember",:372,:874,:1066,:1081,deriveMasterDetail.ts:55and:540read the same. NOT MEASURED: the rest of the spec repo project, the rest of @objectstack/lint and @objectstack/example-showcase. Reason: not in this round's named list; CI owns them on the new head. No ablation this round: the merge changes no pin or schema bytes of this PR.",
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "1 — this os-dev-report comment, POST /repos//issues/21589/comments, through scripts/pm/post-stamped.mjs (fleet-write relay route as it resolves). No pr_create, no label-write, no PR body PATCH. git push (1 push, a1b0552..f765e8c) is not a REST write. Read-only REST: GET issue 21589 and its comments, GET comments 5969870827 / 5970999624 / 5973850883 / 5974199078 / 5974322499 / 5974333658 / 5974336258, GET pulls/21632, GET commits/f765e8caaf/check-runs. objectui: one read-onlygit fetchof ab1879721595.",
"nature_of_shared_file_changes": {
"packages/spec/src/ui/component.zod.ts": "no-conflict text merge (5f5b809); no edit this round. It carries #21625 (1cbe165: pin citations and records, 40+ hunks) and #21642 (15fe567: three comment lines at :675, :751, :4639 on main), the only two incoming commits that touched it. #21625 did NOT edit the detail entry: none of its changed lines matches sortField / sort_field / MasterDetail / master-detail / line_items / SORT_FIELD / INLINE_GRID, and none of its hunks falls in the pre-image ranges of the detail entry with its docblock (:5684-:5740) or the record:line_items guidance (:2161-:2209). Proof both stand: HEAD vs main change lines = this PR's own delta (5b5e83f..a1b0552) on this file; HEAD vs a1b0552 change lines = main's own delta 5b5e83f..15fe567 on this file (+319/-124, #21625 plus #21642).",
"packages/spec/src/migrations/registry.ts": "no-conflict text merge (5f5b809); no edit this round. #21625's six rewritten entry texts and their mirror stand (+23/-21, no order line changed). HEAD vs main = this PR's own delta; HEAD vs a1b0552 = main's own delta on this file. This PR's D3 entry, ledger row and step-18 fragment (order 70) are intact. D2 is not in this file: it lives in conversions/registry.ts, which main did not touch in 5b5e83f..15fe567.",
"content/docs/references/ui/component.mdx": "regenerated artifact (f765e8c). The os-regen driver kept this branch's side in merge commit 5f5b809 and dropped #21625's object-grid keyboardNavigation row. Step 2 restored main's side (staged). gen:schema + gen:docs on the merged tree produced blob 734d636534. STAGED diff vs the merge commit: +1/-1, the keyboardNavigation row restored. Delta vs main 15fe567: exactly this PR's one sortField row (change lines identical to 5b5e83f..a1b0552). Delta vs a1b0552: exactly #21625's one row.",
"whole tree": "HEAD vs a1b0552: 68 files, change lines identical to 5b5e83f..15fe567. HEAD vs main: the same 15 files, +997 / -30, change lines identical file by file to the reviewed head's delta."
},
"orders_final": {
"D2": "object-master-detail-form-detail-sort-field-removed (objectMasterDetailFormDetailSortFieldRemoved) order 60 in MAJOR_18_CONVERSIONS (conversions/registry.ts:14803), following main's highest,elementTextVariantHeadingLevelsorder 59; unique (main's own ties are 55 and 57). Unchanged.",
"step18_fragment": "STEP18_RATIONALE fragment object-master-detail-form-detail-sort-field-retired order 70 (migrations/registry.ts:5857), following main's highest,ui-object-metric-aggregate-trend-typedorder 69; unique. Main's own ties are 56, 60, 62, 66 and 67, all present at 5b5e83f already. Unchanged."
},
"body_sentences_now_false": [
""All on the final heada1b0552acdunless noted. The branch mergedorigin/maintwice: ate367002e11(which carries the sibling element:text landing36ad3210d4), then at5b5e83f446(which carries #21622's landing3f1bc816a2)." Final head is now f765e8c. The branch merged origin/main a third time, at 15fe567 (merge commit 5f5b809), carrying #21625 (1cbe165) and #21642 (15fe567).",
""@objectstack/speclocal project ata1b0552acd: 609 files and 18059 tests passed (1 todo), with no skip." Same numbers re-measured at f765e8c.",
""@objectstack/specrepo project ata1b0552acd: the three merge-shape and retirement files (...) passed 41 tests." Same, re-measured at f765e8c.",
""@objectstack/lintata1b0552acd:validate-component-props.test.tspassed 51 tests (including the new advisory pin)." Same, re-measured at f765e8c.",
""Gates:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 114 commands ata1b0552acd, and all 114 exited 0 on real runs (six first attempts werePREREQUISITE NOT MET...)" At f765e8c: the same 114-command set, all 114 exit 0 on the first attempt after a full build; --ran 114 / 114 / 0 / 0.",
""--no-inline-config --format jsonover the 12 changed lintable files at the final heada1b0552acdreported 12 files, 0 errors and 0 warnings" Re-measured at f765e8c, same result.",
""Both merges went throughscripts/pm/os-regen-merge.sh. Each time, the os-regen driver kept this branch's copy ofcomponent.mdxand dropped main's rows (the variant row, then #21622'sobject-metricrows). Each time, the page was regenerated from the merged source in its own commit (the second is5547a5a7df)." Now three merges. The third (5f5b809) dropped #21625's object-grid keyboardNavigation row, and the page was regenerated in f765e8c. The closing clause, "the head's delta againstmainis this PR's own delta plus the one 69 → 70 line", still holds.",
""CI ata1b0552acd:Lint & Repo Gatesis red at one step ... It needs one re-run of that job." Superseded: at f765e8c,Lint & Repo Gatesconcluded success (check run 111318194241). No re-run is owed.",
"Stale, not false (the seat's call): "At the.objectui-shapin89cad75d5570,MasterDetailDetailConfighas nosortFieldmember" (What this does). The.objectui-shapin is now ab1879721595. The claim and every cited line (:83,:372,:874,:1066,:1081,deriveMasterDetail.ts:55,:540) hold there byte-identically. The code's own citations of 89cad75d5570 use the historical spelling, which check:objectui-pin-citations does not check, and that gate is green. No code or changeset sentence became false, so no changeset change was made."
],
"ci_at_head": {
"read_at": "2026-10-04T00:09:39Z (one read, no polling)",
"totals": "34 check runs: 31 success, 2 skipped, 1 in_progress",
"required": "Lint & Repo Gates success (111318194241); TypeScript Type Check success; Build Core success; Dogfood Regression Gate success (1/3, 2/3, 3/3 and the rollup); Temporal Conformance (live PG + MySQL) success; Governed Surface Queue Guard success; Test Core shards 2/6 to 6/6 success, 1/6 in_progress (111318135941), rollup not yet posted",
"skipped": "Console Pin Gate and Packed-tarball smoke (opt-in), by their own filters"
},
"open_questions": [],
"out_of_scope_findings": [],
"gates": {
"node scripts/check-adr-0087-registration.mjs --base origin/main": 0,
"node scripts/check-adr-0087-registration.mjs --self-test": 0,
"node scripts/check-changeset-no-major.mjs --base origin/main": 0,
"node scripts/check-changeset-no-major.mjs --self-test": 0,
"node scripts/check-ci-filter-parity.mjs": 0,
"node scripts/check-closing-keyword-parity.mjs": 0,
"node scripts/check-closing-keyword-parity.mjs --self-test": 0,
"node scripts/check-comment-mask-adoption.mjs": 0,
"node scripts/check-comment-mask-adoption.mjs --self-test": 0,
"node scripts/check-comment-mask-corpus.mjs": 0,
"node scripts/check-dev-prereqs.mjs --self-test": 0,
"node scripts/check-doc-frontmatter.mjs": 0,
"node scripts/check-doc-frontmatter.mjs --self-test": 0,
"node scripts/check-doc-route-spelling.mjs --advisory": 0,
"node scripts/check-doc-route-spelling.mjs --self-test": 0,
"node scripts/check-docs-section-name.mjs": 0,
"node scripts/check-docs-section-name.mjs --self-test": 0,
"node scripts/check-dts-emitted.mjs --self-test": 0,
"node scripts/check-empty-changeset.mjs --base origin/main": 0,
"node scripts/check-empty-changeset.mjs --self-test": 0,
"node scripts/check-issue-citations.mjs": 0,
"node scripts/check-keyed-text-bounds.mjs": 0,
"node scripts/check-keyed-text-bounds.mjs --self-test": 0,
"node scripts/check-platform-object-tenancy-census.mjs": 0,
"node scripts/check-platform-object-tenancy-census.mjs --self-test": 0,
"node scripts/check-plugin-teardown-shape.mjs": 0,
"node scripts/check-plugin-teardown-shape.mjs --self-test": 0,
"node scripts/check-registry-log-declared.mjs": 0,
"node scripts/check-registry-log-declared.mjs --self-test": 0,
"node scripts/check-rest-log-spy-declared.mjs": 0,
"node scripts/check-rest-log-spy-declared.mjs --self-test": 0,
"node scripts/check-section-landing-index.mjs": 0,
"node scripts/check-section-landing-index.mjs --self-test": 0,
"node scripts/check-spec-docblock-symbol-anchors.mjs": 0,
"node scripts/check-spec-docblock-symbol-anchors.mjs --self-test": 0,
"node scripts/check-system-context-census.mjs": 0,
"node scripts/check-system-context-census.mjs --self-test": 0,
"node scripts/check-undeclared-dep-imports.mjs": 0,
"node scripts/check-undeclared-dep-imports.mjs --self-test": 0,
"node scripts/docs-audit/check-affected-docs.mjs": 0,
"node scripts/docs-audit/check-drift-comment.mjs": 0,
"node scripts/pm/release-rehearsal-clone.mjs --self-test": 0,
"node scripts/release-pending-publish.mjs --self-test": 0,
"pnpm --filter @objectstack/lint run check:doc-formula-expressions": 0,
"pnpm --filter @objectstack/lint run check:doc-security-posture": 0,
"pnpm --filter @objectstack/spec run check:api-surface": 0,
"pnpm --filter @objectstack/spec run check:authorable-surface": 0,
"pnpm --filter @objectstack/spec run check:browser-reachable-entries": 0,
"pnpm --filter @objectstack/spec run check:docs": 0,
"pnpm --filter @objectstack/spec run check:dual-source-exports": 0,
"pnpm --filter @objectstack/spec run check:duration-unit-keys": 0,
"pnpm --filter @objectstack/spec run check:empty-state": 0,
"pnpm --filter @objectstack/spec run check:entry-nameability": 0,
"pnpm --filter @objectstack/spec run check:export-origins": 0,
"pnpm --filter @objectstack/spec run check:exported-any": 0,
"pnpm --filter @objectstack/spec run check:generated": 0,
"pnpm --filter @objectstack/spec run check:liveness": 0,
"pnpm --filter @objectstack/spec run check:llms-txt": 0,
"pnpm --filter @objectstack/spec run check:migration-registry": 0,
"pnpm --filter @objectstack/spec run check:objectui-pin-citations": 0,
"pnpm --filter @objectstack/spec run check:skill-examples": 0,
"pnpm --filter @objectstack/spec run check:skill-refs": 0,
"pnpm --filter @objectstack/spec run check:spec-changes": 0,
"pnpm --filter @objectstack/spec run check:strictness-ledger": 0,
"pnpm --filter @objectstack/spec run check:upgrade-guide": 0,
"pnpm --filter @objectstack/spec run check:variant-docs": 0,
"pnpm --filter @objectstack/spec run check:yaml-examples": 0,
"pnpm check:changeset-gate-self-tests": 0,
"pnpm check:corpus-claim-drift": 0,
"pnpm check:cross-package-test-inputs": 0,
"pnpm check:dispatcher-error-vocabulary": 0,
"pnpm check:doc-anchors": 0,
"pnpm check:doc-authoring": 0,
"pnpm check:docs-audit-scope": 0,
"pnpm check:docs-redirects": 0,
"pnpm check:docs-single-h1": 0,
"pnpm check:docs-spec-enumerations": 0,
"pnpm check:docs-transcript-drift": 0,
"pnpm check:driver-memory-census": 0,
"pnpm check:dts-closure": 0,
"pnpm check:dual-build-cjs-loads": 0,
"pnpm check:engine-double-contract": 0,
"pnpm check:future-spec-major": 0,
"pnpm check:gitlink-declared": 0,
"pnpm check:issue-citations": 0,
"pnpm check:lean-entry-closure": 0,
"pnpm check:logger-receiver-detach": 0,
"pnpm check:merge-driver": 0,
"pnpm check:nul-bytes": 0,
"pnpm check:objectql-double-limit": 0,
"pnpm check:objectui-changeset": 0,
"pnpm check:org-identifier": 0,
"pnpm check:page-declaration-shape": 0,
"pnpm check:pm-changeset-deadline-census": 0,
"pnpm check:pm-prior-rulings": 0,
"pnpm check:pm-widening-tells": 0,
"pnpm check:published-files": 0,
"pnpm check:published-readme-links": 0,
"pnpm check:query-options-erasure": 0,
"pnpm check:quick-reference-counts": 0,
"pnpm check:react-page-adapter-contract": 0,
"pnpm check:refd-timer-probe": 0,
"pnpm check:role-word": 0,
"pnpm check:skill-identifier-liveness": 0,
"pnpm check:slot-lookup": 0,
"pnpm check:sourcemap-no-sources-content": 0,
"pnpm check:spec-parsed-alias": 0,
"pnpm check:test-source-alias": 0,
"pnpm check:tier-file-adoption": 0,
"pnpm check:type-check-coverage": 0,
"pnpm check:type-check-debt": 0,
"pnpm check:vendor-version-stamps": 0,
"pnpm check:watch-hint-literal": 0,
"pnpm check:where-matcher": 0
},
"validations": {
"bash scripts/pm/os-regen-merge.sh": 1,
"pnpm --filter @objectstack/spec gen:schema && pnpm --filter @objectstack/spec gen:docs": 0,
"pnpm install --frozen-lockfile (post-merge)": 0,
"pnpm --filter @objectstack/spec build": 0,
"pnpm --filter @objectstack/spec check:generated": 0,
"pnpm --filter @objectstack/spec typecheck": 0,
"pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2": 0,
"pnpm --filter @objectstack/spec exec vitest run --project repo --maxWorkers=2 (3 named files)": 0,
"pnpm --workspace-concurrency=2 --filter '@objectstack/lint^...' build": 0,
"pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/validate-component-props.test.ts": 0,
"node scripts/check-issue-citations.mjs": 0,
"pnpm turbo run build --filter=!@objectstack/docs --concurrency=2": 0,
"pnpm exec eslint --no-inline-config --format json (12 changed lintable files)": 0,
"node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.list": 0
},
"deviations": [
"origin/main had moved past the seat's 0c50b5d: the script merged 15fe567, which adds #21642 (15fe567, "re-anchor the last six dead tracker citations in packages/spec/src"). On this PR's files it touches only component.zod.ts: three comment lines, outside the detail entry. The other incoming commits after 5b5e83f touch none of the 15 files except #21625.",
"os-regen-merge.sh step 3 was refused (the designed collection point). Commandbash scripts/pm/os-regen-merge.shgave exit 1 with refusal "✗ step 3's commit was refused — the merge is staged but NOT committed." because pre-commit held component.mdx stale ("check:docs reads packages/spec/json-schema/, which is missing or older than src — NOT running it"). I followed the dispatch and the hook's instruction: regenerate,git addthe one path, read the STAGED diff, commit (f765e8c). The script was not rerun. Its record $GIT_DIR/os-regen-merge-base stays at phase=handoff in the worktree's own git dir.",
"Push timing: I pushed right after the regeneration commit and before validation, per AGENTS.md "Push a WIP commit before every step that takes minutes". The dispatch orders the push after validation (step 5). The validated head is the pushed head f765e8c, and nothing was committed after it, so the one push carries the validated tree.",
"pnpm install --frozen-lockfilealso ran before the merge, because the pre-commit regen check runs repo scripts. Main's incoming range touches no pnpm-lock.yaml and no package.json, so both installs resolve the same lockfile. It ran again after the merge, as dispatched.",
"Beyond the named list: a fullpnpm turbo run build --filter=!@objectstack/docs --concurrency=2before the gates (AGENTS.md Multi-agent §9), so no gate's first attempt refused, and an ESLint re-measure, to give the body bullet a reading at the new head.",
"Long runs (the spec local project, the lint closure build, the full build and the gate runner) were started detached, each with a recorded PID, and waited on in the foreground withtail --pid. Every exit code was read from the file the run wrote. No process was killed.",
"The dispatch places D2 under migrations/registry.ts; D2 lives in conversions/registry.ts (order 60, unchanged; main did not touch that file in the window). migrations/registry.ts holds the D3 entry, the ledger row and the step-18 fragment.",
"The contract review 5974322499 names main's own rationale-order ties as 56, 62, 66 and 67. 60 is a main-own tie too (agent-structured-output-refused-members-retired and translation-widget-sub-caption-retired), present at 5b5e83f and at 15fe567. It involves no fragment of this PR. Noted for the record only.",
"The commit trailer on f765e8c is the model-free pair (Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ+Co-authored-by: Claudenoreply), per AGENTS.md; pre-push printed "check:commit-card-trailers: 2 commit message(s) on this push carry no card relation and no model identifier". Merge commit 5f5b809 carries git's default message (the script runsgit merge --no-edit)."
],
"files_changed": {
"literal git diff --name-only BASE...HEAD (BASE=a1b0552acd): every path is one main brought in": [
".changeset/20595-formula-provenance-anchors.md",
".changeset/20595-metadata-fs-provenance-anchors.md",
".changeset/20749-spec-strings-stage7-registry-rationales.md",
".changeset/21602-package-scoped-job-identity.md",
".changeset/21620-container-sibling-expansion-name.md",
".changeset/21623-flow-read-node-evaluate-refusal.md",
".changeset/console-ab1879721595.md",
".changeset/objectui-pin-citations-ab1879721595.md",
".changeset/spec-last-six-dead-citation-anchors.md",
".github/workflows/ci.yml",
".github/workflows/release.yml",
".objectui-sha",
"content/docs/references/ui/component.mdx",
"content/docs/references/ui/view.mdx",
"packages/cli/test/package-install-local-jobs-shared-name.integration.test.ts",
"packages/formula/src/cel-engine.ts",
"packages/formula/src/validate.test.ts",
"packages/metadata-fs/src/repository.ts",
"packages/metadata-fs/src/sync.ts",
"packages/metadata-fs/test/close-terminates-watch.test.ts",
"packages/metadata-protocol/src/protocol.ts",
"packages/metadata-protocol/src/view-container-runtime-expansion.test.ts",
"packages/runtime/src/app-artifact-handlers.jobs.test.ts",
"packages/runtime/src/app-artifact-handlers.ts",
"packages/sdui-parser/objectui-lockstep.json",
"packages/services/service-analytics/src/tests/cube-measure-field-type-door.test.ts",
"packages/services/service-analytics/src/tests/cube-measure-relationship-path-type.test.ts",
"packages/services/service-analytics/src/tests/json-stored-door-live-drivers.test.ts",
"packages/services/service-analytics/src/tests/json-stored-door-undeclared-join.test.ts",
"packages/services/service-analytics/src/tests/list-at-scalar-operator-both-faces.test.ts",
"packages/services/service-analytics/src/tests/native-sql-aggregate-policies.test.ts",
"packages/services/service-analytics/src/tests/native-sql-base-column-qualify.test.ts",
"packages/services/service-analytics/src/tests/native-sql-boolean-comparand-door.test.ts",
"packages/services/service-analytics/src/tests/native-sql-measure-number-presentation.test.ts",
"packages/services/service-analytics/src/tests/native-sql-number-comparand-door.test.ts",
"packages/services/service-analytics/src/tests/native-sql-offset-only-window.test.ts",
"packages/services/service-analytics/src/tests/objectql-echo-date-bucket.test.ts",
"packages/services/service-analytics/src/tests/objectql-echo-offset-only-window.test.ts",
"packages/services/service-analytics/src/tests/objectql-face-order-limit.test.ts",
"packages/services/service-analytics/src/tests/order-key-selected.test.ts",
"packages/services/service-automation/src/builtin/crud-nodes.ts",
"packages/services/service-automation/src/builtin/get-record-stored-metadata-filter-refusal.integration.test.ts",
"packages/spec/liveness/action.json",
"packages/spec/liveness/state-counts/action.md",
"packages/spec/src/api/error-code-ledger.zod.ts",
"packages/spec/src/data/api-methods-batch-conformance.test.ts",
"packages/spec/src/data/datasource.zod.ts",
"packages/spec/src/data/filter.zod.ts",
"packages/spec/src/data/value-roundtrip-conformance.ts",
"packages/spec/src/kernel/functional-completeness.ts",
"packages/spec/src/kernel/public-auth-features.ts",
"packages/spec/src/migrations/entries/semantic/18.kernel-health-check-and-hot-reload-durations-unit-in-key.ts",
"packages/spec/src/migrations/entries/semantic/18.kernel-runtime-config-timeout-unit-in-key.ts",
"packages/spec/src/migrations/entries/semantic/18.logging-durations-unit-in-key.ts",
"packages/spec/src/migrations/entries/semantic/18.system-metrics-jsdoc-durations-unit-in-key.ts",
"packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts",
"packages/spec/src/migrations/entries/semantic/18.tenant-schema-cache-ttl-unit-in-key.ts",
"packages/spec/src/migrations/registry.ts",
"packages/spec/src/ui/action-outcome-messages.test.ts",
"packages/spec/src/ui/action.zod.ts",
"packages/spec/src/ui/component.test.ts",
"packages/spec/src/ui/component.zod.ts",
"packages/spec/src/ui/dataset.zod.ts",
"packages/spec/src/ui/view.zod.ts",
"scripts/release-pending-publish.mjs",
"scripts/release-verify-npm.mjs",
"scripts/sdui-manifest.record.json",
"sdui.manifest.json"
],
"net_vs_main (git diff --name-only origin/main...HEAD, merge base 15fe567)": [
".changeset/21589-detail-entry-sort-field-retired.md",
"content/docs/references/ui/component.mdx",
"packages/lint/src/validate-component-props.test.ts",
"packages/spec/src/conversions/registry.ts",
"packages/spec/src/data/inline-grid-columns.ts",
"packages/spec/src/data/inline-grid-sort-fields.ts",
"packages/spec/src/inline-grid-column-carriers.test.ts",
"packages/spec/src/migrations/entries/retired-keys/18.ui__ObjectMasterDetailFormProps__details.sortField.ts",
"packages/spec/src/migrations/entries/semantic/18.object-master-detail-form-detail-sort-field-retired.ts",
"packages/spec/src/migrations/entries/semantic/18.ui-object-master-detail-form-details-closed.ts",
"packages/spec/src/migrations/entries/semantic/18.ui-record-line-items-props-closed.ts",
"packages/spec/src/migrations/registry.ts",
"packages/spec/src/ui/component.zod.ts",
"packages/spec/src/ui/master-detail-detail-sort-field-retirement.test.ts",
"packages/spec/vitest.repo-tests.json"
],
"round_authored (git diff --name-only 5f5b809 HEAD)": [
"content/docs/references/ui/component.mdx"
]
},
"line_budget": {
"literal git diff --shortstat BASE...HEAD (BASE=a1b0552acd)": "68 files changed, 3143 insertions(+), 464 deletions(-)",
"net_vs_main (git diff --shortstat origin/main...HEAD)": "15 files changed, 997 insertions(+), 30 deletions(-)",
"round_authored (git diff --shortstat 5f5b809 HEAD)": "1 file changed, 1 insertion(+), 1 deletion(-)"
}
}objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsContract review
Served-tier:
CONTRACT_REVIEW_TIER
Head-sha:f765e8caaf8854f8677350dc153dbbbb3a856945
Local-runs: nonePR #21632 on card #21589, third record:
5973339395reviewed69f42d393d,5974322499revieweda1b0552acd;mainmoved again and the branch merged it, so this is a review of the head that merge round produced. Inputs: the card body and every comment (triage5969870827, claim5970999624, dev reports5973231948,5973850883and5974891957, records5973339395and5974322499, acceptances5973355907and5974333658, seat note5974199078), the PR body, its 15-file list, the net diff againstmainat the merge base15fe567c9c(read as the three-dot diff from the API and as the head tree, fetched into a review ref), the head's check-runs, and the pinned renderer at objectuiab1879721595(the.objectui-shathis head carries), read bygit showagainst that commit. Each dev report and both earlier records were read as claims and re-measured. Nothing of the PR was built, run or re-run.How this head relates to the reviewed one. The net diff against
mainis the same 15 files, +997 / −30. Rendered as patch text with the index and hunk-header lines stripped, this head's three-dot diff against15fe567c9cand the old head's against5b5e83f446are byte-identical: both 1262 lines, zero differing lines. So every byte5974322499judged is the byte this head carries, and where an item below adopts that record's reasoning it says so. What moved ismainunderneath: ten commits between the two merge bases, of which two touch this PR's files. #21625 (1cbe165bfc, the console pin toab1879721595) changed 443 lines ofcomponent.zod.ts, 44 ofmigrations/registry.tsand one row ofcomponent.mdx; #21642 (15fe567c9c) changed three comment lines ofcomponent.zod.ts. Measured on the diff5b5e83f446..15fe567c9c: none of those changed lines namessortField,sort_field,MasterDetail,master-detail,line_items,INLINE_GRIDor the word "twelve"; none inmigrations/registry.tsis anorder:line;conversions/registry.ts,package.json,authorable-surface/andspec-changes.jsondid not move. The round's own authored commit (5f5b8092ea..f765e8caaf) is the one regenerated page, +1 / −1, which restored #21625'sobject-gridkeyboardNavigationrow that the os-regen driver had dropped, so the page's delta againstmainis again exactly this PR's one row. Every judgment below is nevertheless made against this head's tree, and each placemain's movement could bear on a judgment (orders, the pin, the generated page, the version, the ledger) is re-measured here.Check-runs on this head, read at 2026-10-04T00:23Z: 35 runs, all
completed: 33success, 2skipped, none in progress and none red. The seven required contexts are green:Lint & Repo Gates(the context that was red on the previous head at its issue-citations step; green here, no re-run owed),TypeScript Type Check(with itssource gates,consumer gates,debt ledgerandworkspacelegs),Test Core(6 of 6 shards and the rollup, the last to complete at 2026-10-04T00:12Z),Dogfood Regression Gate(3 of 3 and the rollup),Build Core,Temporal Conformance (live PG + MySQL)andGoverned Surface Queue Guard. Also green:Spec property liveness,Check Changeset,Build Docs,Dogfood Verify CLI,Check Documentation Links,Check PR Size,Flag docs affected by code changesand the four claim guards. Skipped by their own filters:Console Pin Gate(its filter names.objectui-sha, whichmainmoved and this PR's 15 paths do not include) andPacked-tarball smoke (opt-in).① Derived judgments
- Accept-set narrowing,
ComponentPropsMap['object-master-detail-form'].details[].sortField:z.string().optional()onmainbecomesretiredKey(MASTER_DETAIL_DETAIL_SORT_FIELD_RETIRED), andretiredKey(shared/retired-key.ts:179) isz.never({ error })made.optional()with the[REMOVED]describe, so the input type isneverand a parse answersinvalid_typeatdetails.N.sortFieldwith the prescription. RIGHT. Re-read at the pin this head carries, objectuiab1879721595:MasterDetailDetailConfiginpackages/plugin-form/src/MasterDetailForm.tsxhas exactly eleven members (childObject,relationshipField,columns,formFields,inlineMode,amountField,totalField,title,minRows,maxRows,addLabel) and the comment at line 83 opens "NosortFieldmember"; everysortFieldon that path is the derived one (the resolved entry's member at 372, handed to the grid assort_fieldat 874, filled fromderived.sortFieldat 1066 and 1081), andderiveMasterDetail.tspicks it at line 540 as the child object's first field named inSORT_FIELD_NAMES(line 55). Both files are byte-identical between89cad75d5570andab1879721595(git diff --statbetween the two commits on those paths is empty), so the pin bump changed none of the facts the PR's texts cite. An authored value is read by nothing, so refusing it changes no rendered behaviour and closes the declared-but-unenforced door the card names. The entry keeps eleven live keys, equal to the member count at the pin; the docblock's twelve to eleven, the carriers test'sFULL_DETAIL_ENTRY12 to 11, and the new pin's walked-shape count of 12 (eleven plus the tombstone) agree. No old spelling maps onto the tombstone, so no alias-table entry is owed. - The prescription. RIGHT on the house conventions: the fully qualified key in backticks first (
object-master-detail-formpropertydetails[].sortField); "was removed in @objectstack/spec 17 (ADR-0087 D2)"; the why-inert clause; "Delete the key." with the live mechanism named, including the fully-configured-entry exception the renderer really has (it loads no child schema for an entry namingrelationshipField, at least one column and atypeon every column, so it stamps no line position); the pinned "Runos migrate meta --from 17…" sentence. "Removed in 17",--from 17, D2 and ledger row under 18 is thepage:headerbreadcrumbcombination in the same file, adopted from5974322499item 2 because those bytes are unchanged and the precedent ismain's. The six names it prints come fromINLINE_GRID_SORT_FIELD_LISTand are the same six, in the same order, asSORT_FIELD_NAMESatab1879721595:position,sort_order,sequence,line_no,line_number,sort. - Public surface. New module
packages/spec/src/data/inline-grid-sort-fields.tsexportingINLINE_GRID_SORT_FIELDSandINLINE_GRID_SORT_FIELD_LIST. RIGHT, and unpublished at this head:data/index.tsre-exports./inline-grid-columnsonly (line 320),inline-grid-columns.tsimports the set (line 92) and re-exports nothing of it, and the module's only importers in the head tree areinline-grid-columns.ts,ui/component.zod.ts(line 87) and the new pin. The module imports nothing, so the two template-literal prescriptions incomponent.zod.tsread an initialised constant. No api-surface movement is owed;TypeScript Type Checkis green on this head. - Guidance text,
RecordLineItemsProps' unknown-key answer forsortField(component.zod.ts:2221): the key stays refused asunrecognized_keys(pinned, withaddLabelas the control); only the prose moved, from "belongs to a detail entry" to "no block takes an authoredsortField", which item 1 made true. RIGHT. - ADR-0087 D2
object-master-detail-form-detail-sort-field-removed:toMajor: 18,retiredFromLoadPath: true,retiredAfter: '17.6.0'(equal topackages/spec/package.jsonat this head,"version": "17.6.0", unmoved bymain), order 60. Re-measured at this head:MAJOR_18_CONVERSIONShas no entry above 60, 60 appears once, and its only ties aremain's own at 55 and 57;maindid not touchconversions/registry.tsbetween the merge bases.mapPageComponentsplus the sharedstripKeys, scoped bytype === 'object-master-detail-form'and byproperties.details[]position, copy-on-write; the fixture carries region, nested-card and slotted carriers (expectedNotices: 3) beside an open-namespace control authoring the same shape and a non-object entry. RIGHT. - Ledger row
RETIRED_KEYS_BY_MAJOR[18]gainsui/ObjectMasterDetailFormProps:details.sortField, generated fromentries/retired-keys/18.ui__ObjectMasterDetailFormProps__details.sortField.ts, named asentries/README.mdprescribes (the major, then the id with/and:spelled__; the dot insidedetails.sortFieldstays). RIGHT:authorable-surface/ui.jsonat this head carries only top-level rows for this def (lines 972 to 985,…:detailsat 973, nodetails.row), so no[RETIRED]baseline line is owed, the nested row resolves against the emitted schema, andcheck:authorable-surfaceis inside the greenTypeScript Type Check. "Migration registry matches its entry files" is a step of the greenLint & Repo Gates. - D3
object-master-detail-form-detail-sort-field-retired(the entry file and the generated block carry the same text) plus aSTEP18_RATIONALEfragment at order 70. Re-measured at this head: 70 appears once and nothing is above it; 69 isui-object-metric-aggregate-trend-typed;main's own ties are 56, 60, 62 (three entries), 66 and 67, none of them this PR's fragment. The newest report's correction of5974322499(which listed the ties as 56, 62, 66, 67 and missed 60) is right and changes nothing: the tie at 60 is between twomainfragments.surfacecarries no backticks;replacementspells the six names literally because the generated registry keeps no imports, and the pin holds that literal equal to the one declaration;reasonandacceptanceCriteriaare non-empty. RIGHT. - Generated reference page: the
content/docs/references/ui/component.mdxrow becomesneverwith the[REMOVED] …text, byte-equal toretiredKey's describe. RIGHT: the page's delta againstmainat this head is exactly that one row (+1 / −1), which also shows the regeneration on the merged tree (f765e8caaf) carriesmain'skeyboardNavigationrow;check:docsis inside the greenTypeScript Type Check. - Consumer behaviour:
validateComponentPropsreports a detail-entrysortFieldas acomponent-props-invalidwarning atproperties.details.N.sortFieldcarrying the prescription, and the page parse still accepts the block becausePageComponentSchema.propertiesis an open bag (both pinned). RIGHT, the posture of the other tombstones inui/component.zod.ts. - Pins. RIGHT, bytes unchanged from
5974322499item 10, adopted: refusal by issue code and path with the prescription; the six names drawn from the declaration; the props-lint row is the same schema object; the tombstone-versus-strict-arm control; the walked shape; the tsc channel (@ts-expect-errorcompiled bycheck:test-typecheck, inside the greendebt ledgerleg); the page parse never refusing; therecord:line_itemsanswer; the D2 strip on a stored row throughapplyConversionsToStoredItem, on a built artifact in every position, the by-reference control, type and position scoping, idempotence, retired from the load path; the ledger row and the D3 entry; the tree-scoped absence walk overpackages,examples,skills,contentandscripts(the@objectstack/specradiusscripts/cross-package-test-inputs.mjsdeclares) with anti-vacuity floors and structural exclusions only. Listed invitest.repo-tests.json; "Cross-package test inputs" is a step of the greenLint & Repo Gates, andTest Coreis green 6 of 6 on this head, which answers the test the earlier reports could not run under the foreground cap. - Writer census, re-measured at this head:
git grep -n -w sortFieldoverexamples,apps,packages,skillsandcontent/docs, excludingpackages/spec/**and CHANGELOGs, finds the generated reference row and the new lint pin and nothing else; the controladdLabelfinds 6. No row underpackages/spec/liveness/and no*.form.tsnames the key or the def;Spec property livenessis green. - Pin citations after the bump. The PR's docblocks, the new module, the ledger comment, the D2 docblock and the changeset cite
89cad75d5570, which is no longer the pin. RIGHT as written, and not a text made false: every one of them uses the historical spelling (.objectui-shapinsha, with or without the parenthesis), whichpackages/spec/scripts/check-objectui-pin-citations.tsclassifies as a dated record of a past measurement and deliberately does not check against the pin file; the asserting spelling (.objectui-sha=sha) appears nowhere in the diff. The measurements those records make hold byte-identically atab1879721595(item 1). The gate is acheck:*gate, carried by the greenLint & Repo Gates. - Sibling checkout. RIGHT that no objectui edit ships here. No export is removed (
ObjectMasterDetailFormPropsSchemastays, and atab1879721595objectui'sapps/console/src/__tests__/masterDetailFormTypeManifest.test.tsreads it;public-contract.test.tsandregistry-inputs-spec-parity.test.tsreadComponentPropsMap, whose key set and whoseobject-master-detail-formtop-level keys are unchanged). At that pin everysortFieldoutside the derive path is objectui's own:GridField.tsx'scfg.sort_field, the column-sort state inRecordPickerDialog.tsxandRelatedList.tsx,MetadataService.specKeyObjectPayload.test.tsasserting thatObjectSchemadoes not accept the name, and themasterDetailDetailsMembers-8071probes that author the key onMasterDetailDetailConfigto assert nothing reads it (one under@ts-expect-error). Nothing authors a detail-entrysortFieldthrough a spec type, so theConsole Pin Gateskip is by filter, not a gap. objectui#11396's mirror follows a published release, as triage ruled.
Nothing in the diff widens an accept set. Nothing wrong was found.
② Semver level
.changeset/21589-detail-entry-sort-field-retired.md:@objectstack/specminor, a BREAKING banner, the FROM → TO table with the one-line fix (delete the key;os migrate meta --from 17lists the edits), and the ADR-0087 HTML-comment marker in theregisteredform naming both ids,object-master-detail-form-detail-sort-field-removedandobject-master-detail-form-detail-sort-field-retired, the formscripts/check-adr-0087-registration.mjsdocuments.minoris right: an accept-set narrowing on a published authoring surface, shipped under the launch-window convention thatscripts/check-changeset-no-major.mjsenforces (majorrefused);Check Changesetis green on this head. Nothing from any other released package publishes: the other touched packages arepackages/lint(a test file only) andcontent/docs, so no second entry is owed andskip-changesetis rightly absent. The changeset's own citation of the pin (89cad75d5570) is a dated record in a file the pin-citation gate places outside its population by design (changesets are historical records), and the reading it records holds at the new pin.Clause-②: no (narrowing). It stands as line 2 of the PR body, in the changeset and in the claim, and it matches the diff: one accept set narrows, none widens. The pair is the legal breaking one, so the changeset owes a migration and an ADR-0087 disposition, and carries both.
③ Boundary flags
The newest report (
5974891957) carries noopen_questionsand noout_of_scope_findings. Itsdeviations, each answered:origin/mainhad moved past the seat's0c50b5dfe7to15fe567c9c(docs(spec): re-anchor the last six dead tracker citations in packages/spec/src to the commits that decided them #21642, three comment lines ofcomponent.zod.ts). Accepted: measured above, those lines re-anchor rule citations and none falls in the detail entry, therecord:line_itemsguidance or the imports;15fe567c9cis the merge base this review reads against, and the three-dot diff is this PR's 15 files and nothing ofmain's.os-regen-merge.shstep 3 refused bypre-commit(the page stale), the one path regenerated and committed asf765e8caaf. Accepted: the round's authored delta is that page alone, +1 / −1, and its delta againstmainis this PR's one row (item 8).- Push before validation. Not a contract matter: the pushed head is the validated head, nothing was committed after it, and the head's check-runs are the gate verdicts, all green.
pnpm install --frozen-lockfilebefore and after the merge; a full turbo build and an ESLint re-measure beyond the named list; long runs detached with recorded PIDs. Not contract matters; nothing to judge.- The dispatch placed D2 under
migrations/registry.ts; D2 lives inconversions/registry.ts. Accepted:MAJOR_18_CONVERSIONSis declared inconversions/registry.ts, and the claim5970999624names both registries. - The tie at rationale order 60 that
5974322499did not list. Accepted and recorded in item 7: amain-own tie, not this PR's. - Commit trailers in the model-free pair; the merge commit carries git's default message. Not a contract matter.
The report's
body_sentences_now_falselist is a PR-body matter for the seat, not a dev flag on the diff: the Evidence heads and readings ata1b0552acd, the two-merges sentence and the CI note are stale at this head, and the dev re-measured each; no changeset or code sentence is among them. Its one judgment call, the body's "At the.objectui-shapin89cad75d5570" sentence, is answered by item 12: a reading in the historical spelling that holds byte-identically atab1879721595, so it is stale, not false, and the seat may add the re-read when it rewrites the body.Carried over from the first report (
5973231948) and the PR body:- The open question the PR body re-asks ("Reviewer, please confirm that this correction belongs in this PR"): the two step-18 D3 texts,
ui-object-master-detail-form-details-closedandui-record-line-items-props-closed. A, keep them here, re-confirmed at this head: the two hunks are byte-identical to the reviewed ones;packages/spec/spec-changes.jsonat this head carries 268 entries, alltoMajor: 17, so step 18 is unreleased and no generated 18 artifact exists to be stale;check:spec-changesandcheck:upgrade-guideare inside the greenTypeScript Type Check; outsidesrc/migrationsthe two ids live only in release-owned CHANGELOGs, which the PR leaves alone. Left uncorrected, the future 17 to 18 guide would tell an author to write a key the same guide retires. Both earlier records and both acceptances answered the same. - The first report's other deviations (file surface wider than the claim; orders now 60 and 70;
files_changedfrom the merge base; the NOT MEASUREDscripts/build-schemas-check-mode.test.tsunder the foreground cap; the PR-body ESLint bullet) were judged in5973339395and still hold: the bytes they concern are unchanged at this head, the orders are re-measured in items 5 and 7, and the not-measured test is CI-owned withTest Coregreen 6 of 6 here. - The seat note
5974199078is a seat record, not a dev flag; its CI reading was of the previous head, and this head's reading above supersedes it. - Governance: none of the 15 paths is a governed surface (
docs/adr/**,docs/NORTH-STAR.md,.claude/**,skills/**,AGENTS.md,CLAUDE.md); head repo equals base repo; 1,027 changed lines. The contract-tier review this record is was owed for thepackages/spec/src/**narrowing and for nothing else. Every required context is green on this head, so nothing CI-side is left for the seat to re-run before it acts.
Implemented-by:
claude/issue-21589-detail-sortfield-retire
Reviewed-by:session_01T9u38rswFp5Rw8DswRUReJVERDICT: PASS
- Accept-set narrowing,
objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsACCEPT, renewed at merge round 3's head — PR #21632 at
f765e8caafdomain:specseat 1 ·os-project-manager· sessionsession_01T9u38rswFp5Rw8DswRUReJ· 2026-10-04T00:24Z · renews ACCEPT5974333658for the head round 3 produced, on the maintainer's 「21632 你负责处理」. Contract review PASS5974961520(at-tier, isolated, on this head). Round-3 report:5974891957.- Why round 3: PR chore(objectui): bump the console pin to ab1879721595 (carries objectui f624f278, b0bf413c and ab187972) #21625 (
1cbe165bfc) landed onmainand touched three of this PR's files (component.zod.ts,migrations/registry.ts,component.mdx). The merge was owed for its own sake. Its push also re-ranLint & Repo Gates, which had been red ata1b0552acdonly from a GitHub API 503. That re-run gave the job a reading on this head; no empty commit was pushed. - What moved: the merge
5f5b8092eaoforigin/main15fe567c9c(carrying chore(objectui): bump the console pin to ab1879721595 (carries objectui f624f278, b0bf413c and ab187972) #21625 and docs(spec): re-anchor the last six dead tracker citations in packages/spec/src to the commits that decided them #21642), plusf765e8caaf, which regeneratescomponent.mdxand restores chore(objectui): bump the console pin to ab1879721595 (carries objectui f624f278, b0bf413c and ab187972) #21625'skeyboardNavigationrow (+1 / −1). The record compared the two net diffs againstmainand found them byte-identical once index lines are stripped (15 files, +997 / −30). D2 order 60 and fragment order 70 are unique on the merged tree. - Why a fresh record, not a
Regen-provenance:carry: the move carriesmain's changes into two of this PR's own non-generated files. The carry rule refuses that shape, so this head got a full at-tier record. - Pin: the renderer facts hold byte-identically at the new pin
ab1879721595.MasterDetailForm.tsxandderiveMasterDetail.tsare unchanged since89cad75d5570, and the record re-read the cited lines. The PR body now says so. - CI at
f765e8caaf: complete and green, 33 success and 4 skipped, every skip in the roster (check-expected-skipsexit 0). All seven required contexts are green,Lint & Repo Gatesincluded. - Landing readings:
mergeable_stateclean; not governed, 1027 changed lines. No commit onmainafter the merge base15fe567c9ctouches the PR's 15 files. Generated drift is therefore zero. - The open question stays answered A (keep the two step-18 D3 text corrections). Every earlier deviation stays accepted. The body's stale sentences were rewritten in an earlier act, with the head unchanged.
The seat lands it in this act.
- Why round 3: PR chore(objectui): bump the console pin to ab1879721595 (carries objectui f624f278, b0bf413c and ab187972) #21625 (
objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsLanded: PR #21632 →
6ec54f00badomain:specseat 1 ·os-project-manager· sessionsession_01T9u38rswFp5Rw8DswRUReJ· 2026-10-04T00:58Z · holder of claim5970999624.- Landed: PR feat(spec)!: retire the object-master-detail-form detail entry sortField — the console derives the line-position field from the child object (#21589) #21632 merged through the merge queue at 2026-10-04T00:58Z as
6ec54f00ba. It has one parent (f40bb3217f) and is an ancestor oforigin/main. - Content check: all 15 files at
6ec54f00baare blob-equal to the reviewed headf765e8caaf(contract review PASS5974961520, renewed ACCEPT5974972135). No commit between the PR's merge base15fe567c9cand the merge parent touched any of them. - Card: closed
completedby the PR'sFixesline. That was the only closing keyword in the body. This act removespm:dispatchedand the assignee. - What now holds:
object-master-detail-form'sdetails[].sortFieldis aretiredKey()tombstone. Its prescription names the six derived sort-field names from one declaration (data/inline-grid-sort-fields.ts, whichinline-grid-columns.tsimports).- The D2 conversion
object-master-detail-form-detail-sort-field-removed(step 18, order 60) strips it from stored pages. The D3 entryobject-master-detail-form-detail-sort-field-retiredcarries step-18 rationale order 70. - The two landed step-18 D3 texts that named
sortFieldas a detail-entry key are corrected.
- Path to the landing, for the record: three merge rounds (spec(ui): release 2 of the objectui#7450 convergence —
element:text.variant'sheading/subheadingbecome named refusals through the value-level retirement mechanism #21015's, then feat(spec)!: object-metric aggregate and trend take the shape the tile reads; drillDown, compareTo held for a ruling (#21464, stage 4) #21622's and chore(objectui): bump the console pin to ab1879721595 (carries objectui f624f278, b0bf413c and ab187972) #21625's landings each touched this PR's files) and three at-tier reviews, all PASS (5973339395,5974322499,5974961520). Ata1b0552acd,Lint & Repo Gateswas red only from a GitHub API 503. Round 3's merge was owed for chore(objectui): bump the console pin to ab1879721595 (carries objectui f624f278, b0bf413c and ab187972) #21625, and its push re-ran the job green. ⛔ No empty commit and no out-of-channel re-run.
- Landed: PR feat(spec)!: retire the object-master-detail-form detail entry sortField — the console derives the line-position field from the child object (#21589) #21632 merged through the merge queue at 2026-10-04T00:58Z as
- added a commit that references this issue
on Oct 5, 2026
Filing gate: ① a declared ≠ enforced defect with a named landing site.
reach:the published authoring door.@objectstack/spec17.6.0 accepts the key (package/src/ui/component.zod.ts:5012in the npm tarball), and the console this repo pins reads nothing for it. Filed by thedomain:specseat 1 (session_01T9u38rswFp5Rw8DswRUReJ, seat post #6017). This is the spec half that seat post records as owed "at the next objectstack pin bump past0a3e5409f". ⛔ Filed bare: grading and routing are triage's. ⛔ Not a claim.Reader who acts: triage grades and routes it, then the
domain:specseat claims and dispatches it.The defect
ComponentPropsMap['object-master-detail-form'].details[]declaressortField: z.string().optional(). Onorigin/maina7ab047cf6that ispackages/spec/src/ui/component.zod.ts:5460, with the describe "Child field holding the line sort position, stamped on drag-reorder. …". The entry docblock (about:5433) lists it as one of "the renderer's own" keys.0a3e5409f(objectui#11376, objectui#11070 round 9). At the pin89cad75d5570,packages/plugin-form/src/MasterDetailForm.tsx:83reads: "⛔ NosortFieldmember. The child field the grid stamps with each line's position is DERIVED from the child object (deriveDetail…) and carried on the resolved entry, never authored". The onlysortFieldon that path is the derived one (deriveMasterDetail.ts:540, handed to the grid assort_fieldatMasterDetailForm.tsx:874).compare 0a3e5409f...89cad75d5570on objectui answersahead,ahead_by: 46. The pin crossed0a3e5409fin PR chore(objectui): bump the console pin to 89cad75d5570 (carries objectui 0858267e, 8001068b, 3ae91930 and d0fba91aa) #21380 (8963dbfa, 2026-10-02T10:06Z), which did not retire the key.sortField: 'line_no'gets a cleanos validateand a drag-reorder that stamps whatever the child's derived field is, or nothing.git grep -n 'sortField' origin/main -- examples apps packages ':!packages/spec/**'finds no authoredsortField:on a detail entry. Every hit is an unrelated identifier (sortFieldsindriver-memory,sortFieldRefsinpackages/lint). Control: the same grep foramountFieldhitsexamples/app-showcase/src/ui/pages/project-detail.page.ts.Landing site (the direction recorded when the key was declared)
The record is
5937457620on #21220's landing, mirrored on objectstack-ai/objectui#11396 ③: "retiresortFieldfrom the detail entry, tombstone plus ADR-0087".retiredKey()tombstone on the detail entry. Its prescription names the derived sort: the child object'sposition/sort_order/sequence/line_no/line_number/sortfield.sortField(the line-position field …)";record:line_items' guidance "sortFieldbelongs to anobject-master-detail-formdetail entry" (about:2196–:2197);detailsentry as a strict shape — the parity row and mirror follow at the next spec bump; the spec'ssortFieldfollows #11376 at the next pin bump objectui#11396's mirror follows when a published@objectstack/speccarries this.Dedupe
search_issues, repo-scoped, open and closed together, "retire sortField object-master-detail-form detail entry": 2 hits. spec(ui): an object-master-detail-form detail entry's sortField describe says it is derived when omitted; on the renderer's fast path nothing derives it #21315 (closed: the describe's derivation claim, not the retirement) and spec(forms): retirepublicPicker— anonymous public forms stop taking lookup / master_detail / user fields, and the anonymous picker route goes (ruling E on #21079) #21180 (unrelated).sortField: 0 cards; the only hit is the seat post [PM seat] domain:spec — ⏳ vacant #6017.Dedupe words: sortField master-detail detail entry retire · MasterDetailDetailConfig sortField retired 0a3e5409f · object-master-detail-form details sortField tombstone