Skip to content

docs(spec): pin the derived related-list sort-inheritance rule in relatedList's describe - #13310

Merged
hotlong merged 3 commits into
mainfrom
claude/issue-13294-relatedlist-sort-pin
Aug 30, 2026
Merged

docs(spec): pin the derived related-list sort-inheritance rule in relatedList's describe#13310
hotlong merged 3 commits into
mainfrom
claude/issue-13294-relatedlist-sort-pin

Conversation

@os-project-manager

@os-project-manager os-project-manager commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Fixes #13294

What

Pins the derived related-list sort-inheritance rule as contract text on relatedList's .describe(), the same way relatedListFilter was pinned by #8704. The behaviour is already landed and browser-verified — #11345's 2026-08-29 acceptance run — this PR only makes the rule readable in the spec's own contract text instead of living solely in comments/PR history.

The pinned text states:

  • Inheritance source: a derived related list (relatedList: 'primary') inherits its row order from the child object's DEFAULT list view sort — the isDefault expanded view, or the first declared list item when none is marked default (resolver expandViewContainer, ui/view.zod.ts).
  • Wire spelling: the REST shorthand sort=FIELD / sort=-FIELD (objectui's serializeOrderBy lowers QueryParams.$orderby to this on the wire) — never the OData $orderby token.
  • Negative half: a child object with no list-view sort emits no ordering parameter at all, and rows fall back to record-id order.

Text-only change — the relatedList accept-set (z.union([z.boolean(), z.literal('primary')]).optional()) is byte-identical before and after; only the .describe() string and the JSDoc block above it grew.

Describe text — before / after

Before:

Show this child collection as a related list on the parent's detail page (read-side mirror of inlineEdit). false = suppress; true/absent = shown (stacked under the shared "Related" tab); 'primary' = core relationship, promoted to its own tab. Prominence intent, not a layout switch (ADR-0085).

After (appended sentence, following the #8704 relatedListFilter pattern):

… Prominence intent, not a layout switch (ADR-0085). A derived related list (relatedList: 'primary') inherits its row order from the child object's DEFAULT list view sort — the isDefault view, or the first declared list item when none is marked default; wire spelling is sort=FIELD / sort=-FIELD, never $orderby. A child object with no list-view sort emits no ordering parameter, and rows fall back to record-id order.

Pin test

Added two tests in packages/spec/src/data/field.test.ts (immediately before the relatedListFilter family, same structural spot the #8704 precedent sits in), asserting both halves of the contract text — a one-directional assertion would leave the other half free to drift silently:

  • relatedList contract text states DEFAULT-list-view sort inheritance and its wire spelling
  • relatedList contract text states the negative half: no list-view sort ⇒ no ordering parameter

Regenerated reference pages

pnpm --filter @objectstack/spec check:generated reported content/docs/references/** stale after the describe edit (1 of 14 artifacts); regenerated with pnpm --filter @objectstack/spec gen:docs (the only stale one — never the whole set). Three pages embed the Field schema table and all three moved identically (same one-line describe update, no unrelated drift): content/docs/references/data/field.mdx, content/docs/references/data/object.mdx, content/docs/references/system/migration.mdx. Re-ran check:generated after — all 14 generated artifacts up to date.

Changeset

.changeset/relatedlist-sort-inheritance-pin.md@objectstack/spec: patch (text face, per the card's landing note).

Tests

At commit 3865d2c7 (this branch's HEAD):

  • pnpm --filter @objectstack/spec build — clean, all 34 declared .d.ts present.
  • pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/data/field.test.ts203 passed (203), including the 2 new pin tests.
  • pnpm --filter @objectstack/spec check:generatedall 14 generated artifacts up to date (after gen:docs).
  • pnpm --filter @objectstack/spec typecheck — clean (tsc --noEmit, check:scripts-typecheck, check:test-typecheck all pass; test-typecheck debt ledger unchanged at 54 files / 262 errors).
  • pnpm check:nul-bytes — OK (7360 text files scanned, no raw ASCII control bytes).

Clause-② / draft status

Path limb fires (packages/spec/src/**) even though the content limb doesn't (pure describe prose, no accept/reject change) — per the card's own landing note, this PR stays draft with needs:contract-review on both the PR and the card; the contract-review chain owns enqueue, not me.

Refs: #11345 (verified behaviour + close condition) · #8704 (the pin pattern) · objectui#6088 (the implementation)

Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N

claude added 3 commits August 30, 2026 02:44
…atedList's describe

Pin the behaviour landed and browser-verified in #11345 as contract text on
relatedList's .describe(): a derived related list (relatedList: 'primary')
inherits the child object's DEFAULT list view sort (isDefault expanded view,
or the first declared list item when none is default; resolver
expandViewContainer, ui/view.zod.ts). Wire spelling is the REST shorthand
sort=<field> / sort=-<field>, never the OData $orderby token. A child object
with no list-view sort emits no ordering parameter, falling back to
record-id order.

Text-only change -- the relatedList accept-set is byte-identical before and
after. Adds a pin test (following the #8704 relatedListFilter precedent)
asserting both the inheritance statement and the negative (no-sort =>
no-ordering-parameter) half of the contract text.
…hange

pnpm --filter @objectstack/spec check:generated proved content/docs/references/**
stale after the describe edit; regenerated via pnpm --filter @objectstack/spec
gen:docs (the only artifact it reported stale). All 14 generated artifacts are
up to date after this commit.
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 1 documentable anchor(s).

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

  • content/docs/concepts/metadata-driven.mdx (via FieldSchema (symbol))
  • content/docs/data-modeling/external-datasources.mdx (via FieldSchema (symbol))
  • content/docs/data-modeling/field-types.mdx (via FieldSchema (symbol))
  • content/docs/data-modeling/validation-rules.mdx (via FieldSchema (symbol))
  • content/docs/deployment/troubleshooting.mdx (via FieldSchema (symbol))
  • content/docs/deployment/validating-metadata.mdx (via FieldSchema (symbol))
  • content/docs/getting-started/quick-reference.mdx (via FieldSchema (symbol))
  • content/docs/kernel/contracts/data-engine.mdx (via FieldSchema (symbol))
  • content/docs/protocol/backward-compatibility.mdx (via FieldSchema (symbol))
  • content/docs/protocol/objectql/types.mdx (via FieldSchema (symbol))
  • content/docs/protocol/objectui/concept.mdx (via FieldSchema (symbol))

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

  • content/docs/releases/v17.mdx (via FieldSchema (symbol))

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

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

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

Which tree this was computed on

This run read content/docs from 02571744269145b2e9de57703ceda65347a40388 — the merge of head 3865d2c7f977552d60fff153a52114e014260ef2 into base 8f743076d47638fa29b86cafa772912b67a8d9b6, which is what actions/checkout gives a pull_request run. Not the PR head.

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

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 02571744269145b2e9de57703ceda65347a40388 && git checkout 02571744269145b2e9de57703ceda65347a40388
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8f743076d47638fa29b86cafa772912b67a8d9b6 3865d2c7f977552d60fff153a52114e014260ef2 && git checkout -B drift-repro 8f743076d47638fa29b86cafa772912b67a8d9b6 && git merge --no-ff 3865d2c7f977552d60fff153a52114e014260ef2

node scripts/docs-audit/affected-docs.mjs --json 8f743076d47638fa29b86cafa772912b67a8d9b6

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

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

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling labels Aug 30, 2026
@hotlong
hotlong marked this pull request as ready for review August 30, 2026 05:02
@hotlong
hotlong added this pull request to the merge queue Aug 30, 2026
Merged via the queue into main with commit 74a7804 Aug 30, 2026
44 checks passed
@hotlong
hotlong deleted the claude/issue-13294-relatedlist-sort-pin branch August 30, 2026 05:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:data size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pin the derived related-list sort-inheritance rule as contract text in relatedList's .describe() — the #8704 pattern

3 participants