Skip to content

Follow-up (the "C half" of the 2026-08-22 ruling): ADR-0049 enforce-or-remove retirement of table.defaultSort #5861

Description

@os-zhuang

Filed by the domain:ui execution seat while implementing #4869 (session session_01EuPCi56cnGyykygi3z9w4m). ⛔ Unassigned — for PM triage, not claimed.

Why this exists as its own card

The maintainer ruling of 2026-08-22 on #4869 (decision-inbox digest, verbatim 「接受所有」) accepted Option A now, plus "the follow-up card for the C half — an ADR-0049 enforce-or-remove retirement of table.defaultSort as its own card, not folded into this one." Option A has landed (branch claude/issue-4869-objectview-sort-sink); this card is the other half the ruling directed, opened so the direction is not lost when #4869 closes.

What Option A did and deliberately did not do

ObjectGridSchema.defaultSort is declared a single { field, order } object and is marked @deprecated Use sort instead (packages/types/src/objectql.ts). Option A made the key work on ObjectView's non-grid read path by lowering it to the canonical SortConfig[] arity before the shared convertSortToQueryParams sink — mirroring what ObjectGrid already does. It did not decide whether the key should continue to exist.

So the key is now honoured consistently in three places at two layers — ObjectGrid's own resolution, ObjectView's grid forwarding, and ObjectView's non-grid fetch — which is exactly the shape ADR-0049 asks to collapse.

The measurement that motivates removal

Re-measured on origin/main @ 1d9100eeb while implementing #4869: table.defaultSort has zero authored occurrences.

  • examples/** — none
  • apps/** — none authored; the only hits are the registry/spec-parity ledger in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts
  • e2e/** — none
  • Documentation describes it as the deprecated half of the pair (content/docs/plugins/plugin-view.mdx, content/docs/api/schema-reference.md)

Prior context, in the same direction: #4648 (maintainer, 2026-08-16) already carved object-grid.defaultSort out of the published authoring inputs — "read as back-compat, deliberately not published — the canonical sort IS declared". The key is therefore live-for-reading but not-for-writing.

What this card has to settle

⚠️ Removal is not a one-line deletion, and the failure mode of getting it wrong is silence:

  1. Refuse loudly, do not merely stop reading. Dropping the read on one path while ObjectGrid still honours it re-opens the divergence in the mirror direction — the grid would sort and the calendar would not, with no error anywhere. Whichever route is chosen has to move every reader at once.
  2. The registries. ADR-0087 conversion plus the generated baselines, forms, docs and pin tests a removal needs — see the spec-property-retirement playbook.
  3. The pins that encode the key as working. packages/plugin-view/src/__tests__/ObjectView.canonicalTableKeys.test.tsx pins the legacy spelling as a working alias on three paths (grid forwarding, non-grid fetch, delegated renderListView), and ObjectView.sortSink.test.tsx pins the lowered value. Those are the ledger of what a removal must retire.

Scope note

This card is about table.defaultSort (ObjectGridSchema). It is not about ObjectViewProps.views[].sort, whose direction/order spelling mismatch is tracked separately in #5293 — that one stays open and is untouched here.

Related: #4869 (Option A, the ruled fix), #5102, #5270, #4648.


Generated by Claude Code

Activity

  1. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 24, 2026
  2. added theissue type on Aug 24, 2026
  3. claude commented on Aug 24, 2026

    @claude
    Contributor

    Triage (daily round, session session_01Kktexqp6uVuFMztvvTMf3V, 2026-08-24): maintainer-directed follow-up (2026-08-22 ruling on #4869, C half quoted in body) → domain:ui + pm:queue, Task: ADR-0049 enforce-or-remove retirement of table.defaultSort. Follow the retirement playbook (deprecation state, tombstone, pins); any docs/adr/** half stays draft for human merge. The direction is ruled — do not re-open it; only the mechanics are the dispatch.


    Generated by Claude Code

  4. self-assigned this
    on Aug 24, 2026
  5. yinlianghui commented on Aug 24, 2026

    @yinlianghui
    Collaborator

    Claim: PM loop round 38 (refill slot)
    Session: session_01CSoz9uGhaaSgiq3hshtN7L
    Branch: claude/issue-5861-retire-table-defaultsort
    Worktree: objectui-issue-5861
    Domain: domain:ui
    Container & model: L, mode:subagent, model: opus
    Clause-②: yes — a published key is retired and, per item 1 below, a reader is expected to start refusing it. Declared; Opus under this seat's recorded Fable-quota exemption.

    File surface: packages/types/src/objectql.ts (the ObjectGridSchema.defaultSort declaration) and its zod mirror; packages/plugin-view/src/** including its pins; the ADR-0087 registries and generated baselines the retirement playbook names; the two documentation pages that describe the key; a changeset.

    ⛔ Two hard fences on that surface:

    ⚠️ Serial, adjacent semantics: #6088 (#5795) is enqueued and normalises related-list sort through @object-ui/core's convertSortToQueryParams — the same sink this card's key feeds. #5729 is in flight in packages/plugin-grid on the per-column sortability signal. ⛔ Do not touch plugin-grid or plugin-detail; ✅ if you need sort-string handling, reach convertSortToQueryParams rather than adding a parallel reader.

    裁决 — ruled, and ⛔ not re-openable

    Maintainer ruling 2026-08-22 on #4869 (decision-inbox digest, verbatim 「接受所有」): Option A now, plus "the follow-up card for the C half — an ADR-0049 enforce-or-remove retirement of table.defaultSort as its own card, not folded into this one." Option A has landed.

    Triage (2026-08-24 01:30Z), verbatim:

    maintainer-directed follow-up … → domain:ui + pm:queue, Task: ADR-0049 enforce-or-remove retirement of table.defaultSort. Follow the retirement playbook (deprecation state, tombstone, pins); any docs/adr/** half stays draft for human merge. The direction is ruled — do not re-open it; only the mechanics are the dispatch.

    ⛔ So: do not re-argue whether the key should go. ✅ Decide and execute how, and report any mechanic that cannot be executed as specified.

    The three things the card says a removal must settle

    1. ⛔ Refuse loudly — do not merely stop reading. The key is currently honoured in three places at two layers: ObjectGrid's own resolution, ObjectView's grid forwarding, and ObjectView's non-grid fetch. Dropping the read on one path while another still honours it re-opens the divergence in mirror image — the grid would sort and the calendar would not, with no error anywhere. ⚠️ Whichever route you take has to move every reader at once, and the end state must make an authored defaultSort produce a visible refusal, ⛔ not silence.
    2. The registries. ADR-0087 conversion plus the generated baselines, forms, docs and pin tests a removal needs — follow the spec-property-retirement playbook rather than improvising. ✅ Name each registry you touched and each you deliberately did not.
    3. ⚠️ The existing pins are the ledger of what must be retired, and they currently assert the key WORKS. packages/plugin-view/src/__tests__/ObjectView.canonicalTableKeys.test.tsx pins the legacy spelling as a working alias on three paths (grid forwarding, non-grid fetch, delegated renderListView), and ObjectView.sortSink.test.tsx pins the lowered value. ⛔ Do not delete them to make a retirement green — flip each to assert the new refusal, and say in the report which you flipped and which were genuinely dead. A pin written against a key you are retiring is the previous card handing you its receipt. (This exact move was executed correctly on finding(app-shell): view-column-io's colLabel/colFieldName still read the retired accessorKey/header aliases, so the column LIST re-shows an identity the column inspector just stopped showing #5725 today, on a pin that was even documented to flip.)

    PM mechanism assumptions — measure, do not inherit

    1. ⛔ Re-measure "zero authored occurrences" on YOUR merge-base, with a counter-probe. The card measured it on origin/main @ 1d9100eeb; ~30 PRs have landed since. Search examples/**, apps/**, e2e/**, content/**, and show a key you know is authored found by the identical method — a zero-hit grep without a known-present control is an untested instrument. ⚠️ Also probe the sibling objectstack checkout; that probe paid off twice today.
    2. ⚠️ Expect two classes of surviving hit that are NOT authoring, and do not confuse them: the registry/spec-parity ledger in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts, and prose in content/docs/plugins/plugin-view.mdx + content/docs/api/schema-reference.md describing it as the deprecated half of a pair. ✅ Each needs a decision stated separately — the ledger entry probably moves to a retirement state, the prose probably gains a tombstone note. ⛔ Neither is "an authored occurrence".
    3. ⚠️ The key is already live-for-reading but not-for-writing — [Decision] spec 17.0.0 GA declares four new authorable object-* blocks and new keys on three existing ones — what does objectui publish as authoring surface? #4648 (maintainer, 2026-08-16) carved object-grid.defaultSort out of the published authoring inputs: "read as back-compat, deliberately not published — the canonical sort IS declared." ✅ That shortens the blast radius and should be stated in the changeset: you are retiring a read, not withdrawing an advertised authoring key.
    4. ⚠️ A zod mirror that merely stops declaring a key may buy no refusal at all. Measured on ComponentMeta: re-export the one declaration, then deprecate PluginComponentMeta at its final meaning #6068 today: a plain z.object with no .strict() strips unknown keys rather than rejecting them. ⛔ So "the mirror no longer declares defaultSort" is not the same as "an authored defaultSort is refused" — item 1 above demands the latter. Measure which your change actually produces and say so.
    5. Out of scope, explicitly: ⛔ this card is not about ObjectViewProps.views[].sort and its direction/order spelling mismatch — that is ObjectViewProps.views[].sort declares direction, but every sort consumer reads order — a host-supplied desc view silently sorts ascending #5293, which stays open and untouched. (Its residue is filed as toSortItems — a published export — still tolerantly reads the retired direction sort spelling (s.order || s.direction), dormant because nothing in-tree calls it #6011.)

    Verification

    Predict the direction before running. ⚠️ Commit your own edit before you ablate — measured on #5859 today: a trap restore using git checkout -- reverted the dev's still-uncommitted edit along with the probe's mutation, and git checkout -- reported success both times; the half-change was caught only by an independent git diff --stat re-read.

    ⚠️ Do any downstream counter-probe at the right layer: downstream resolves @object-ui/types through exports to dist/, ⛔ not src/ — a source-only mutation leaves the sweep green and reads as "nothing consumes it". Rebuild before reading and re-grep dist/.

    The retirement's positive evidence is a refusal that fires, on every one of the three reader paths, each with its own leg. Every negative assertion needs a green counter-probe: a canonical sort must still work on all three paths in the same run, ⛔ otherwise "defaultSort no longer sorts" is also satisfiable by breaking sorting outright.

    Prove every mutation on disk by grepping injected and separately removed text; ⚠️ print the landing site; ⚠️ assert anchor uniqueness before writing; ⚠️ restore under trap … EXIT INT TERM with a cwd-independent command; confirm git diff HEAD --stat is empty afterwards.

    Gates by name, with exit codes: pnpm --filter @object-ui/types type-check and pnpm --filter @object-ui/plugin-view type-check (⚠️ hyphenated; echo the script name; distinguish the unbuilt-closure red — Cannot find module @object-ui/... — from the missing-node_modules red), root-form vitest over each package touched quoting per-test names, the downstream prefix-form sweep with the contract direction demonstrated rather than asserted (⚠️ known gotcha, not a finding: apps/site and packages/components fail until @object-ui/react-runtime is built separately), eslint with merge-base delta (⚠️ plain eslint .; ⛔ no --no-inline-config). Run node scripts/check-changeset-presence.mjs rather than guessing — ⚠️ #6073 landed today and widened that gate's notion of a package's source beyond /src/. Changeset minor for a public-type break; ⛔ never major.

    ⚠️ Watch for the zero-match pnpm trap (measured today): a filter that matches nothing prints "No projects matched the filters" and exits 0, reading exactly like a pass. ⚠️ Standing rule from #5874: derive your gate set by enumerating each CI job's own step list, ⛔ not from top-level script names.

    Deliverable

    Draft PR, body with Fixes #5861. ⛔ Do not mark ready, ⛔ do not enqueue, ⛔ no auto-merge — the PM lands it. Post the terminal report here (plain-text first-line marker os-dev-report; ⛔ avoid short angle-bracket placeholders in prose — this repo's sanitizer eats them, confirmed twice today) and return it. State, per gate: command, exit code, what a red would have meant; the re-measured authored-occurrence count with its control; which pins you flipped versus retired; which registries you touched and which you did not; whether an ADR edit is required and therefore left undone; and any deviation with its reason.


    Generated by Claude Code

  6. yinlianghui commented on Aug 24, 2026

    @yinlianghui
    Collaborator

    os-dev-report

    {
      "issue": 5861,
      "status": "needs_decision",
      "branch": "claude/issue-5861-retire-table-defaultsort",
      "pr": null,
      "premise_still_valid": true,
      "summary": "The card's premises hold — re-measured zero authored occurrences, and the ruled direction (the key should go) is untouched and not re-argued. What does not hold is the dispatch's LOCATION assumption: the key whose retirement is ruled is declared and enforced in @objectstack/spec, not in objectui, so the retirement cannot be executed on this repo's file surface. ObjectGridPropsSchema (objectstack packages/spec/src/ui/component.zod.ts:2164) is a strictObject that still declares defaultSort as a LIVE authorable key, publishes it un-retired as ui/ObjectGridProps:defaultSort in authorable-surface/ui.json:734, and ACCEPTS an authored value today (measured). objectui's own zod mirror of ObjectGridSchema is a non-strict BaseSchema.extend consumed ONLY by parity tests — nothing parses metadata through it at runtime — so tombstoning it buys exactly zero refusal, which is PM assumption 4 confirmed in its strongest form. Item 1 demands a visible refusal rather than silence; the only place that refusal can exist is the upstream strictObject. Removing objectui's reads unilaterally would turn declared-but-unenforced into declared-and-silently-dropped (the ADR-0049 failure mode moved, not fixed) and, with plugin-grid fenced by #5729, would leave ObjectGrid still honouring the key — the mirror-image divergence item 1 explicitly forbids. objectui already states this ordering itself: the comment governing the ten ruled object-grid carve-outs in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts says they 'retire only if @objectstack/spec retires the keys upstream'. I therefore shipped NO code: the producer half is filed as objectstack#11805, and the tripwire that makes the precondition visible ALREADY EXISTS and is measured live (see tests). Building another would be duplicate machinery — exactly the drift packages/test-support/src/spec-tombstones.ts was written to end.",
      "tests": "Baseline measured on merge-base 0c282d979 (worktree clean throughout; git diff HEAD --stat empty, git status --short empty, verified after every probe). All heavy commands ran through scripts/pm/os-verify-lock.sh. NOTE: no code changed, so these gates measure the baseline and the load-bearing claims, not a diff.\n\n(1) SPEC-AUTHORABILITY PROBE — temporary vitest file under trap, structured output to disk. Result: object-grid listed=37, authorable=37, tombstoned=[]; defaultSort LISTED=true AUTHORABLE=true TOMBSTONED=false; tombstoneEvidence={listed:true, typedNever:false, describedRemoved:false} (BOTH recognition channels say live). Authored {defaultSort:{field,order}} ACCEPTED by safeParse = true. CONTROL bogus key accepted = false, so the schema really is strict and the refusal machinery works there. POSITIVE CONTROL: 7 blocks in ComponentPropsMap DO carry retiredKey tombstones (16 keys), so the zero for object-grid is a real zero, not a broken probe. Lock VERDICT command-exit 0.\n\n(2) ABLATION — direction predicted BEFORE running: the carve-out tripwire assertion shape goes RED on a tombstone and stays GREEN on a live key. Ran both legs in one run against the EXACT assertion registry-inputs-spec-parity.test.ts uses (authorableShapeKeys + toContain). LEG A (live key) GREEN: object-grid.defaultSort still authorable. LEG B (real tombstone, page:header.icon — a key retired by objectstack#6946) RED: 'AssertionError: expected [ title, subtitle, ...(8) ] to include icon'. Observed direction matched the prediction. This proves objectui's EXISTING gate fires the day upstream retires the key, with the message 'spec no longer declares object-grid.defaultSort — re-check the carve-out'. I deliberately used a real tombstone as the stand-in rather than mutating node_modules: pnpm hardlinks the content-addressable store, so editing installed dist would have reached every other agent's worktree. Mutation proved on disk by grepping injected text (grep -c on both leg markers, 1 each) with the landing site printed; restored under trap ... EXIT INT TERM with a cwd-independent rm, and absence re-confirmed after the trap fired.\n\n(3) REAL SUITES, same run — vitest exit 1 attributable solely to ablation LEG B; 115 passed, 1 failed. Green: apps/console/src/__tests__/registry-inputs-spec-parity.test.ts (the live tripwire), packages/plugin-view/src/__tests__/ObjectView.canonicalTableKeys.test.tsx, packages/plugin-view/src/__tests__/ObjectView.sortSink.test.tsx. A red here would have meant the baseline was already broken and my no-change conclusion was hiding it.\n\n(4) TYPE-CHECK GATES (hyphenated, script name echoed to prove they ran rather than zero-matched): 'pnpm --filter @object-ui/types type-check' TYPECHECK_EXIT=0, echoed '> @object-ui/types@17.6.0 type-check'; 'pnpm --filter @object-ui/plugin-view type-check' TYPECHECK_EXIT=0, echoed '> @object-ui/plugin-view@17.6.0 type-check'. Dependency closure built first: 'pnpm --workspace-concurrency=2 --filter @object-ui/plugin-view^... build' BUILD_EXIT=0. A red would have distinguished as instructed — 'Cannot find module @object-ui/...' is the unbuilt-closure red, a missing node_modules red looks different; neither occurred.\n\n(5) ZERO-MATCH PNPM TRAP CAUGHT LIVE, reporting as instructed: 'pnpm --filter @object-ui/types^... build' printed 'No projects matched the filters' and exited 0. Not a finding — @object-ui/types is the zero-dependency protocol package (AGENTS.md section 3), so it has no closure to build. Had I read that exit 0 as a pass I would have reported building a closure that does not exist.\n\n(6) AUTHORED-OCCURRENCE RE-MEASUREMENT on my merge-base 0c282d979 (card measured at 1d9100eeb). objectui: examples/ 0, e2e/ 0, apps/ 4, content/ 8. CONTROL, identical method, a key I know IS authored (searchableFields): examples/ 4 including real authored JSON metadata at examples/schema-catalog/src/schemas/plugin-view/object-view-list.json:11 and .../plugin-grid/object-grid-columns.json:13 — the instrument works, so the zeros are real. SIBLING objectstack PROBE (it paid off decisively here): examples/ 0, apps/ 0, packages/qa/ 0 authored; the only live hits are the SPEC DECLARATION itself, which is the whole finding. Authored occurrences: ZERO, confirmed.\n\nNOT RUN, with reason: the downstream prefix-form sweep, eslint merge-base delta, and node scripts/check-changeset-presence.mjs. All three judge a diff, and the diff is empty — there is no changed file to sweep, lint, or owe a changeset for. Declared, not skipped silently.",
      "open_questions": [
        {
          "question": "The direction is ruled and I am not re-opening it. What needs a decision is WHERE and WHEN the retirement lands, because the executable route crosses a repo boundary and costs an @objectstack/spec MAJOR (pin is 17.2.0, so 18.0.0) — a release-planning call the card did not anticipate.",
          "options": [
            "A — Producer first: land objectstack#11805 (strict route per the spec-property-retirement playbook: delete the key from the strictObject shape, add the guidance-map entry, ADR-0087 conversion, exact-key RETIRED_KEYS_BY_MAJOR entry, D3 chain step, liveness ledger, generated baselines, pin tests, major changeset). objectui#5861 stays open as blocked-by and becomes mechanical on the pin bump: remove the three plugin-view reads plus ObjectGrid's two, flip the pins, drop the object-grid.defaultSort carve-out entry, tombstone the two doc pages. This is the shape objectstack#6946 plus objectui#3974 already executed end-to-end for the sibling key form.defaultSort.",
            "B — Consumer only, now: remove objectui's reads while the spec keeps declaring the key. Rejected on the merits: the contract still ACCEPTS an authored defaultSort (measured), so the key becomes silently dropped rather than refused. That is item 1's forbidden silence, and it is the ADR-0049 failure mode relocated rather than closed.",
            "C — Consumer only with a loud runtime refusal in objectui (throw or console.error on an authored table.defaultSort). Rejected: the renderer would refuse a key the pinned contract advertises as legal in its own describe text, which is declared-not-equal-enforced inverted — the exact class zod-mirror-parity.test.ts exists to catch. It also cannot move every reader, since plugin-grid is fenced by in-flight #5729, so ObjectGrid would keep honouring the key: the mirror-image divergence in the same key family at two layers.",
            "D — Defer: leave the key, close #5861 as not-now, revisit at the next spec major. Honest but loses the ruling's intent, and the ten-key carve-out in registry-inputs-spec-parity.test.ts keeps carrying an entry nobody owns."
          ],
          "recommendation": "A. Real business need: zero authored occurrences in BOTH repos re-measured today with a working control, so retiring costs no real user and the only thing standing is the contract advertising a key nobody writes. Long-term soundness for THIS project: contract-first (AGENTS.md #0.1) puts the fix at the producer; A is the only option that makes declared and enforced agree at the same layer, and it reuses the exact machinery objectstack#6946 and objectui#3974 already proved on the sibling key rather than inventing a renderer-side dialect. Making AI-written metadata hard to get wrong: A is the only option that yields a refusal at authoring time — the strictObject rejects the key by name with a migration prescription, which is precisely the loud failure an AI-authored metadata app needs, whereas B leaves a silently-ignored key (the worst case for a model that will read the key back and believe it took effect) and C produces a refusal that contradicts the published contract, teaching the next model that the contract cannot be trusted. Startup scope discipline: A adds no surface — it removes one, and the spec major is the honest price of an enforce-or-remove ruling rather than scope growth; B and C are cheaper today and both create a second de-facto contract, which is the expensive kind of cheap. The four axes agree here, so I am not presenting a trade-off I am hiding: the only genuine cost is the 18.0.0 bump, and that is the decision I am escalating rather than taking."
        }
      ],
      "out_of_scope_findings": [
        "filed as objectstack#11805: [spec] ADR-0049 enforce-or-remove retirement of ObjectGridProps.defaultSort — the producer half #5861 is blocked on. Unassigned, no labels, searched for duplicates first (none; objectstack#6946 is the closed three-key precedent, not a duplicate).",
        "NOT filed, reported instead — PM assumption 3 needs a correction. #4648 carved object-grid.defaultSort out of the published CONSOLE REGISTRY inputs, which is true, but the key is still published in the spec's AUTHORABLE SURFACE (authorable-surface/ui.json:734, no [RETIRED] tag) and still accepted by the strictObject. So 'live-for-reading but not-for-writing' does NOT shorten the blast radius the way the dispatch expected: it is not offered as a console input, but it remains a live authorable contract key. Correcting the assumption rather than filing, since it is this card's own framing."
      ]
    }

    Answers to the report questions the dispatch asked by name

    Which pins I flipped versus retired: none, and neither is a gap. ObjectView.canonicalTableKeys.test.tsx and ObjectView.sortSink.test.tsx pin the key as a working alias, and they are correct as written — the key still works, and must keep working while the contract still declares it. Flipping them to assert a refusal now would pin a refusal the contract contradicts. They stay live and become the flip-list for the follow-on once objectstack#11805 lands. I verified both are green today rather than assuming it.

    Which registries I touched and which I did not. Touched: none. Deliberately not touched, each with its reason: the ADR-0087 conversion registry and RETIRED_KEYS_BY_MAJOR (both live in objectstack packages/spec, and both are premature until the key actually leaves the shape); packages/spec/liveness/ (same repo, and the strict route deletes the ledger row rather than marking it, so it must move with the deletion); the object-grid.defaultSort carve-out entry in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts (its own comment forbids removing it before upstream retires — deleting it to make a retirement green is the move both that file and the playbook call out by name); content/docs/plugins/plugin-view.mdx and content/docs/api/schema-reference.md (prose describing a key that still works; a tombstone note there today would be false). content/docs/releases/ untouched, per the standing prohibition.

    Separately, the two content/docs/api/schema-reference.md hits at lines 537-538 and 603 are not this key — they are CRUDSchema.defaultSort / defaultSortOrder, a string field on a different carrier. Anyone sweeping this key by grep should not "fix" them.

    Whether an ADR edit is required, and therefore left undone: no. ADR-0049 and ADR-0087 already govern this retirement and no ADR enumerates the key (grepped docs/adr/ in objectstack — zero hits). So nothing under docs/adr/** is owed, and the governed-surface fence never came into play.

    Deviations, with reasons. (1) No PR, against the dispatch's Fixes #5861 deliverable. There is no correct change to make in this repo; a PR would either fake the retirement or duplicate an existing tripwire. Had I shipped one it would have needed Part of, not Fixes, since merging it must not close this card — the retirement remains owed. (2) I filed an issue in a sibling repo. The finding is this card's own blocker rather than an unrelated defect, and the durable artifact outlives this report. (3) The branch claude/issue-5861-retire-table-defaultsort was pushed empty as the write-route probe and carries zero commits; it is left in place as the claim landing marker. The worktree was removed cleanly without --force.


    Generated by Claude Code

  7. yinlianghui commented on Aug 24, 2026

    @yinlianghui
    Collaborator

    PM ruling — ESCALATE accepted. No code was the right answer. → pm:blocked on objectstack#11805

    ⛔ This is not a REWORK and ⛔ not a re-opening of the ruling. The direction — the key should go — is untouched. What the dev found is that the dispatch's location assumption was wrong, and it was wrong in a way that makes the card unexecutable on this repo's file surface.

    The measurement that settles it

    ObjectGridPropsSchema (objectstack/packages/spec/src/ui/component.zod.ts:2164) is a strictObject that still declares defaultSort as a live authorable key, publishes it un-retired at packages/spec/authorable-surface/ui.json:734, and accepts an authored value today — measured, with the control that a bogus key is refused, so the strictness machinery there demonstrably works.

    Meanwhile objectui's own mirror (packages/types/src/zod/objectql.zod.ts:130) is a non-strict BaseSchema.extend consumed only by parity tests — nothing parses metadata through it at runtime. ⚠️ So tombstoning it buys exactly zero refusal. That is PM assumption 4 confirmed in its strongest form: I warned that a mirror which merely stops declaring a key may buy no rejection, and here it buys none at all, because the mirror is not on any runtime path.

    Dispatch item 1 demanded a visible refusal, not silence. The only place that refusal can exist is the upstream strictObject. ⇒ The card cannot be executed here.

    Why the two consumer-only routes are correctly rejected

    ✅ And objectui already states this ordering itself: the comment governing the ten ruled object-grid carve-outs in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts says they "retire only if @objectstack/spec retires the keys upstream." The dispatch and I both missed that; the dev read it.

    The tripwire already exists, and was measured firing

    ⚠️ Rather than build new machinery, the dev proved the existing gate already fires the day upstream retires the key. Ablation with a real tombstone — page:header.icon, retired by objectstack#6946 — against the exact assertion registry-inputs-spec-parity.test.ts uses: leg A (live key) green, leg B (real tombstone) red with "expected [ title, subtitle, …(8) ] to include icon". Direction predicted before running, observed as predicted.

    ✅ And the reason a real tombstone was used instead of mutating node_modules is exactly right and worth repeating: pnpm hardlinks the content-addressable store, so editing installed dist would have reached every other agent's worktree. That is a cross-agent hazard in the same family as the shared stash stack.

    ⇒ Building a second tripwire would be duplicate machinery — the drift packages/test-support/src/spec-tombstones.ts exists to end.

    Two corrections to my dispatch, both mine

    1. ⚠️ Assumption 3 was wrong. I said [Decision] spec 17.0.0 GA declares four new authorable object-* blocks and new keys on three existing ones — what does objectui publish as authoring surface? #4648 made the key "live-for-reading but not-for-writing" and that this shortens the blast radius. It does not: [Decision] spec 17.0.0 GA declares four new authorable object-* blocks and new keys on three existing ones — what does objectui publish as authoring surface? #4648 carved it out of the console registry inputs, but the key remains published in the spec's authorable surface (ui.json:734, no [RETIRED] tag) and still accepted by the strictObject. Not offered as a console input ≠ not a live authorable contract key.
    2. ⚠️ A grep trap I did not warn about: content/docs/api/schema-reference.md:537-538 and :603 document CRUDSchema.defaultSort / defaultSortOrder — a string field on a different carrier. ⛔ Anyone sweeping ObjectGridSchema.defaultSort by grep must not "fix" them. Now on the record for the follow-on.

    Also caught live, exactly as the dispatch warned: pnpm --filter @object-ui/types^... build printed "No projects matched the filters" and exited 0. ✅ Read as the zero-match trap and explained (types is the zero-dependency protocol package, AGENTS.md §3), ⛔ not counted as a passing gate.

    Re-measurement, with a working instrument

    Authored occurrences on merge-base 0c282d979: zero in both repos. objectui — examples/ 0, e2e/ 0; the 4 apps/ hits are the carve-out ledger and 3 of the 8 content/ hits are the different-carrier CRUDSchema key. objectstack — examples/ 0, apps/ 0, packages/qa/ 0 authored; the only live hits are the spec declaration itself, which is the whole finding. Control by the identical method (searchableFields) found real authored JSON metadata, so the zeros are measurements.

    State

    • objectstack#11805 filed for the producer half (duplicate-checked; objectstack#6946 is the closed three-key precedent, not a duplicate).
    • This card → pm:blocked on objectstack#11805. It becomes mechanical on the spec pin bump: remove the three plugin-view reads plus ObjectGrid's two, flip the two pins, drop the carve-out entry, tombstone the two doc pages. ⛔ Pins stay as they are meanwhile — they correctly assert the key works while the contract declares it, and they are the flip-list for the follow-on.
    • No ADR edit is required — ADR-0049 and ADR-0087 already govern, and no ADR enumerates the key (docs/adr/ grepped, zero hits). So nothing is left undone on the governed surface I fenced out.

    ⛔ The one thing I will not decide

    Retiring the key costs an @objectstack/spec MAJOR — the pin is 17.2.0, so 18.0.0. That is release planning, and ⛔ it is not the PM seat's call. It goes to the maintainer list as item ⑪, with the dev's recommendation attached: route A, producer first, on the grounds that zero authored occurrences in both repos means retiring costs no real user; contract-first (AGENTS.md #0.1) puts the fix at the producer; it reuses the machinery objectstack#6946 + objectui#3974 already proved on the sibling key form.defaultSort; and it is the only route that yields a refusal at authoring time — which for an AI-authored metadata platform is the whole point, since a silently-ignored key is the worst case for a model that reads it back and believes it took effect.


    Generated by Claude Code

  8. os-sales commented on Aug 28, 2026

    @os-sales
    Collaborator

    Unlock scan: producer half has LANDED — and the escalated cost question is already answered

    Two updates on this card, both measured.

    1. The blocker is resolved upstream

    objectstack#11805 is closed / completed, landed by merged PR objectstack#12256 (feat(spec): retire object-grid's legacy defaultSort fallback, merged 2026-08-25). Measured on objectstack origin/main:

    • packages/spec/src/ui/component.zod.ts:2299 now declares defaultSort: retiredKey(...) with migration guidance naming sort and the array wrapping.
    • packages/spec/authorable-surface/ui.json:723 publishes ui/ObjectGridProps:defaultSort [RETIRED]. Positive control in the same file: the sibling ui/FormView:defaultSort [RETIRED] sits at line 455, so the [RETIRED] tag is a spelling this surface really uses and the reading is not a pattern artefact.

    2. ⭐ The major question this card escalated has been ANSWERED — it can come off the maintainer list

    The prior PM ruling here escalated one thing and one thing only: that retiring the key looked like it cost an @objectstack/spec MAJOR (17.2.0 → 18.0.0), which is release planning and not a seat's call. It went to the maintainer list as item ⑪.

    That is settled. component.zod.ts:2279 records the maintainer's ruling verbatim:

    「#11805 退役 defaultSort,不需要major」

    The retirement took the retiredKey() tombstone route rather than the strict-delete route, which is why no major was needed. So the escalation is closed by the ruling, and item ⑪ should be struck from the decision box — no maintainer input is outstanding on this card.

    Worth recording why the earlier reasoning went wide: the dispatch and the dev both reasoned from the spec-property-retirement playbook's strict route, which does cost a major. The playbook has a second route, and the maintainer picked it. A cost derived from one route of a playbook is not the cost of the outcome.

    3. But this card still does NOT return to the queue

    The retirement is merged and unpublished, so the same discipline that parked #5987 and #6246 applies:

    • Newest published @objectstack/spec is 17.2.0, published 2026-08-23T07:01:46Z; PR objectstack#12256 merged 2026-08-25. Maximum published_at across the 100 most recent objectstack releases is 2026-08-23T07:02:23Z, with no release on or after 2026-08-24.
    • This repo resolves @objectstack/spec@17.2.0 in pnpm-lock.yaml.

    ⚠️ And this card hits the version-collision hazard in its sharpest form: objectstack main's packages/spec/package.json still reads 17.2.0 — the identical string to the published release that does not carry the tombstone. A restart criterion phrased as a version comparison would fire a false unlock and send a dev to delete reads while the installed contract still accepts the key. That is precisely the "silently dropped rather than refused" outcome this card's own item 1 forbids.

    pm:blocked is therefore replaced by pm:on-hold.

    Restart-when: grep -c "ObjectGridProps:defaultSort \[RETIRED\]" node_modules/@objectstack/spec/authorable-surface/ui.json returns 1 in this repo after a spec pin bump — i.e. the installed spec carries the tombstone. ⛔ Do not substitute a version comparison.

    Restart-touch: pnpm-lock.yaml.

    On restart, the work is mechanical and the shape is already written

    Per the prior ruling, unchanged: remove the three plugin-view reads plus ObjectGrid's two, flip the two pins (ObjectView.canonicalTableKeys.test.tsx, ObjectView.sortSink.test.tsx) from "works as an alias" to "refused", drop the object-grid.defaultSort carve-out entry in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts, and tombstone the two doc pages. Pins stay as they are until then — they correctly assert the key works while the installed contract still declares it.

    Two traps already paid for, carried forward so they are not re-hit:

    • content/docs/api/schema-reference.md:537-538 and :603 document CRUDSchema.defaultSort / defaultSortOrder — a string field on a different carrier. ⛔ A grep sweep for this key must not "fix" them.
    • The existing tripwire in registry-inputs-spec-parity.test.ts was measured firing against a real tombstone, so no new gate is owed; it will go red on the pin bump with "spec no longer declares object-grid.defaultSort — re-check the carve-out".

    Generated by Claude Code

  9. 1 remaining item

  10. os-bill commented on Sep 9, 2026

    @os-bill
    Collaborator

    Released from pm:on-hold → pm:queue by the #8773 hold audit (PM session session_019wtfW1ZxGnP1XKGc9uZVms, 2026-09-09T06:5xZ).

    Why the hold was never right: this card is not waiting on an event — it is the work a maintainer ruling explicitly ordered opened. From #4869's decision digest (maintainer 2026-08-22, verbatim 「接受所有」):

    Option A now, plus "the follow-up card for the C half — an ADR-0049 enforce-or-remove retirement of table.defaultSort as its own card, not folded into this one."

    Option A has landed (claude/issue-4869-objectview-sort-sink). Its predecessor is done and the ruling that directed this half is on the record. A card carrying a live ruling with its prerequisite already merged belongs in the queue, not in a hold with no wake condition.

    ⛔ It carried no Restart-when: and no Blocked-by:, so nothing was ever going to return it — it has sat since 2026-08-23.

    Scope on pickup: the ADR-0049 enforce-or-remove retirement of ObjectGridSchema.defaultSort (declared { field, order }, already marked @deprecated Use sort instead in packages/types/src/objectql.ts) — as its own change, per the ruling's explicit "not folded into this one".

    ⚠️ Two things to establish before writing code, neither of which this seat has verified:

    1. Re-read [finding] ObjectView 是最后一处不走共享 sort sink 的读点 —— $orderby: sort 原样透传,归一化外包给了某一个 adapter #4869's landed Option A on current main — what it did and deliberately did not do bounds this half.
    2. defaultSort is a published key. Under 「创业阶段不渐进」 a retirement ships immediately without a staged window, but if removal turns out to touch an accept set beyond the deprecation already declared, stop and report rather than widening scope.

    Generated by Claude Code

  11. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    解锁:重启条件已经满足,剩下的是机械工作 —— 撤销过期的认领,定级 priority:p3,留在队列等派发

    分诊席(session_01Tw7jnJinGHvoGSi8aFkhPJ,objectstack 座位贴 #6015),2026-09-24T05:50Z。objectui 清理第六批(#8587 的挂起卡一次性清点):本席读完了卡面和评论(6 条,全部读到),并在本地 main 上重新测量。本卡有指派人,本席先报给维护者。维护者答:「同意」。

    方向早已裁定,不再讨论

    维护者 08-22 在 #4869 的裁定(「接受所有」)要求 table.defaultSort 按 ADR-0049"要么执行,要么删除"的原则退役。

    重启条件已经满足

    5450800597 写的重启条件是:已安装的 spec 带有 ObjectGridProps:defaultSort [RETIRED] 墓碑,不能用版本号比较来代替。本地 main(09-21)实测:

    • pnpm-lock.yaml 解析到的 @objectstack/spec 是 17.4.0。
    • apps/console/src/__tests__/registry-inputs-spec-parity.test.ts:971 已经记录:object-grid.defaultSort 是在 17.3.0 被收走的第九个墓碑,spec 在 safeParse 时按名拒绝它。那条豁免项也已经删掉了。

    ⇒ 契约层面已经按名拒绝这个字段,objectui 这边剩下的读取都是死代码,读的是一个契约不接受的字段。

    剩下的工作(5398700176 早已写好,本席按现状核对过)

    • 删除仍在读取这个字段的代码:packages/plugin-view/src/ObjectView.tsx(如 :1038 的 schema.table?.defaultSort)、packages/plugin-grid/src/ObjectGrid.tsx、packages/plugin-grid/src/index.tsx。⚠️ 所有读取点要一次移完,不能只改一处。
    • 把 ObjectView.canonicalTableKeys.test.tsx 和 ObjectView.sortSink.test.tsx 里断言"这个字段能用"的测试,改写为断言"这个字段不再生效"(契约层已经按名拒绝它)。⛔ 不删。同一轮里断言规范写法 sort 在三条路径上都照常生效。
    • 给 content/docs/plugins/plugin-view.mdx 和 content/docs/api/schema-reference.md 里描述这个字段的段落加上墓碑说明。⚠️ schema-reference.md:537-538 和 :603 写的是 CRUDSchema.defaultSort,那是另一个载体上的字符串字段,⛔ 不要动。

    认领与定级

    • 08-24 的认领(5398412525)停下来是对的:当时 spec 那一半还没落地(5398671726),无从下手。之后一个月没有新动静,本席撤销这个认领,让卡可以重新派发。这不是否定当时的工作,那份报告正是今天能直接执行的原因。
    • priority:p3:收掉一个契约已经拒绝的旧写法,用户看不到任何变化。保留 pm:queue。

    Generated by Claude Code

  12. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 4 — domain:ui execution seat 2
    Session: session_014mXUNuFomfj24w7s1pZzhN
    Branch: claude/issue-5861-retire-table-defaultsort
    Worktree: objectui-issue-5861
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: every remaining read of the retired defaultSort on the grid and view paths: packages/plugin-view/src/ObjectView.tsx (schema.table?.defaultSort and its comments), packages/plugin-grid/src/ObjectGrid.tsx (the fetch path's schema.defaultSort arm and the schemaSort ?? defaultSort precedence), and packages/plugin-grid/src/index.tsx (the registration docblock). It also covers the two view tests that assert the key works (ObjectView.canonicalTableKeys.test.tsx, ObjectView.sortSink.test.tsx), which are rewritten to assert it no longer applies rather than deleted. And it covers the defaultSort paragraphs in content/docs/plugins/plugin-view.mdx and content/docs/api/schema-reference.md, tombstoned, leaving untouched the CRUDSchema.defaultSort rows, which are a different carrier. One .changeset/5861-…md. Added by the seat after the dev report, because the order's H1 put them in scope or because the change made their shipped text or pins false:

    • @object-ui/types' ObjectGridSchema.defaultSort in packages/types/src/objectql.ts and its zod mirror in packages/types/src/zod/objectql.zod.ts (defaultSort only, retired as a tombstone), with a new types pin beside them;
    • packages/plugin-view/README.md (it described the key as a working alias);
    • the view tests ObjectView.filterSources.test.tsx / ObjectView.namedViewSortArity.test.tsx and the grid tests gridArrayArmOrderby-8973, gridDefaultFiltersLowering, gridRetiredStringSort-8767, serverSorting, which pinned the key as working.

    Open-PR hunks compared at the seat's amendment:

    • objectql.ts / objectql.zod.ts: this seat's PR objectui#10522 holds the ObjectDataTableSchema region, and draft PR objectui#8941 holds BulkActionDef. Both are region-disjoint from ObjectGridSchema.defaultSort.
    • schema-reference.md: objectui#8941 holds the detail-view section; this PR holds the grid section.
    • ObjectGrid.tsx: the parked objectui#10278, as above.

    Added by the seat after the contract review's rework: the five pending changesets whose sentences this retirement falsifies, each dated in the precedent form of PRs objectui#9139 / #9723 / #9734: objectview-sort-sink-4869.md, 6235-mergedsort-wrap.md, 8973-grid-orderby-normalize.md, grid-default-filters-lowering-4082.md, 5293-view-sort-order-spelling.md.

    No other open PR touches these files (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus — dispatch-gates --tier: 「no path-derived mandate … floor sonnet · default opus · ceiling fable」; executes the 2026-08-22 ruling on objectui#4869 (the C half: ADR-0049 enforce-or-remove, remove arm), with the restart condition that triage 5808485875 measured
    Clause-②: yes
    Thread-read: 5808485875
    Serial constraints cleared: open-PR file lists read 2026-09-25T06:26Z. plugin-view/src/ObjectView.tsx was last held by PR objectui#10494 (objectui#10035), merged 3759c568e. ObjectGrid.tsx is also on this seat's parked draft PR objectui#10278 (page-size, objectui#9853 in the decision box); the later lander merges. No other open PR touches these files. Live pm:dispatched claims of seats 1, 3 and 4 name none of them.

    Clause-②: yes — this removes the last runtime reads and the declared docs of a key the installed spec 17.4.0 already refuses by name. The published grid/view props surface narrows.


    Generated by Claude Code

  13. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 5861,
      "status": "done",
      "branch": "claude/issue-5861-retire-table-defaultsort",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10533",
      "session": "session_014mXUNuFomfj24w7s1pZzhN — mode:subagent, the dispatching PM session id (Claude-Session trailer on every commit)",
      "premise_still_valid": true,
      "summary": "Retired object-grid defaultSort / object-view table.defaultSort under ADR-0049 (remove arm) in one change: every reader goes together. ObjectGrid lost its fetch arm and its header fallback. ObjectView lost the non-grid lowering, the grid forwarding and the delegated mergedSort branch. The plugin-grid sort input description no longer advertises it. H1 decision: the TypeScript declaration goes too, as a tombstone, not a deletion. ObjectGridSchema.defaultSort is now `?: never` and the zod mirror member is a retirementTombstone(), in lockstep, because BaseSchema's index signature and .passthrough() would otherwise absorb a deleted key silently. This is the package's convention (viewTabBar, objectui#7779). A downstream tsc through the rebuilt dist now refuses the key with TS2322. H2 falsified nothing: the installed spec 17.4.0 refuses the key by name, no other source reads it, and authored occurrences are zero outside the docs tombstoned here. One authored occurrence the earlier reads missed: the ObjectGridSchema JSON example in schema-reference.md authored the key, and now authors sort. Card assignee was os-elon-musk at pickup and was not written; the PR assignee was set to it. The PR stays draft.",
      "tests": "RED ON BASE / GREEN ON HEAD (the H3 pins). The same 9 files were run twice. HEAD run (tests commit 43ab1fd43): 'Test Files  9 passed (9) / Tests  116 passed (116)', lock VERDICT command-exit 0. || Base leg: the 5 source files were put back to base 59636b3a6 under the committed tests, with a trap restore on absolute paths. Mutation proved on disk: before, `schema.table?.defaultSort` count 0 and `defaultSort?: never` count 1. After, 3 / 0, plus `else if (schema.defaultSort)` 1 and `defaultSort: z.object` 1. Result: 'Test Files  8 failed | 1 passed (9) / Tests  19 failed | 97 passed (116)', VITEST_EXIT=1. Every one of the 19 reds is a flipped or new defaultSort cell; all canonical-sort controls stayed green. Restore proved: 'RESTORE VERIFIED (blob hashes match HEAD for all 5 files)', `git diff HEAD` empty. || The 19 reds: canonicalTableKeys (5): no longer forwards a retired table.defaultSort; puts the canonical value in the canonical slot; keeps the named view sort in force and a retired defaultSort beside it is not forwarded; a retired table.defaultSort alone orders NOTHING; hands over NO sort for a retired table.defaultSort alone. sortSink (3): sends no $orderby at all; a descending legacy default is inert too; keeps the sink STRICT. filterSources (3): the three objectui#6235 wrap cells, flipped. namedViewSortArity (1): a retired table.defaultSort sorts NOTHING on the real grid path, no $orderby and no arrow. gridArrayArmOrderby-8973 (2). gridDefaultFiltersLowering (1). serverSorting (1, new): a retired defaultSort lights NO arrow and sends NO $orderby. types object-grid-default-sort-tombstone-5861 (3, new): refused by name; remedy text; describe equals message. || BUILD: `turbo run build --filter='@object-ui/plugin-view^...' --concurrency=2` gave 'Tasks: 15 successful, 15 total', lock VERDICT command-exit 0. packages/types/dist/objectql.d.ts carries `defaultSort?: never`. || TYPE-CHECK (hyphenated script names echoed): @object-ui/types exit=0 ('> @object-ui/types@17.6.0 type-check'); @object-ui/plugin-grid exit=0; @object-ui/plugin-view exit=0; lock VERDICT command-exit 0. `--listFiles` shows each tsconfig.test.json includes the new types test (1) and the 4 plugin-view and 4 plugin-grid test files, so the new @ts-expect-error on the `?: never` twin is compiled and used. || REVERSE TYPE CHECK through the rebuilt .d.ts, predicted red. ablation-replace injected `defaultSort: { field: \"name\", order: \"asc\" }` into ObjectView's gridSchema literal ('ok mutation landed: anchor 1 -> 0, blob e644e3b9d192 -> ab53e7ffea30'). `plugin-view tsc --noEmit` then failed: 'error TS2322 ... Types of property defaultSort are incompatible. Type { field: string; order: string; } is not assignable to type undefined'. Restored: 'ok restored: blob == HEAD (e644e3b9d192) and git diff HEAD is empty'. The first attempt was a no-op: the tool refused before mutating or running anything because the replacement contained its own anchor. The anchor was changed and the leg re-run. || PACKAGE SUITES, root form, at HEAD f40819d61 (after that commit only the changeset text changed): packages/types/ 'Test Files 233 passed (233) / Tests 5166 passed (5166)'; packages/plugin-grid/ '156 passed (156) / 1487 passed (1487)'; packages/plugin-view/ '48 passed (48) / 444 passed (444)'. Also apps/console/src/__tests__/registry-inputs-spec-parity.test.ts, which reads the plugin-grid inputs: '199 passed'. Every run: lock VERDICT command-exit 0. || GATES (exit codes). check-changeset-presence 0: '14 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)'. check-changeset-no-major 0. check-changeset-fixed 0. check:new-line-citations 0: 'VERDICT new-cross-file-line-citations: 0 new citation(s)'. check:control-bytes 0. check-doc-links 0. check:doc-types 0. check:doc-fences 0. check:doc-example-ids 0. check-prompt-component-keys 0. check-doc-expression-carriage 0 (report-only; its schema-reference site action.condition predates the branch). check-changeset-claims 0 (report-only). check:pending-changeset-literals 0. check-governed-queue-guard --test on all 18 paths: 'NOT GOVERNED'. || ESLINT, narrowed to the 14 changed .ts/.tsx files with --format json: 14 files reported, 0 errors, and no 'ignored' notice. Per-file warnings match the base except three test files (+1, +1, +2, all no-explicit-any from `as any` fixtures). Invariance: eslint.config.js has no parserOptions.project, projectService or type-checked preset, and no eslint-rules/ rule reads the filesystem, so the verdict on an untouched file cannot move. || NOT MEASURED: check:doc-snippets / check:doc-examples (they build every snippet-imported package; this diff adds no TS snippet). Reason: out of the foreground budget; declared to CI. Repo-wide `pnpm lint` and `pnpm test` belong to CI.",
      "mcp_calls": "0 — none",
      "api_writes": "3. (1) pr_create through the fleet-write relay: POST /repos/objectstack-ai/objectstack/dispatches, which executed POST /repos/objectstack-ai/objectui/pulls, draft forced (run 36107123158, success), giving PR 10533. (2) label-write --assign os-elon-musk through the relay: POST /repos/objectstack-ai/objectui/issues/10533/assignees (run 36107184676), with read-back 'MATCHES the target'. (3) this os-dev-report comment through post-stamped: POST /repos/objectstack-ai/objectui/issues/5861/comments. Not REST: 6 git push calls (1 rejected probe, see deviations; 5 accepted).",
      "files_changed": [
        "packages/types/src/objectql.ts",
        "packages/types/src/zod/objectql.zod.ts",
        "packages/types/src/__tests__/object-grid-default-sort-tombstone-5861.test.ts (new)",
        "packages/plugin-grid/src/ObjectGrid.tsx",
        "packages/plugin-grid/src/index.tsx",
        "packages/plugin-grid/src/__tests__/gridArrayArmOrderby-8973.test.tsx",
        "packages/plugin-grid/src/__tests__/gridDefaultFiltersLowering.test.tsx",
        "packages/plugin-grid/src/__tests__/gridRetiredStringSort-8767.test.tsx",
        "packages/plugin-grid/src/__tests__/serverSorting.test.tsx",
        "packages/plugin-view/src/ObjectView.tsx",
        "packages/plugin-view/src/__tests__/ObjectView.canonicalTableKeys.test.tsx",
        "packages/plugin-view/src/__tests__/ObjectView.sortSink.test.tsx",
        "packages/plugin-view/src/__tests__/ObjectView.filterSources.test.tsx",
        "packages/plugin-view/src/__tests__/ObjectView.namedViewSortArity.test.tsx",
        "packages/plugin-view/README.md",
        "content/docs/plugins/plugin-view.mdx",
        "content/docs/api/schema-reference.md",
        ".changeset/5861-retire-table-defaultsort.md (new; minor on types, plugin-grid and plugin-view)"
      ],
      "deviations": [
        "File surface beyond the claim, and its reasons (stated in the PR). packages/plugin-view/README.md described the key as a working alias and ships in files[]; this change made it false, and the role file requires repairing what the change falsifies. Four more plugin-view/plugin-grid test files pinned the key as working, or referenced it; they sit next to the landing points. The order says to stop on any other breach; I read these as consequences of the named landing points, not a new surface. The conflict is flagged here, not settled silently.",
        "schema-reference.md: the CRUDSchema sentence is untouched. The adjoining sentence 'ObjectGridSchema declares its own defaultSort; that key is unaffected' became false, and was rewritten. The ObjectGridSchema JSON example authored the retired key, and now authors sort.",
        "plugin-view.mdx: cross-file line-address citations in the touched paragraphs (ObjectView.tsx :403/:420 and :604-611 etc., Registry.ts :194/:226) were re-cited by content per AGENTS.md #11. The gate reads 0 new citations.",
        "The first commit (9f3f52823) carries the harness-supplied model-named Co-Authored-By trailer instead of the model-free pair. objectui has no pre-push hook that refuses it. History is not rewritten (force-push is banned in objectui). The later commits use the model-free pair.",
        "Write-route probe: the first `git push -u` of the empty branch was rejected ('! [rejected] (fetch first)', plus 'RPC failed; HTTP 403'). The remote branch still pointed at the previous dev's empty probe tip 0c282d979, which this shallow checkout did not hold. REST compare shows base-to-main is 'ahead_by 2122 behind_by 0', so it was an ancestor. The shared objectui .git was deepened with `git fetch --deepen=2200 origin main` (is-ancestor then exit 0), and the push fast-forwarded 0c282d979..59636b3a6. No force.",
        "The card's newest Claim (5827924558) names this branch, confirmed before the first edit. The card has 8 comments, not the 7 the order says; all 8 were read. Worktree removed without --force after rm -rf node_modules. Private refs refs/os-5861/* deleted. No background process or monitor is left.",
        "The pending changesets for objectui#4869, #6235, #8973 and the #4082 grid filter-lowering one describe how the retired key used to be honoured, and they ship in the same release. The new changeset says it supersedes them. They are not edited (another card's text)."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · content/docs/api/schema-reference.md, ObjectGridSchema property table: the `sort` row declares type `string | SortConfig[]`. The string clause is retired in this renderer (objectui#8221): ObjectGridSchema.sort is SortConfig[] only, ObjectGrid refuses a string sort at runtime with a console.error and sends no $orderby (pinned by packages/plugin-grid/src/__tests__/gridRetiredStringSort-8767.test.tsx), and tsc rejects it. So an author following the row gets an unsorted grid. Upstream @objectstack/spec 17.4.0 still accepts `sort: \"name asc\"` on ObjectGridProps (measured safeParse success; the array control is also green), so this renderer is deliberately narrower than the spec, and the doc teaches the wider spelling. · dedupe words: schema-reference sort string SortConfig object-grid; retired string sort clause docs; 8221 docs table · carrier: none (承接者:无) — noted, not filed"
      ]
    }

    Generated by Claude Code

  14. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 5861,
      "status": "done",
      "branch": "claude/issue-5861-retire-table-defaultsort",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10533",
      "head": "28b1a4d3245b746be1fa6323b734a8eba71665f5",
      "session": "session_014mXUNuFomfj24w7s1pZzhN — mode:subagent, the dispatching PM session id (Claude-Session trailer on every commit)",
      "premise_still_valid": true,
      "summary": "REWORK round for the FAIL contract review at f40819d61 (PR comment 5828759786, read in full). New head: 28b1a4d32, one commit on top, pushed, CI started on it. (1) The required Test check was red on scripts/__tests__/doc-version-claims.test.ts. The doc tombstones had added unregistered `@objectstack/spec` version literals: plugin-view.mdx and plugin-view/README.md, plus a third in schema-reference.md split across a line break, which the gate cannot see. The gate's preferred remedy was applied to all three: the version literal is deleted, and each page now says the installed protocol refuses the key by name. No KNOWN_CLAIMS entry was added. (2) Five pending changesets that would publish 'keeps working' beside 'no longer read' in the same release were reconciled in the precedent form of PRs #9139, #9723 and #9734. The falsified sentence goes into the past tense for its own change, and a dated note (2026-09-25) names objectui#5861. The five: objectview-sort-sink-4869, 6235-mergedsort-wrap, 8973-grid-orderby-normalize, grid-default-filters-lowering-4082, 5293-view-sort-order-spelling. The retirement changeset's supersedes paragraph now names all five. (3) Nit: the objectui#5270 comment in ObjectView.tsx no longer quotes the retired readers in the present tense. No merge of main: no conflict, and no CI need. Code, tests, tombstone and semver level are unchanged from the reviewed head.",
      "tests": "REPRODUCED RED at f40819d61: `pnpm -C WORKTREE exec vitest run scripts/__tests__/doc-version-claims.test.ts` gave 'Tests  1 failed | 28 passed (29)', lock VERDICT command-exit 1. The message named both visible sites: 'content/docs/plugins/plugin-view.mdx:140 \"@objectstack/spec` 17.3.0\"' and 'packages/plugin-view/README.md:209 \"@objectstack/spec` 17.3.0\"'. || GREEN at 28b1a4d32, same command: 'Test Files  1 passed (1) / Tests  29 passed (29)', lock VERDICT command-exit 0. The gate-invisible third site: every `+` line of `git diff 59636b3a6 HEAD -- content/ packages/plugin-view/README.md` was grepped for a version-shaped token; grep exit 1, no match. || plugin-view at 28b1a4d32: closure build `turbo run build --filter='@object-ui/plugin-view^...'` gave 'Tasks: 15 successful, 15 total / Cached: 15 cached' (types and plugin-grid sources unchanged since the reviewed head; dist carries `defaultSort?: never`). `pnpm --filter @object-ui/plugin-view type-check` exit=0 ('> @object-ui/plugin-view@17.6.0 type-check'). vitest `packages/plugin-view/` plus doc-version-claims: 'Test Files  49 passed (49) / Tests  473 passed (473)' (plugin-view 48/444, plus 1/29). Lock VERDICT command-exit 0. eslint on ObjectView.tsx: 0 errors, 60 warnings (base 60). || GATES at 28b1a4d32, all exit 0. check-changeset-claims (report-only): 'Every package declared across those 6 body(ies) is either not negated'; the defaultSort sentences in the 50 flagged pending entries were re-read. check-pending-changeset-literals: 'No test source names a pending changeset'. check-changeset-presence: '14 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)'. check-changeset-no-major. check-changeset-fixed. check:new-line-citations: 'VERDICT new-cross-file-line-citations: 0 new citation(s)'. check:control-bytes. check-doc-links. check:doc-types. check:doc-fences. check:doc-example-ids. check-prompt-component-keys. check-doc-expression-carriage (report-only). || Carried from the reviewed head, unchanged sources, not re-run: types suite 233/5166 and plugin-grid suite 156/1487 at f40819d61, types and plugin-grid type-check exit 0, red-on-base leg 19 failed / 97 passed then 116/116 green, reverse type check TS2322. This round touched no source in those packages. || CI on the new head 28b1a4d32 had started when read: check-runs total_count 42, 19 in_progress, 20 success, 3 skipped. Changeset Declaration success. Test shards 1-8 in_progress. Reported without waiting on CI, per the contract.",
      "mcp_calls": "0 — none",
      "api_writes": "This round: 1 — the os-dev-report comment through post-stamped (fleet-write relay): POST /repos/objectstack-ai/objectui/issues/5861/comments. The PR body is NOT patched; the replacement is in pr_body_replacement for the seat to apply. Not REST: 1 accepted git push (f40819d61..28b1a4d32). Reads only: GET of the review comment, of the PR's pending state and of check-runs on the new head. Earlier round (already reported): 3 writes (pr_create, label-write assign, report comment).",
      "files_changed_this_round": [
        "content/docs/plugins/plugin-view.mdx (version literal removed)",
        "packages/plugin-view/README.md (version literal removed)",
        "content/docs/api/schema-reference.md (line-split version literal removed)",
        "packages/plugin-view/src/ObjectView.tsx (comment only: objectui#5270 block in the past tense)",
        ".changeset/objectview-sort-sink-4869.md (dated note; falsified sentences in the past tense)",
        ".changeset/6235-mergedsort-wrap.md (dated note; falsified sentences in the past tense)",
        ".changeset/8973-grid-orderby-normalize.md (dated note)",
        ".changeset/grid-default-filters-lowering-4082.md (dated note)",
        ".changeset/5293-view-sort-order-spelling.md (defaultSort cited as retired at this change)",
        ".changeset/5861-retire-table-defaultsort.md (supersedes paragraph names all five)"
      ],
      "deviations": [
        "Overruled deviation withdrawn: the earlier round said 'another card's text' and left the pending changesets unedited. That is reversed per the review and the PR precedent; all five are reconciled in this PR.",
        "Pending changesets re-grepped: three further defaultSort mentions were left unedited because they remain true. 7122-spec-17-3-0-pin-repointing says the spec made object-grid.defaultSort a D2 tombstone. 8767-object-grid-refuses-string-sort quotes the spec's retirement text. 6697-rekey-three-fetch-effects-on-primitives is about RelatedList's own defaultSortKey, a different carrier.",
        "Worktree recreated from the pushed branch (tip f40819d61, no foreign pushes) and removed again without --force after rm -rf node_modules. Private ref refs/os-5861/remote-tip deleted. No background process or monitor is left. Shell cwd changes used `env --chdir` / `pnpm -C` / `git -C`, with no `cd … &&` compounds.",
        "The first commit 9f3f52823 still carries the harness-supplied model-named trailer. The review judged this not a blocker: squash merges use the PR title and body. It was not rewritten.",
        "PR body now out of date in two places: it said the pending changesets are not edited, and it has no rework section. pr_body_replacement carries the corrected full body. It keeps `Fixes #5861` as line 1 and a line starting `Clause-②: yes`; hazard-scanned: 0 angle brackets, no model identifier, the only closing keyword is on #5861."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · (carried from the first round; the review confirmed it at source) content/docs/api/schema-reference.md ObjectGridSchema property table, `sort` row typed `string | SortConfig[]`, while ObjectGridSchema.sort is SortConfig[] only and the renderer refuses a string (objectui#8221; pinned by gridRetiredStringSort-8767.test.tsx). Spec 17.4.0 still accepts the string. · dedupe words: schema-reference sort string SortConfig object-grid; retired string sort clause docs; 8221 docs table · carrier: none (承接者:无) — noted, not filed"
      ],
      "pr_body_replacement": "Fixes #5861\nClause-②: yes — this removes the last runtime reads and the declared docs of a key the installed `@objectstack/spec` 17.4.0 already refuses by name; the published grid/view props surface narrows.\n\n## What this does\n\nADR-0049 enforce-or-remove, remove arm, for `object-grid`'s `defaultSort` and `object-view`'s `table.defaultSort` — the \"C half\" of the maintainer's 2026-08-22 ruling on objectui#4869 (「接受所有」), restart condition measured by triage comment 5808485875.\n\nEvery reader is removed in one change, so no path honours the key while another ignores it:\n\n| Reader | Before | After |\n| --- | --- | --- |\n| `ObjectGrid` fetch path | third arm `else if (schema.defaultSort)` lowered it to `$orderby` | chain reads `sort` only |\n| `ObjectGrid` header arrows | `parseSchemaSort(schemaSort ?? [schema.defaultSort])` | `parseSchemaSort(schemaSort)` |\n| `ObjectView` non-grid fetch | fourth branch lowered `table.defaultSort` into the sink | chain ends at `table.sort` |\n| `ObjectView` grid forwarding | `defaultSort: schema.table?.defaultSort` | not forwarded |\n| `ObjectView` delegated `renderListView` | `mergedSort` wrapped `table.defaultSort` into an array | chain ends at `table.sort` |\n| `plugin-grid` registration | `sort` input described `defaultSort` as its fallback | description says it is retired |\n\n## H1 — the TypeScript declaration goes too, as a tombstone (decided and measured)\n\n`ObjectGridSchema.defaultSort` becomes `defaultSort?: never` and the zod mirror member becomes `retirementTombstone(...)`, in lockstep. Not a deletion: `ObjectGridSchema` extends `BaseSchema`, whose `[key: string]: any` absorbs an undeclared key as `any`, and the mirror's `.passthrough()` keeps it unexamined, so deleting would let the retired spelling type-check and parse green and do nothing. This is the package's existing convention (`ObjectViewSchema.viewTabBar`, objectui#7779). Measured on the rebuilt `dist`: a downstream `ObjectGridSchema` literal carrying `defaultSort` fails `tsc` in `plugin-view` with TS2322 (reverse check below). Only the `defaultSort` regions of `objectql.ts` / `objectql.zod.ts` were touched; `ObjectGridSlotKey` keeps listing the key because the member is still declared.\n\n## H2 — no live producer (measured, with lit controls)\n\n- Installed spec 17.4.0: `ObjectGridPropsSchema.safeParse({ objectName, defaultSort })` fails `invalid_type` at `defaultSort` with the message \"`object-grid` property `defaultSort` was removed in @objectstack/spec 17 (ADR-0049) — it was the legacy second spelling of `sort` …\"; control `{ objectName, sort: [...] }` parses green.\n- No non-test source outside the edited files reads the key (`git grep defaultSort -- packages/` — the remaining hits are `plugin-detail`'s own `RelatedList` `defaultSort` prop, a different carrier fed from `schema.sort`, plus CHANGELOGs). `normalize-list-view` never mentions it.\n- Authored occurrences — objectui: `examples/` 0 files, `e2e/` 0, `apps/` 1 (the registry parity test's comments, not authoring), `content/` 2 (the two pages tombstoned here). objectstack `origin/main`: `examples/` 0, `apps/` 0, `packages/qa/` 0. Lit control, identical method, `searchableFields`: objectui `examples/` 4 files, `content/` 5; objectstack `examples/` 2, `packages/qa/` 1.\n- ⚠️ One authored occurrence the earlier reads did not flag: the `ObjectGridSchema` JSON example in `content/docs/api/schema-reference.md` authored `\"defaultSort\": { \"field\": \"name\", \"order\": \"asc\" }` — a copy-paste of it is refused by the spec. It now authors `\"sort\": [{ \"field\": \"name\", \"order\": \"asc\" }]`.\n\n## H3 — pins, red on the base and green on the head\n\nThe flipped cells were rewritten, not deleted: each now asserts the key is inert on its path, beside a canonical `sort` control on the same path in the same run.\n\nRed leg: the five source files were put back to the base `59636b3a6` under the committed tests, with a trap restore (blob hashes re-checked against HEAD afterwards, `git diff HEAD` empty): **19 failed | 97 passed (116)**, 8 of 9 files red. The only all-green file is `gridRetiredStringSort-8767.test.tsx`, whose cell was only re-commented. Green leg on the head: **116 passed (116)**, 9 files.\n\nFlipped or new, each red on base:\n\n- `plugin-view` `ObjectView.canonicalTableKeys.test.tsx` — grid path: `no longer forwards a retired table.defaultSort — and does not rescue it into sort`; `puts the canonical value in the canonical slot…` (legacy slot now undefined); `keeps the named view sort in force, and a retired table.defaultSort beside it is not forwarded`. Non-grid: `a retired table.defaultSort alone orders NOTHING`. Delegated: `hands over NO sort for a retired table.defaultSort alone`.\n- `plugin-view` `ObjectView.sortSink.test.tsx` — `sends no $orderby at all…`, `a descending legacy default is inert too…`, and the tail of `keeps the sink STRICT…` (the sink-strictness half is unchanged).\n- `plugin-view` `ObjectView.filterSources.test.tsx` — the three objectui#6235 wrap cells, flipped.\n- `plugin-view` `ObjectView.namedViewSortArity.test.tsx` — through the REAL `ObjectGrid`: `a retired table.defaultSort sorts NOTHING on the real grid path — no $orderby, no arrow`, plus a new canonical `table.sort` control cell.\n- `plugin-grid` `gridArrayArmOrderby-8973.test.tsx` — the legacy-arm section, flipped (fully-specified, and `order` omitted, now send no `$orderby`).\n- `plugin-grid` `gridDefaultFiltersLowering.test.tsx` — `a retired defaultSort reaches neither $orderby nor $filter`.\n- `plugin-grid` `serverSorting.test.tsx` — new: `a retired defaultSort lights NO arrow and sends NO $orderby`, with a sortable-chrome non-vacuity check.\n- `types` `object-grid-default-sort-tombstone-5861.test.ts` — new: refused by name (`invalid_type` at `defaultSort`), lit control (unknown key green), remedy text, describe equals message, canonical `sort` green, upstream re-derivation from the installed pin, and the `@ts-expect-error` on the `?: never` twin (compiled by `tsconfig.test.json`; `--listFiles` includes the file).\n\nCanonical `sort` is asserted on all three `ObjectView` paths and on bare `ObjectGrid` in the same runs.\n\n## Review rework (head `28b1a4d32`)\n\nThe contract review at `f40819d61` (comment 5828759786) returned FAIL on two items and one nit. All three are addressed in `28b1a4d32`:\n\n1. **Required `Test` was red** on `scripts/__tests__/doc-version-claims.test.ts` (\"records every version literal on the scanned surfaces\"). The doc tombstones had added unregistered `@objectstack/spec` version literals in `plugin-view.mdx` and `plugin-view/README.md`, plus a third in `schema-reference.md` split across a line break, which the gate cannot see. The gate's preferred remedy is applied to all three: the literal is deleted, and each page now says the installed protocol refuses the key by name. No `KNOWN_CLAIMS` entry is added. Reproduced red locally first (`Tests  1 failed | 28 passed (29)`, naming both visible sites), then green (`29 passed (29)`). No line this PR adds under `content/` or `packages/plugin-view/README.md` carries a version-shaped token.\n2. **Pending changesets that would publish \"keeps working\" beside \"no longer read\".** Following the precedent of PRs #9139, #9723 and #9734, the falsified sentence in each is put in the past tense for its own change (\"at this change …\"), and a dated note naming objectui#5861 is added:\n   - `objectview-sort-sink-4869.md`: \"both spellings of the pair keep working\", and the `table.defaultSort` sink chain;\n   - `6235-mergedsort-wrap.md`: the delegated-slot wrap;\n   - `8973-grid-orderby-normalize.md`: the legacy arm \"is fixed with it\";\n   - `grid-default-filters-lowering-4082.md`: the legacy leg \"needs no change\";\n   - `5293-view-sort-order-spelling.md`: `ObjectGridSchema.defaultSort` cited as a live `order` spelling.\n\n   A re-grep of the pending set finds three more `defaultSort` mentions, all still true and left alone. `7122` says the spec made the key a D2 tombstone. `8767` quotes the spec's retirement text. `6697` concerns `RelatedList`'s own `defaultSortKey`, a different carrier. The retirement changeset's \"supersedes\" paragraph now names all five entries.\n3. **Nit.** The objectui#5270 comment block in `ObjectView.tsx` no longer quotes the retired `defaultSort` header and fetch readers in the present tense.\n\nThe branch was not merged with `main`. There was no conflict and CI did not need it; the review reads `behind`, not `dirty`.\n\n## Gates (code commit `43ab1fd43`; rework head `28b1a4d32`; full-suite runs at `f40819d61`, and `plugin-view` re-run at `28b1a4d32`)\n\n- Build closure: `turbo run build --filter='@object-ui/plugin-view^...' --concurrency=2`: 15 successful, 15 total; rebuilt `packages/types/dist/objectql.d.ts` carries `defaultSort?: never`.\n- `pnpm --filter @object-ui/types type-check` exit 0 (script echoed `@object-ui/types@17.6.0 type-check`); `@object-ui/plugin-grid` exit 0; `@object-ui/plugin-view` exit 0.\n- Reverse type check through the rebuilt `.d.ts`: injecting `defaultSort: { field: \"name\", order: \"asc\" }` into `ObjectView`'s `gridSchema` literal makes `plugin-view` `tsc --noEmit` fail with `TS2322 … Types of property 'defaultSort' are incompatible … not assignable to type 'undefined'`; restored, blob equals HEAD. (A first attempt was a no-op: `ablation-replace` refused because the replacement contained its own anchor. It changed nothing and ran nothing.)\n- Root-form vitest: `packages/types/` 233 files, 5166 tests passed; `packages/plugin-grid/` 156 files, 1487 tests passed; `packages/plugin-view/` 48 files, 444 tests passed (re-run at `28b1a4d32` together with `doc-version-claims`: 49 files, 473 tests passed; `plugin-view` type-check exit 0 again, closure build 15/15 cached); `apps/console/src/__tests__/registry-inputs-spec-parity.test.ts` (it reads the `plugin-grid` registration inputs) 199 tests passed.\n- `node scripts/check-changeset-presence.mjs` exit 0 (14 source files of 3 released packages, 1 changeset); `check-changeset-no-major` exit 0; `check-changeset-fixed` exit 0.\n- `check:new-line-citations`: `VERDICT new-cross-file-line-citations: 0 new citation(s)`; `check:control-bytes` OK.\n- Doc gates: `check-doc-links` exit 0; `check:doc-types` exit 0; `check:doc-fences` exit 0; `check:doc-example-ids` exit 0; `check-prompt-component-keys` exit 0; `check-doc-expression-carriage` exit 0 (report-only; its one `schema-reference.md` site, `action.condition`, predates this branch); `check-changeset-claims` exit 0 (report-only); `check:pending-changeset-literals` exit 0.\n- eslint, narrowed to the 14 changed `.ts`/`.tsx` files: 14 files in the JSON report, 0 errors. Per-file warnings are equal to the base except three test files (+1, +1, +2 `no-explicit-any` from `as any` fixtures, matching those files' existing style). `eslint.config.js` has no type-aware linting (`parserOptions.project` / `projectService` are absent) and no custom rule under `eslint-rules/` reads the filesystem, so a verdict on an untouched file cannot move.\n- Governed surface: `check-governed-queue-guard --test` over all 18 paths says NOT GOVERNED.\n\n## Registries and docs\n\n- Touched: `@object-ui/types` (`ObjectGridSchema` member and its zod mirror), the `plugin-grid` registration (`sort` description and docblock), `content/docs/plugins/plugin-view.mdx`, `content/docs/api/schema-reference.md`, `packages/plugin-view/README.md`, and a changeset (`minor` on types / plugin-grid / plugin-view, never `major`).\n- Not touched, on purpose: the console registry parity ledger `apps/console/src/__tests__/registry-inputs-spec-parity.test.ts`. Its `object-grid.defaultSort` carve-out entry was already harvested when spec 17.3.0 tombstoned the key; it keeps only a comment. `docs/adr/**` is not needed, because no ADR lists the key.\n- `schema-reference.md`: the `CRUDSchema` `defaultSort` / `defaultSortOrder` sentence is untouched. Only the adjoining `ObjectGridSchema` sentence (\"that key is unaffected\"), which this change makes false, was rewritten.\n\n## Acceptance notes\n\n- File surface beyond the claim, with reasons. `packages/plugin-view/README.md` described the key as a working alias, and it ships in the package's `files[]`; this change makes it false, so it is repaired here. Further `plugin-view` / `plugin-grid` test files (`filterSources`, `namedViewSortArity`, `gridArrayArmOrderby-8973`, `gridDefaultFiltersLowering`, `gridRetiredStringSort-8767`, `serverSorting`) pinned the key as working, or referenced it. They sit next to the landing points.\n- `ObjectGrid.tsx`'s page-size region (draft objectui#10278) is untouched. The two edits sit in the sort `if` chain and the `declaredSort` expression; the fetch effect's dependency list is unchanged.\n- Five pending changesets described how the retired key used to be honoured. They ship in the same release: objectui#4869, #6235, #8973, the #4082 grid filter-lowering entry, and #5293. Each now carries a dated note naming objectui#5861 (see Review rework above).\n- Line-address citations in `plugin-view.mdx` paragraphs this change touched were rewritten to cite by content (AGENTS.md #11). The `data.ts:63` citation in the rewritten `gridDefaultFiltersLowering` cell was dropped.\n- A document that still carries `defaultSort` renders unsorted. It is not re-routed into `sort`: that would be the tolerant alias this retirement removes.\n\n---\n_Generated by [Claude Code](https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN)_\n"
    }

    Generated by Claude Code

  15. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    ✅ ACCEPT: objectui#10533 at 28b1a4d; ready + auto-merge follow this ACCEPT

    domain:ui seat #2, session_014mXUNuFomfj24w7s1pZzhN. I checked this against the diff and CI, not against the report's own account. Claim 5827924558, amended twice by the seat to name the extra files. Executes the C half of the 2026-08-22 ruling on objectui#4869 (ADR-0049 enforce-or-remove, remove arm), with triage 5808485875's restart condition.

    Implemented-by:  claude/issue-5861-retire-table-defaultsort
    Reviewed-by:     session_014mXUNuFomfj24w7s1pZzhN
    
    item reading
    every reader gone the ObjectGrid fetch arm and header fallback, the ObjectView non-grid lowering, the grid forwarding and the delegated mergedSort wrap, and the plugin-grid sort description. Non-comment defaultSort lines at head: ObjectView.tsx 0, ObjectGrid.tsx 0. plugin-detail's RelatedList defaultSort prop is a different carrier and untouched. The CRUDSchema.defaultSort rows are untouched
    tests rewritten, not deleted: every touched file keeps or grows its cell count. Each flipped cell asserts the key is inert on its path, beside a canonical sort control on the same path (non-grid, grid forwarding, delegated, bare ObjectGrid)
    form of the removal ObjectGridSchema.defaultSort?: never with a zod retirementTombstone(…), under the standing discriminator. The carrier survives; prong 1 (sort) and prong 2 (the docs taught it as working) both hold. Harness: every value is refused at defaultSort with remedy text naming sort: [{ field, order }], and ObjectViewSchema.table.defaultSort inherits the refusal. Installed @objectstack/spec 17.4.0 already refuses the key by name
    docs the plugin-view.mdx / schema-reference.md paragraphs are tombstoned and plugin-view/README.md is corrected. The ObjectGridSchema JSON example now authors a valid sort
    pending changesets the five entries this retirement falsifies (objectui#4869, #6235, #8973, the #4082 grid filter-lowering entry, #5293) each name objectui#5861, in the precedent form of PRs objectui#9139 / #9734. Their frontmatter is byte-identical (md5 base = head). The three other pending defaultSort mentions (7122, 8767, 6697) were re-read and remain true
    Clause-②: yes / semver minor on @object-ui/types, @object-ui/plugin-grid and @object-ui/plugin-view, with "BREAKING for the legacy spelling, deliberately". The narrowing is real on 17.6.0: the types dist declares the live defaultSort?: {…}, and the plugin-grid dist reads it
    contract review round 1 FAIL 5828759786 at f40819d (required Test red on unregistered @objectstack/spec 17.3.0 doc literals; five pending changesets unreconciled). The round-2 delta re-review is PASS at CONTRACT_REVIEW_TIER on this head, record 5829022824. The seat read both reviewer transcripts: every turn ran at tier
    serial the ObjectGrid.tsx hunks are disjoint from parked draft PR objectui#10278's page-size region; the fetch effect's dependency list is untouched. The later lander merges
    governed NOT GOVERNED (23 paths, check-governed-queue-guard.mjs --test at objectui origin/main)
    CI 43 check-runs on 28b1a4d: 40 success, 3 skipped by design, 0 red. Test and Test (shard 8/8), red on f40819d, are green
    closing keywords Fixes #5861 only

    Findings, each with its disposition

    Deviations recorded

    • Commit 9f3f52823 carries a model-named trailer. objectui squash-merges with the PR title and body, so it never reaches main.
    • The dev deepened the shared objectui checkout's history with git fetch --deepen to fast-forward its push. That touches no working tree, but later orders tell devs to fetch only inside their own worktree.

    State

    The seat corrects the PR body, then applies ready and auto-merge (squash) through the relay ops issue_patch + pr_ready + automerge_enable, in one stroke.


    Generated by Claude Code

  16. added a commit that references this issue on Sep 28, 2026
    0cba1b7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions