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-manageros-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 #8704relatedListFilter 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

…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.

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

Labels

documentationImprovements or additions to documentationprotocol:datasize/steststooling

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

@os-project-manager@hotlong@claude