Skip to content

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

@objectstack-fleet

Filing gate: ① a declared ≠ enforced defect with a named landing site. reach: the published authoring door. @objectstack/spec 17.6.0 accepts the key (package/src/ui/component.zod.ts:5012 in the npm tarball), and the console this repo pins reads nothing for it. Filed by the domain:spec seat 1 (session_01T9u38rswFp5Rw8DswRUReJ, seat post #6017). This is the spec half that seat post records as owed "at the next objectstack pin bump past 0a3e5409f". ⛔ Filed bare: grading and routing are triage's. ⛔ Not a claim.

Reader who acts: triage grades and routes it, then the domain:spec seat claims and dispatches it.

The defect

  • ComponentPropsMap['object-master-detail-form'].details[] declares sortField: z.string().optional(). On origin/main a7ab047cf6 that is packages/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.
  • objectui retired the authored override at 0a3e5409f (objectui#11376, objectui#11070 round 9). At the pin 89cad75d5570, packages/plugin-form/src/MasterDetailForm.tsx:83 reads: "⛔ No sortField member. 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 only sortField on that path is the derived one (deriveMasterDetail.ts:540, handed to the grid as sort_field at MasterDetailForm.tsx:874).
  • compare 0a3e5409f...89cad75d5570 on objectui answers ahead, ahead_by: 46. The pin crossed 0a3e5409f in 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.
  • So an author (an AI above all) who writes sortField: 'line_no' gets a clean os validate and a drag-reorder that stamps whatever the child's derived field is, or nothing.
  • Writers: git grep -n 'sortField' origin/main -- examples apps packages ':!packages/spec/**' finds no authored sortField: on a detail entry. Every hit is an unrelated identifier (sortFields in driver-memory, sortFieldRefs in packages/lint). Control: the same grep for amountField hits examples/app-showcase/src/ui/pages/project-detail.page.ts.

Landing site (the direction recorded when the key was declared)

The record is 5937457620 on #21220's landing, mirrored on objectstack-ai/objectui#11396 ③: "retire sortField from the detail entry, tombstone plus ADR-0087".

Dedupe

Dedupe words: sortField master-detail detail entry retire · MasterDetailDetailConfig sortField retired 0a3e5409f · object-master-detail-form details sortField tombstone

Activity

  1. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · priority:p2 · domain:spec · area:records · pm:queue. Retire sortField from the detail entry, as recorded at #21220's landing

    Triage 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:5460 on origin/main 901e7cf13a still declares sortField on object-master-detail-form details[].
    • At the pin 89cad75d5570, objectui's MasterDetailForm.tsx:83 says "⛔ No sortField member". The only sortField on 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 validate hides 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 ComponentPropsMap cards (#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, so domain: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' sortField guidance (about :2196–:2197), and any generated reference page or pin that names the key.
    • objectui#11396's mirror follows when a published @objectstack/spec carries it. ⛔ No objectui edit in this card's PR.

    Serial note for the claim. ui/component.zod.ts is #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

  2. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · 2026-10-03T16:18Z
    Session: session_01T9u38rswFp5Rw8DswRUReJ
    Account: os-project-manager (the seat's linked user as GET /user answers 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 (at origin/main 9a4182a752; stop on breach and explain in the report):

  3. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    os-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": "Retired sortField from the object-master-detail-form detail 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 conversion object-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] row ui/ObjectMasterDetailFormProps:details.sortField, one D3 entry object-master-detail-form-detail-sort-field-retired plus 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 listed sortField?; 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 typecheck exit 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/lint vitest run → "119 passed / 5621 tests" (includes the new detail-entry warning pin), typecheck OK; @objectstack/example-showcase typecheck OK + 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 json over 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) planted sortField: '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 authors sortField: '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 named sortField as 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 is git diff --name-only origin/main...HEAD (merge base e367002), not git 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 from git 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 published data barrel — 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, read git 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: Claude with 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 — command timeout 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...HEAD at 69f42d3"
    }

  4. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Contract review

    Served-tier: CONTRACT_REVIEW_TIER
    Head-sha: 69f42d393d2affe661e571fda4563c5d77a141da
    Local-runs: none

    PR #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 main at this head, the head's check-runs, and the pinned renderer at objectui 89cad75d5570 read by git show against 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:generated reconcile, 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 (its console filter names .objectui-sha, scripts/build-console.sh and 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

    1. Accept-set narrowing: ComponentPropsMap['object-master-detail-form'].details[].sortField goes from z.string().optional() to a retiredKey() tombstone on the strict detail entry. RIGHT. At the pin, MasterDetailDetailConfig in packages/plugin-form/src/MasterDetailForm.tsx has no sortField member (the comment opening "No sortField member", line 83 at the pin); the only sortField on that path is the derived one on the resolved entry (lines 372, 874, 1066, 1081), picked by deriveDetail in deriveMasterDetail.ts (line 540) from SORT_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 of MasterDetailDetailConfig at the pin (childObject, relationshipField, columns, formFields, inlineMode, amountField, totalField, title, minRows, maxRows, addLabel); the docblock's twelve to eleven and the carriers test's FULL_DETAIL_ENTRY 12 to 11 both match that count.
    2. 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 17 sentence. The "removed in 17", "--from 17", registered-under-18 combination is the page:header breadcrumb precedent 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's SORT_FIELD_NAMES at the pin.
    3. Public surface: new module packages/spec/src/data/inline-grid-sort-fields.ts exporting INLINE_GRID_SORT_FIELDS and INLINE_GRID_SORT_FIELD_LIST. RIGHT, and unpublished: data/index.ts re-exports ./inline-grid-columns only, where the set was a module-private const before; the new module is reached by relative import from inline-grid-columns.ts, ui/component.zod.ts and the new pin, and from nothing else at the head. No api-surface movement is owed, and check:api-surface is green on this head.
    4. Guidance text: RecordLineItemsProps' unknown-key answer for sortField no longer sends the author to the detail entry. RIGHT: the key stays refused as unrecognized_keys (pinned, with the addLabel sibling as control); only the prose moved, and the old prose became false with item 1.
    5. ADR-0087 D2 object-master-detail-form-detail-sort-field-removed: toMajor: 18, retiredFromLoadPath: true, retiredAfter: '17.6.0' (equal to packages/spec/package.json at the head), order 60 (unique in MAJOR_18_CONVERSIONS at the head; the 55 and 57 ties are main's own and the list header permits ties), inserted in identifier order between objectKanbanQuickAddRemoved and objectTenancyOrganizationFieldRemoved. The strip is scoped by component type and by properties.details[] position, uses the shared stripKeys (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.
    6. Ledger row: RETIRED_KEYS_BY_MAJOR[18] gains the nested key ui/ObjectMasterDetailFormProps:details.sortField. RIGHT: authorable-surface/ui.json carries only top-level rows for this def (ui/ObjectMasterDetailFormProps:details, no details. rows), so no [RETIRED] baseline line is owed; the nested half of checks (b2) and (b3) in scripts/build-schemas.ts resolves such a row against the emitted schema, and check:authorable-surface is green on this head, so the row resolved as tombstoned, neither live nor unresolvable. json-schema.manifest/ui.json records def names only and is rightly unmoved.
    7. D3 object-master-detail-form-detail-sort-field-retired plus a STEP18_RATIONALE fragment at order 69 (unique at the head). RIGHT: surface carries no backticks; replacement spells the six names literally because the generated registry keeps no imports, and the new pin holds that literal equal to the one declaration.
    8. Generated reference page: the content/docs/references/ui/component.mdx row becomes never with the [REMOVED] text, byte-equal to the describe. RIGHT (check:docs green on this head).
    9. Consumer behaviour: validateComponentProps reports a detail-entry sortField as a component-props-invalid warning at properties.details.N.sortField, and the page parse still accepts the block because PageComponentSchema.properties is an open bag. RIGHT, and the same posture as the other thirty tombstones already in ui/component.zod.ts.
    10. Pins. RIGHT: refusal by issue code and path with the prescription; the tombstone-versus-strict-arm control; the tsc channel (the new @ts-expect-error is compiled by check:test-typecheck through tsconfig.test.json, whose include is src/**/*; that gate is green on this head); stored-row replay through applyConversionsToStoredItem; 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, examples including examples/*/src/**/*.ts, skills, content, scripts) are exactly the @objectstack/spec radius scripts/cross-package-test-inputs.mjs already declares and turbo.json's @objectstack/spec#test:repo mirrors. The test is listed in vitest.repo-tests.json, and both the matcher and the walk carry anti-vacuity floors.
    11. Writer census, re-measured at the head: git grep -n -w sortField over examples, apps, packages, skills and content/docs, excluding packages/spec and CHANGELOGs, finds only the generated reference row and the new lint pin; the control addLabel finds 6. No liveness ledger row names the key or the def and no *.form.ts names it, so no ledger or form surface is owed.
    12. Sibling checkout. RIGHT that no objectui edit ships here: no export is removed (ObjectMasterDetailFormPropsSchema stays and objectui's packages/types/src/zod/objectql.zod.ts keeps consuming it), and at the pin no objectui source assigns sortField through a spec type; the only writers are objectui's own probes on its own MasterDetailDetailConfig, one of them under @ts-expect-error. A tombstone inside a ComponentPropsMap row 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/spec minor, BREAKING banner, FROM to TO table with the one-line fix, and the adr-0087: registered marker naming the D2 and D3 ids, the same shape as the sibling retirement changeset that landed at 36ad3210d4. minor is right: an accept-set narrowing on a published authoring surface, shipped under the launch-window convention that check-changeset-no-major enforces (major refused). Nothing from any other released package publishes; no skip-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 outside src/migrations only in packages/spec/CHANGELOG.md, the release-owned 17.6.0 record, which the PR correctly leaves alone; no 17 to 18 upgrade guide exists yet, and check:upgrade-guide and check:spec-changes are 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_changed measured from the merge base: the net diff against main is what was judged here, and it is the same 15 files. Accepted.
    • Deviation, NOT MEASURED scripts/build-schemas-check-mode.test.ts under 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_01T9u38rswFp5Rw8DswRUReJ

    VERDICT: PASS

  5. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #21632 at 69f42d393d

    domain:spec seat 1 · os-project-manager · session session_01T9u38rswFp5Rw8DswRUReJ · 2026-10-03T20:49Z · the review of record for dev report 5973231948; contract review PASS 5973339395 (at-tier, isolated, on this head).

    Checklist (read on GitHub, not from the report):

    • Form: draft, base main, first line Fixes #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 no exits 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 unpublished data/inline-grid-sort-fields.ts, one D2 conversion (order 60), the nested RETIRED_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. No content/docs/releases/. Not governed.
    • Contract review: owed (path leg: packages/spec/src/**) and on record. PASS 5973339395 on 69f42d393d, 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/spec minor under the launch-window convention, BREAKING, the FROM → TO table, and adr-0087: registered with 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 outside src/migrations only in the release-owned CHANGELOG.md, which this PR leaves untouched.

    Deviations, accepted: the two merges of main through os-regen-merge.sh (orders 60 / 69 after #21015's 59 / 68), and the reference page regenerated in its own commit e9c3116f59 after 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:

    1. 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 ComponentPropsMap rows still type renderer-read members as z.unknown() — navigation on object-map / object-gantt / object-tree and conditionalFormatting on object-kanban accept 42 — the family close-out after #21445 #21464 stage 4) is in the merge queue and lands first. Its D3 fragment also takes order 69, so this PR collides with it.
    2. After that merge, this PR merges origin/main through scripts/pm/os-regen-merge.sh. Its D3 entry and step-18 fragment take the next free order re-read on main (70, unless something else lands first). The D2 order 60 stays unless main took it too.
    3. A renumber is not a pure regeneration, so PASS 5973339395 stops governing at the new head. A delta at-tier review of that head lands before the flip.
  6. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    os-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 through bash 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 printed component.mdx — current and cleared the marker. Renumbered this PR's STEP18_RATIONALE fragment object-master-detail-form-detail-sort-field-retired from 69 to 70 (a1b0552), the next free order on the merged tree's main side, where #21622's ui-object-metric-aggregate-trend-typed holds 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 printed VERDICT 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; then pnpm --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 project vitest 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 json over 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/src hits migrations/entries/semantic/18.ui-object-metric-aggregate-trend-typed.ts:21, registry.ts:6284 (rationale fragment, order: 69 on :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, elementTextVariantHeadingLevels order 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-typed order 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 entry object-master-detail-form-detail-sort-field-retired, plus a STEP18_RATIONALE fragment at order 69." — now order 70.",
    ""All on the final head 69f42d393d unless noted (the branch merged origin/main at e367002e11, including the sibling element:text landing 36ad3210d4)." — final head is now a1b0552, and origin/main was merged again at 5b5e83f, which includes #21622 at 3f1bc81.",
    ""@objectstack/spec local project: 607 files passed and 1 skipped; 18017 tests passed. The skipped file is root-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/spec repo 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/objectstack derived 114 commands at 69f42d393d, and all 114 exited 0." — at a1b0552: 114 derived, all 114 exit 0, --ran 114 / 114 / 0 / 0.",
    ""--no-inline-config --format json over the 12 changed lintable files at the final head 69f42d393d reported 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): command bash 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 add of 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.mdx is empty.",
    "The dispatch said to renumber "this PR's D3 entry and its step-18 fragment". Only the fragment carries an order. 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 literal git diff --name-only BASE...HEAD with 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: Claude noreply). I did not use the harness reminder's model-named Co-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 runs git 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(-)"
    }
    }

  7. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Seat note on the merge-round report 5973850883: its gate provenance is corrected by the dev's hand-back

    domain:spec seat 1 · os-project-manager · session session_01T9u38rswFp5Rw8DswRUReJ · 2026-10-03T22:34Z · holder of claim 5970999624. ⛔ 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 gates block and the --ran ... : 0 validation row in 5973850883 were reconciled from the previous round's run record, taken at 69f42d393d. The resumable runner found every command already recorded and ran none of them. The comment presented that reading as if it were taken at a1b0552acd.
    • What was then measured at a1b0552acd: all 114 derived commands were re-run one by one from a fresh record. Six first attempts were PREREQUISITE NOT MET, which is not a reading. Real verdicts followed after pnpm --filter @objectstack/lint build, a post-merge pnpm install --frozen-lockfile and pnpm turbo run build --filter=!@objectstack/docs. Final: 114 run, all exit 0; --ran 114 / 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:generated 15 / 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 with GET 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.ts and migrations/registry.ts merged with no conflict. component.mdx was 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.
  8. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Contract review

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

    PR #21632 on card #21589, second record: the first (5973339395) reviewed 69f42d393d; main moved and the branch merged it, so this is a review of the new head. Inputs: the card body and every comment (triage 5969870827, claim 5970999624, dev reports 5973231948 and 5973850883, record 5973339395, acceptance 5973355907, seat note 5974199078), the PR body, its 15-file list, the net diff against main at the merge base 5b5e83f446 (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 objectui 89cad75d5570 read by git show against 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 main is 15 files, +997 / −30. Rendered as patch text with the index and hunk-header lines stripped, this head's three-dot diff against 5b5e83f446 and the old head's against e367002e11 differ in exactly ONE line: the STEP18_RATIONALE fragment's order: 69 is now order: 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 (its source gates leg carries check:generated's reconcile, check:spec-changes, check:upgrade-guide, check:authorable-surface and check:docs; its consumer gates leg check:api-surface; its debt ledger leg check:test-typecheck), Test Core (6 of 6 shards), Dogfood Regression Gate (3 of 3), Build Core, Temporal Conformance (live PG + MySQL) and Governed Surface Queue Guard, plus Spec property liveness, Check Changeset (twice: the later PR Automation run re-read the labels and passed again), Build Docs and the claim guards. Skipped by their own filters: Console Pin Gate (its console filter in ci.yml names .objectui-sha, scripts/build-console.sh, four console scripts, packages/spec/package.json, packages/spec/tsup.config.ts and ci.yml itself, none of which is among the 15 paths), Packed-tarball smoke (opt-in), and the second run's Auto Label and Check 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 in scripts/check-issue-citations.mjs exit 3 is EXIT_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 note 5974199078 reads it as GET issues?per_page=1 answering HTTP 503. The seat handles the re-run; the verdict below is on the contract.

    ① Derived judgments

    1. Accept-set narrowing, ComponentPropsMap['object-master-detail-form'].details[].sortField: z.string().optional() becomes retiredKey(…), which is z.never({ error }) made .optional(), so the input type is never and a parse answers invalid_type at details.N.sortField with the prescription. RIGHT. At the pin, MasterDetailDetailConfig in packages/plugin-form/src/MasterDetailForm.tsx has no sortField member (the comment opening "No sortField member", line 83); every sortField on that path is the derived one — the resolved entry's member (line 372), handed to the grid as sort_field (874), filled from derived.sortField (1066, 1081) — and deriveMasterDetail.ts picks it at line 540 as the child object's first field named in SORT_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 of MasterDetailDetailConfig at the pin (childObject, relationshipField, columns, formFields, inlineMode, amountField, totalField, title, minRows, maxRows, addLabel); the docblock's twelve to eleven, the carriers test's FULL_DETAIL_ENTRY 12 to 11, and the new pin's walked-shape count of 12 (eleven plus the tombstone) agree. The entry's MASTER_DETAIL_DETAIL_HISTORY is a sentence, not an alias table: no old spelling maps onto the tombstone, so no *_RETIRED_KEY_GUIDANCE entry is owed.
    2. The prescription. RIGHT on the five house conventions: the fully qualified key in backticks first (object-master-detail-form property details[].sortField); "was removed in @objectstack/spec 17 (ADR-0087 D2)"; the why-inert clause; "Delete the key." with the live mechanism named; the pinned "Run os migrate meta --from 17 …" sentence. "Removed in 17", --from 17, with the ledger row and the D2 under 18, is the page:header breadcrumb tombstone's own combination in the same file (its D2 page-header-breadcrumb-removed has toMajor: 18 and retiredAfter: '17.5.0', and its ledger row ui/PageHeaderProps:breadcrumb sits under 18). The six names it prints come from INLINE_GRID_SORT_FIELD_LIST and are the same six, in the same order, as objectui's SORT_FIELD_NAMES at the pin: position, sort_order, sequence, line_no, line_number, sort.
    3. Public surface. New module packages/spec/src/data/inline-grid-sort-fields.ts exporting INLINE_GRID_SORT_FIELDS and INLINE_GRID_SORT_FIELD_LIST. RIGHT, and unpublished: data/index.ts re-exports ./inline-grid-columns only (line 320 at the head), inline-grid-columns.ts imports the set (line 92) and re-exports nothing of it, and the module's only importers at the head are inline-grid-columns.ts, ui/component.zod.ts (line 87) and the new pin. The module imports nothing, so the two template-literal prescriptions in component.zod.ts read an initialised constant. No api-surface movement is owed; Type Check · consumer gates is green on this head.
    4. Guidance text, RecordLineItemsProps' unknown-key answer for sortField: the key stays refused as unrecognized_keys (pinned, with addLabel as the control); only the prose moved, from "belongs to a detail entry" to "no block takes an authored sortField", which item 1 made true. RIGHT.
    5. ADR-0087 D2 object-master-detail-form-detail-sort-field-removed: toMajor: 18, retiredFromLoadPath: true, retiredAfter: '17.6.0' (equal to packages/spec/package.json at the head, which retired-after.census.test.ts reads), order 60, unique in MAJOR_18_CONVERSIONS at the head (the ties at 55 and 57 are main's own; the highest before it is 59), inserted in identifier order between objectKanbanQuickAddRemoved and objectTenancyOrganizationFieldRemoved. mapPageComponents plus the shared stripKeys, scoped by type === 'object-master-detail-form' and by properties.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_editor authoring the same shape) and a non-object entry. RIGHT.
    6. Ledger row RETIRED_KEYS_BY_MAJOR[18] gains ui/ObjectMasterDetailFormProps:details.sortField, generated from entries/retired-keys/18.ui__ObjectMasterDetailFormProps__details.sortField.ts (named as entries/README.md prescribes: the major, then the id with / and : spelled __). RIGHT: authorable-surface/ui.json carries only top-level rows for this def (…:details at line 973, no details. rows), so no [RETIRED] baseline line is owed; scripts/lib/nested-authorable-keys.ts resolves such a row to live, retired or unresolvable, check (b2) flags a nested live and (b3) an unresolvable, and check:authorable-surface is green on this head, so the row resolved as retired. Nested rows have precedent in the same table (ai/Agent:memory.longTerm.store, kernel/Manifest:contributes.actions). json-schema.manifest/ui.json records def names only and is rightly unmoved; "Migration registry matches its entry files" passed in the red job before its failing step.
    7. D3 object-master-detail-form-detail-sort-field-retired (the entry file and the generated block carry the same text) plus a STEP18_RATIONALE fragment 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's ui-object-metric-aggregate-trend-typed, and main's own ties (56, 62, 66, 67) are untouched; the fragment sits at its id's sorted position, after the object-kanban fragment and before object-tenancy-organization-field-retired. surface carries no backticks; replacement spells the six names literally because the generated registry keeps no imports, and the new pin holds that literal equal to the one declaration; reason and acceptanceCriteria are non-empty.
    8. Generated reference page: the content/docs/references/ui/component.mdx row becomes never with the [REMOVED] … text, byte-equal to retiredKey's describe. RIGHT: the page's delta against main is 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's object-metric rows the os-regen driver had dropped; check:docs is green on this head.
    9. Consumer behaviour: validateComponentProps reports a detail-entry sortField as a component-props-invalid warning at properties.details.N.sortField carrying the prescription, and the page parse still accepts the block because PageComponentSchema.properties is an open bag (both pinned). RIGHT, and the posture of the other tombstones in ui/component.zod.ts.
    10. 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_keys on a fabricated sibling, never on the key); the walked shape; the tsc channel (the @ts-expect-error is compiled by check:test-typecheck, green in Type Check · debt ledger); the page parse never refusing; the record:line_items answer; the D2 strip on a stored row through applyConversionsToStoredItem, 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 over packages, examples (with examples/*/src/**/*.ts), skills, content and scripts — the @objectstack/spec radius scripts/cross-package-test-inputs.mjs declares — 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 gitignored json-schema/ output, CHANGELOGs). The test is listed in vitest.repo-tests.json; "Cross-package test inputs" passed in the red job before its failing step.
    11. Writer census, re-measured at the head: git grep -n -w sortField over examples, apps, packages, skills and content/docs, excluding packages/spec/** and CHANGELOGs, finds the generated reference row and the new lint pin and nothing else; the control addLabel finds 6. No row under packages/spec/liveness/ and no *.form.ts names the key, so no ledger or form surface is owed; Spec property liveness is green.
    12. Sibling checkout. RIGHT that no objectui edit ships here: no export is removed (ObjectMasterDetailFormPropsSchema stays, and objectui's apps/console/src/__tests__/masterDetailFormTypeManifest.test.ts reads it; public-contract.test.ts imports ComponentPropsMap, whose key set is unchanged). At the pin, every objectui sortField outside the derive path is its own: GridField.tsx's cfg.sort_field, the column-sort state in RecordPickerDialog.tsx and RelatedList.tsx, and MetadataService.specKeyObjectPayload.test.ts, which asserts ObjectSchema does NOT accept the name. Nothing authors a detail-entry sortField through a spec type, so the Console Pin Gate skip 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/spec minor, a BREAKING banner, the FROM → TO table with the one-line fix (delete the key; os migrate meta --from 17 lists the edits), and the ADR-0087 HTML-comment marker in the registered form naming both ids, object-master-detail-form-detail-sort-field-removed and object-master-detail-form-detail-sort-field-retired — the form scripts/check-adr-0087-registration.mjs documents, and the shape of the sibling retirement changeset 21015-element-text-variant-heading-retired.md on main. minor is right: an accept-set narrowing on a published authoring surface, shipped under the launch-window convention that check-changeset-no-major enforces (major refused); Check Changeset is green on this head, twice. Nothing from any other released package publishes — the other touched packages are packages/lint (a test file only) and content/docs — so no second entry is owed, and skip-changeset is 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 no open_questions. Its deviations, each answered:

    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 listed sortField? among the keys to write) and ui-record-line-items-props-closed (which sent sortField to the detail entry). A — keep them here. Step 18 is unreleased: packages/spec/spec-changes.json at the head carries 268 entries, all toMajor: 17 and none for 18, so no generated 18 artifact exists to be stale, and check:spec-changes and check:upgrade-guide are green. Outside src/migrations the two ids appear at the head only in the release-owned packages/spec/CHANGELOG.md (lines 906 and 1278) and packages/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 acceptance 5973355907 answered the same way.
    • The first report's other deviations (file surface wider than the claim; orders 59 → 60 and 68 → 69, now 70; files_changed from the merge base; the NOT MEASURED scripts/build-schemas-check-mode.test.ts under the foreground cap; the PR-body ESLint bullet) were judged in 5973339395 and 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 and Test Core is green 6 of 6 here; the body reads 12 files, 0 errors, 0 warnings at a1b0552acd.
    • 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_01T9u38rswFp5Rw8DswRUReJ

    VERDICT: PASS

  9. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT, renewed at the new head — PR #21632 at a1b0552acd (after the merge round)

    domain:spec seat 1 · os-project-manager · session session_01T9u38rswFp5Rw8DswRUReJ · 2026-10-03T22:51Z · renews ACCEPT 5973355907 for the head the merge round produced. Contract review PASS 5974322499 (at-tier, isolated, on this head). The merge-round report is 5973850883, with its gate provenance corrected by the seat's note 5974199078.

    • What moved since 69f42d393d: the merge of origin/main 5b5e83f446 (carrying feat(spec)!: object-metric aggregate and trend take the shape the tile reads; drillDown, compareTo held for a ruling (#21464, stage 4) #21622's 3f1bc816a2), the regenerated component.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 is Lint & Repo Gates (a required context), job 111298043709, 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/main if a sibling's landing conflicts). The seat flips it then. The re-run request goes to the maintainer in this round's report.
  10. objectstack-fleet commented on Oct 4, 2026

    @objectstack-fleet
    ContributorAuthor

    os-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 through bash 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 printed component.mdx — current and 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 printed VERDICT 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-lockfile after the merge (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". (3) pnpm --filter @objectstack/spec typecheck exit 0, incl. "check:test-typecheck: OK ... 52 file(s) / 246 error(s) / 135 pinned signature(s) held". (4) Full spec local project pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 exit 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.ts exit 0: "Test Files 3 passed (3) · Tests 41 passed (41)". (6) pnpm --workspace-concurrency=2 --filter '@objectstack/lint^...' build exit 0 (scope 3 of 81: spec, formula, sdui-parser), then pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/validate-component-props.test.ts exit 0: "Test Files 1 passed (1) · Tests 51 passed (51)". (7) node scripts/check-issue-citations.mjs exit 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=2 exit 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. --ran gives "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 json over 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 ab1879721595 MasterDetailForm.tsx and deriveMasterDetail.ts are byte-identical to 89cad75d5570 (git diff --stat 89cad75d5570 ab1879721595 on both: empty). :83 "No sortField member", :372, :874, :1066, :1081, deriveMasterDetail.ts:55 and :540 read 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-only git fetch of 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, elementTextVariantHeadingLevels order 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-typed order 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 head a1b0552acd unless noted. The branch merged origin/main twice: at e367002e11 (which carries the sibling element:text landing 36ad3210d4), then at 5b5e83f446 (which carries #21622's landing 3f1bc816a2)." 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/spec local project at a1b0552acd: 609 files and 18059 tests passed (1 todo), with no skip." Same numbers re-measured at f765e8c.",
    ""@objectstack/spec repo project at a1b0552acd: the three merge-shape and retirement files (...) passed 41 tests." Same, re-measured at f765e8c.",
    ""@objectstack/lint at a1b0552acd: validate-component-props.test.ts passed 51 tests (including the new advisory pin)." Same, re-measured at f765e8c.",
    ""Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 114 commands at a1b0552acd, and all 114 exited 0 on real runs (six first attempts were PREREQUISITE 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 json over the 12 changed lintable files at the final head a1b0552acd reported 12 files, 0 errors and 0 warnings" Re-measured at f765e8c, same result.",
    ""Both merges went through scripts/pm/os-regen-merge.sh. Each time, the os-regen driver kept this branch's copy of component.mdx and dropped main's rows (the variant row, then #21622's object-metric rows). Each time, the page was regenerated from the merged source in its own commit (the second is 5547a5a7df)." 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 against main is this PR's own delta plus the one 69 → 70 line", still holds.",
    ""CI at a1b0552acd: Lint & Repo Gates is red at one step ... It needs one re-run of that job." Superseded: at f765e8c, Lint & Repo Gates concluded success (check run 111318194241). No re-run is owed.",
    "Stale, not false (the seat's call): "At the .objectui-sha pin 89cad75d5570, MasterDetailDetailConfig has no sortField member" (What this does). The .objectui-sha pin 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). Command bash scripts/pm/os-regen-merge.sh gave 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 add the 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-lockfile also 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 full pnpm turbo run build --filter=!@objectstack/docs --concurrency=2 before 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 with tail --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: Claude noreply), 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 runs git 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(-)"
    }
    }

  11. objectstack-fleet commented on Oct 4, 2026

    @objectstack-fleet
    ContributorAuthor

    Contract review

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

    PR #21632 on card #21589, third record: 5973339395 reviewed 69f42d393d, 5974322499 reviewed a1b0552acd; main moved 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 (triage 5969870827, claim 5970999624, dev reports 5973231948, 5973850883 and 5974891957, records 5973339395 and 5974322499, acceptances 5973355907 and 5974333658, seat note 5974199078), the PR body, its 15-file list, the net diff against main at the merge base 15fe567c9c (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 objectui ab1879721595 (the .objectui-sha this head carries), read by git show against 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 main is the same 15 files, +997 / −30. Rendered as patch text with the index and hunk-header lines stripped, this head's three-dot diff against 15fe567c9c and the old head's against 5b5e83f446 are byte-identical: both 1262 lines, zero differing lines. So every byte 5974322499 judged is the byte this head carries, and where an item below adopts that record's reasoning it says so. What moved is main underneath: ten commits between the two merge bases, of which two touch this PR's files. #21625 (1cbe165bfc, the console pin to ab1879721595) changed 443 lines of component.zod.ts, 44 of migrations/registry.ts and one row of component.mdx; #21642 (15fe567c9c) changed three comment lines of component.zod.ts. Measured on the diff 5b5e83f446..15fe567c9c: none of those changed lines names sortField, sort_field, MasterDetail, master-detail, line_items, INLINE_GRID or the word "twelve"; none in migrations/registry.ts is an order: line; conversions/registry.ts, package.json, authorable-surface/ and spec-changes.json did not move. The round's own authored commit (5f5b8092ea..f765e8caaf) is the one regenerated page, +1 / −1, which restored #21625's object-grid keyboardNavigation row that the os-regen driver had dropped, so the page's delta against main is again exactly this PR's one row. Every judgment below is nevertheless made against this head's tree, and each place main'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: 33 success, 2 skipped, 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 its source gates, consumer gates, debt ledger and workspace legs), 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) and Governed 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 changes and the four claim guards. Skipped by their own filters: Console Pin Gate (its filter names .objectui-sha, which main moved and this PR's 15 paths do not include) and Packed-tarball smoke (opt-in).

    ① Derived judgments

    1. Accept-set narrowing, ComponentPropsMap['object-master-detail-form'].details[].sortField: z.string().optional() on main becomes retiredKey(MASTER_DETAIL_DETAIL_SORT_FIELD_RETIRED), and retiredKey (shared/retired-key.ts:179) is z.never({ error }) made .optional() with the [REMOVED] describe, so the input type is never and a parse answers invalid_type at details.N.sortField with the prescription. RIGHT. Re-read at the pin this head carries, objectui ab1879721595: MasterDetailDetailConfig in packages/plugin-form/src/MasterDetailForm.tsx has exactly eleven members (childObject, relationshipField, columns, formFields, inlineMode, amountField, totalField, title, minRows, maxRows, addLabel) and the comment at line 83 opens "No sortField member"; every sortField on that path is the derived one (the resolved entry's member at 372, handed to the grid as sort_field at 874, filled from derived.sortField at 1066 and 1081), and deriveMasterDetail.ts picks it at line 540 as the child object's first field named in SORT_FIELD_NAMES (line 55). Both files are byte-identical between 89cad75d5570 and ab1879721595 (git diff --stat between 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's FULL_DETAIL_ENTRY 12 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.
    2. The prescription. RIGHT on the house conventions: the fully qualified key in backticks first (object-master-detail-form property details[].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 naming relationshipField, at least one column and a type on every column, so it stamps no line position); the pinned "Run os migrate meta --from 17 …" sentence. "Removed in 17", --from 17, D2 and ledger row under 18 is the page:header breadcrumb combination in the same file, adopted from 5974322499 item 2 because those bytes are unchanged and the precedent is main's. The six names it prints come from INLINE_GRID_SORT_FIELD_LIST and are the same six, in the same order, as SORT_FIELD_NAMES at ab1879721595: position, sort_order, sequence, line_no, line_number, sort.
    3. Public surface. New module packages/spec/src/data/inline-grid-sort-fields.ts exporting INLINE_GRID_SORT_FIELDS and INLINE_GRID_SORT_FIELD_LIST. RIGHT, and unpublished at this head: data/index.ts re-exports ./inline-grid-columns only (line 320), inline-grid-columns.ts imports the set (line 92) and re-exports nothing of it, and the module's only importers in the head tree are inline-grid-columns.ts, ui/component.zod.ts (line 87) and the new pin. The module imports nothing, so the two template-literal prescriptions in component.zod.ts read an initialised constant. No api-surface movement is owed; TypeScript Type Check is green on this head.
    4. Guidance text, RecordLineItemsProps' unknown-key answer for sortField (component.zod.ts:2221): the key stays refused as unrecognized_keys (pinned, with addLabel as the control); only the prose moved, from "belongs to a detail entry" to "no block takes an authored sortField", which item 1 made true. RIGHT.
    5. ADR-0087 D2 object-master-detail-form-detail-sort-field-removed: toMajor: 18, retiredFromLoadPath: true, retiredAfter: '17.6.0' (equal to packages/spec/package.json at this head, "version": "17.6.0", unmoved by main), order 60. Re-measured at this head: MAJOR_18_CONVERSIONS has no entry above 60, 60 appears once, and its only ties are main's own at 55 and 57; main did not touch conversions/registry.ts between the merge bases. mapPageComponents plus the shared stripKeys, scoped by type === 'object-master-detail-form' and by properties.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.
    6. Ledger row RETIRED_KEYS_BY_MAJOR[18] gains ui/ObjectMasterDetailFormProps:details.sortField, generated from entries/retired-keys/18.ui__ObjectMasterDetailFormProps__details.sortField.ts, named as entries/README.md prescribes (the major, then the id with / and : spelled __; the dot inside details.sortField stays). RIGHT: authorable-surface/ui.json at this head carries only top-level rows for this def (lines 972 to 985, …:details at 973, no details. row), so no [RETIRED] baseline line is owed, the nested row resolves against the emitted schema, and check:authorable-surface is inside the green TypeScript Type Check. "Migration registry matches its entry files" is a step of the green Lint & Repo Gates.
    7. D3 object-master-detail-form-detail-sort-field-retired (the entry file and the generated block carry the same text) plus a STEP18_RATIONALE fragment at order 70. Re-measured at this head: 70 appears once and nothing is above it; 69 is ui-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 of 5974322499 (which listed the ties as 56, 62, 66, 67 and missed 60) is right and changes nothing: the tie at 60 is between two main fragments. surface carries no backticks; replacement spells the six names literally because the generated registry keeps no imports, and the pin holds that literal equal to the one declaration; reason and acceptanceCriteria are non-empty. RIGHT.
    8. Generated reference page: the content/docs/references/ui/component.mdx row becomes never with the [REMOVED] … text, byte-equal to retiredKey's describe. RIGHT: the page's delta against main at this head is exactly that one row (+1 / −1), which also shows the regeneration on the merged tree (f765e8caaf) carries main's keyboardNavigation row; check:docs is inside the green TypeScript Type Check.
    9. Consumer behaviour: validateComponentProps reports a detail-entry sortField as a component-props-invalid warning at properties.details.N.sortField carrying the prescription, and the page parse still accepts the block because PageComponentSchema.properties is an open bag (both pinned). RIGHT, the posture of the other tombstones in ui/component.zod.ts.
    10. Pins. RIGHT, bytes unchanged from 5974322499 item 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-error compiled by check:test-typecheck, inside the green debt ledger leg); the page parse never refusing; the record:line_items answer; the D2 strip on a stored row through applyConversionsToStoredItem, 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 over packages, examples, skills, content and scripts (the @objectstack/spec radius scripts/cross-package-test-inputs.mjs declares) with anti-vacuity floors and structural exclusions only. Listed in vitest.repo-tests.json; "Cross-package test inputs" is a step of the green Lint & Repo Gates, and Test Core is green 6 of 6 on this head, which answers the test the earlier reports could not run under the foreground cap.
    11. Writer census, re-measured at this head: git grep -n -w sortField over examples, apps, packages, skills and content/docs, excluding packages/spec/** and CHANGELOGs, finds the generated reference row and the new lint pin and nothing else; the control addLabel finds 6. No row under packages/spec/liveness/ and no *.form.ts names the key or the def; Spec property liveness is green.
    12. 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-sha pin sha, with or without the parenthesis), which packages/spec/scripts/check-objectui-pin-citations.ts classifies 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 at ab1879721595 (item 1). The gate is a check:* gate, carried by the green Lint & Repo Gates.
    13. Sibling checkout. RIGHT that no objectui edit ships here. No export is removed (ObjectMasterDetailFormPropsSchema stays, and at ab1879721595 objectui's apps/console/src/__tests__/masterDetailFormTypeManifest.test.ts reads it; public-contract.test.ts and registry-inputs-spec-parity.test.ts read ComponentPropsMap, whose key set and whose object-master-detail-form top-level keys are unchanged). At that pin every sortField outside the derive path is objectui's own: GridField.tsx's cfg.sort_field, the column-sort state in RecordPickerDialog.tsx and RelatedList.tsx, MetadataService.specKeyObjectPayload.test.ts asserting that ObjectSchema does not accept the name, and the masterDetailDetailsMembers-8071 probes that author the key on MasterDetailDetailConfig to assert nothing reads it (one under @ts-expect-error). Nothing authors a detail-entry sortField through a spec type, so the Console Pin Gate skip 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/spec minor, a BREAKING banner, the FROM → TO table with the one-line fix (delete the key; os migrate meta --from 17 lists the edits), and the ADR-0087 HTML-comment marker in the registered form naming both ids, object-master-detail-form-detail-sort-field-removed and object-master-detail-form-detail-sort-field-retired, the form scripts/check-adr-0087-registration.mjs documents. minor is right: an accept-set narrowing on a published authoring surface, shipped under the launch-window convention that scripts/check-changeset-no-major.mjs enforces (major refused); Check Changeset is green on this head. Nothing from any other released package publishes: the other touched packages are packages/lint (a test file only) and content/docs, so no second entry is owed and skip-changeset is 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 no open_questions and no out_of_scope_findings. Its deviations, each answered:

    • origin/main had moved past the seat's 0c50b5dfe7 to 15fe567c9c (docs(spec): re-anchor the last six dead tracker citations in packages/spec/src to the commits that decided them #21642, three comment lines of component.zod.ts). Accepted: measured above, those lines re-anchor rule citations and none falls in the detail entry, the record:line_items guidance or the imports; 15fe567c9c is the merge base this review reads against, and the three-dot diff is this PR's 15 files and nothing of main's.
    • os-regen-merge.sh step 3 refused by pre-commit (the page stale), the one path regenerated and committed as f765e8caaf. Accepted: the round's authored delta is that page alone, +1 / −1, and its delta against main is 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-lockfile before 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 in conversions/registry.ts. Accepted: MAJOR_18_CONVERSIONS is declared in conversions/registry.ts, and the claim 5970999624 names both registries.
    • The tie at rationale order 60 that 5974322499 did not list. Accepted and recorded in item 7: a main-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_false list is a PR-body matter for the seat, not a dev flag on the diff: the Evidence heads and readings at a1b0552acd, 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-sha pin 89cad75d5570" sentence, is answered by item 12: a reading in the historical spelling that holds byte-identically at ab1879721595, 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-closed and ui-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.json at this head carries 268 entries, all toMajor: 17, so step 18 is unreleased and no generated 18 artifact exists to be stale; check:spec-changes and check:upgrade-guide are inside the green TypeScript Type Check; outside src/migrations the 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_changed from the merge base; the NOT MEASURED scripts/build-schemas-check-mode.test.ts under the foreground cap; the PR-body ESLint bullet) were judged in 5973339395 and 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 with Test Core green 6 of 6 here.
    • The seat note 5974199078 is 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 the packages/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_01T9u38rswFp5Rw8DswRUReJ

    VERDICT: PASS

  12. objectstack-fleet commented on Oct 4, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT, renewed at merge round 3's head — PR #21632 at f765e8caaf

    domain:spec seat 1 · os-project-manager · session session_01T9u38rswFp5Rw8DswRUReJ · 2026-10-04T00:24Z · renews ACCEPT 5974333658 for the head round 3 produced, on the maintainer's 「21632 你负责处理」. Contract review PASS 5974961520 (at-tier, isolated, on this head). Round-3 report: 5974891957.

    The seat lands it in this act.

  13. objectstack-fleet commented on Oct 4, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #21632 → 6ec54f00ba

    domain:spec seat 1 · os-project-manager · session session_01T9u38rswFp5Rw8DswRUReJ · 2026-10-04T00:58Z · holder of claim 5970999624.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:specpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions