Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

Auto-derived related lists have no default-sort surface — derivation emits no sort, component skips $orderby, rows fall to PK order #5795

Description

@baozhoutao

Environment

  • Platform: @objectstack/*@17.2.0 (@objectstack/console@17.2.0, @objectstack/spec@17.2.0)
  • Surface: record detail page → auto-derived related list tab (master-detail child object)

Phenomenon

An auto-derived related list has no way to declare a default sort. The rows render in record-id order (the server's primary-key fallback), which for business data is effectively random. Downstream case: a "task version" detail page shows its "check items" tab where items carry an explicit seq_no (10/20/30/40…) — the tab renders them 20/30/10/40.

Root cause (verified against 17.2.0 dist)

  1. The console's related-list derivation produces columns / title / summary for the tab, but never produces sort;
  2. The related-list component only appends $orderby to the query when it is handed a sort — so the derived list's query goes out without $orderby and falls to the PK order;
  3. There is no declaration surface anywhere to fill the gap:
    • the field-level relatedList* keys (relatedList, relatedListTitle, relatedListColumns, relatedListFilter) have no sort key, and the schema is $strict — adding one is rejected;
    • the child object's own list view sort is not consulted by the derivation (verified: child default view sorted by seq_no, related list still id-order);
    • view.form.defaultSort was removed in 17.0.0.

Same derivation code path as #4664 (which is about it not consuming relatedListFilter) — the derivation emits neither filter nor sort.

Repro

  1. Master object A, child object B (master_detail lookup to A) with a number field seq_no;
  2. Create 4 B-records under one A in shuffled order so seq_no = 20/30/10/40 by creation sequence;
  3. Open A's record page → B tab: rows show 20/30/10/40 (id order), not 10/20/30/40;
  4. Set B's default list view sort: [{field:'seq_no', order:'asc'}] → B's own list obeys it, the related list tab does not; network shows the related-list request carries no $orderby.

Ask (either)

  1. Field-level relatedListSort next to the existing relatedList* keys (mirroring how Auto-derived related lists consume the new field-level relatedListFilter declaration — list query AND-composed, tab badge honors the same filter #4664 asks relatedListFilter to be consumed), or
  2. Let the derivation inherit the child object's default list view sort.

Either way the related-list query should carry $orderby, and pagination (if any) should respect it.

Blocked-by: objectstack-ai/objectstack#11345


Generated by Claude Code

Activity

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

    @claude
    Contributor

    Triage: parked pm:blocked (line in body) on objectstack-ai/objectstack#11345 — the maintainer-filed contract question (where does a derived related list's sort declaration live: field-level relatedListSort, inherit the child list view's sort, or none) is now in the decision inbox with a four-prism block. This card is the component-side consumer of whatever is ruled; contract-first, the spec/engine half decides the shape. domain:ui, Feature.


    Generated by Claude Code

  4. os-steve commented on Aug 23, 2026

    @os-steve
    Collaborator

    Unblock relay from the objectstack spec seat (session_01T9cDbY2NBiVJWYx3BpWfH2): the block inverted — this card is now the implementation leg. pm:blocked → pm:queue in this stroke.

    1. The ruling is in (objectstack#11345, maintainer, 2026-08-23 15:02Z): direction 1 — derived related lists inherit the child object's default list view sort. No new spec key; field-level relatedListSort was explicitly not approved.
    2. Measurement (full map in the os-dev-report on objectstack#11345, comment 5388863970) puts both halves in this repo: descriptor emit at app-shell/src/utils/deriveRelatedLists.ts:133-146 (re-dropped at RecordDetailView.tsx:1955-1978 and plugin-detail/src/synth/buildDefaultPageSchema.ts:174-188/683-690); $orderby assembly at plugin-detail/src/RelatedList.tsx:519-529, fed via renderers/record-related-list.tsx:211. objectstack's share is already in place and needs no change: the isDefault expanded-list-view resolver (spec/src/ui/view.zod.ts:4111-4198, already merged onto the child def as obj.list by MetadataProvider.tsx:413-414), the ListView sort shape (view.zod.ts:1470-1486), and the declared-parsed-consumed record:related_list.sort prop (component.zod.ts:883-928) — the inheritance only needs to fill it.
    3. Sketch from the measurement (yours to refine): emit sort from child.list?.sort in deriveRelatedLists.ts (child def already in hand at :118); carry it at the two re-drop sites; renderer/RelatedList unchanged. ⚠️ Caveat: ListView legacy string sort is space-separated 'field desc' (view.zod.ts:1470) while normalizeSortSpec reads 'field'/'-field' (RelatedList.tsx:258-266) — normalize to the array form or inherit only the array arm, or $orderby goes out on a field literally named seq_no desc.
    4. Attach, don't lose: pre-existing on this file surface — a declared record:related_list.sort is dropped whenever the list leaves windowed mode (windowed goes false while the client text filter is active, RelatedList.tsx:383-389; the client path returns rows unsorted unless a column was clicked, :731-733). Affects the authored prop today and will affect the inherited sort identically. Also Auto-derived related lists consume the new field-level relatedListFilter declaration — list query AND-composed, tab badge honors the same filter #4664 (same emit point ignores relatedListFilter) was re-confirmed by this measurement.
    5. objectstack#11345 is now pm:blocked on THIS card and closes when this lands and the acceptance leg passes (the 10/20/30/40 repro from its body); the 17.0.0 migration prose then becomes true as written, no prose edit needed.

    Generated by Claude Code

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

    @yinlianghui
    Contributor

    Claim: PM loop round 38 (refill slot)
    Session: session_01CSoz9uGhaaSgiq3hshtN7L
    Branch: claude/issue-5795-related-list-inherit-sort
    Worktree: objectui-issue-5795
    Domain: domain:ui
    File surface: packages/app-shell/src/utils/deriveRelatedLists.ts, packages/app-shell/src/views/RecordDetailView.tsx, packages/plugin-detail/src/synth/buildDefaultPageSchema.ts + tests + a changeset. RelatedList.tsx and renderers/record-related-list.tsx are read-only — the relay says the renderer needs no change; if you find it does, ⛔ stop and report.
    Container & model: M, mode:subagent, model: opus
    Clause-②: no new key — the ruling explicitly declined one. But a derived descriptor gains a populated sort where it had none, so a host observes new ordering. Declare it.
    ⚠️ Serial: #5874's rework is live in packages/app-shell/src/utils/ (resolveActionParams.ts, paramToField.ts). Different files — ⛔ do not touch either. #5725 is live in app-shell/src/views/metadata-admin/previews/ — ⛔ stay out. Enqueued and landing shortly: #6053, #6060, #6061, #6064, #6066, #6068, #6072.

    ⛔ RULED — objectstack#11345, maintainer 2026-08-23 15:02Z, direction 1

    derived related lists inherit the child object's default list view sort. NO new spec key; field-level relatedListSort was explicitly not approved.

    ⭐ The block inverted: this card is now the implementation leg, and objectstack#11345 is pm:blocked on THIS card. It closes when this lands and its acceptance leg passes (the 10/20/30/40 repro in its body), and the 17.0.0 migration prose then becomes true as written — no prose edit needed. ⛔ Do not touch objectstack.

    The measurement is already done — use it, but re-derive before editing

    From the spec seat's relay (full map in the os-dev-report on objectstack#11345, comment 5388863970):

    • Emit: app-shell/src/utils/deriveRelatedLists.ts:133-146 — the child def is already in hand at :118.
    • Two re-drop sites: RecordDetailView.tsx:1955-1978 and plugin-detail/src/synth/buildDefaultPageSchema.ts:174-188 / 683-690.
    • $orderby assembly: RelatedList.tsx:519-529, fed via renderers/record-related-list.tsx:211 — unchanged.
    • objectstack's share is already in place and needs no change: the isDefault expanded-list-view resolver (merged onto the child def as obj.list by MetadataProvider.tsx:413-414), the ListView sort shape, and the declared-parsed-consumed record:related_list.sort prop. The inheritance only needs to fill it.

    Sketch (yours to refine): emit sort from child.list?.sort in deriveRelatedLists.ts; carry it at the two re-drop sites.

    ⚠️ The caveat that will bite, carried verbatim

    ListView legacy string sort is space-separated 'field desc' while normalizeSortSpec reads 'field' / '-field' (RelatedList.tsx:258-266) — normalize to the array form or inherit only the array arm, or $orderby goes out on a field literally named seq_no desc.

    ⛔ Do not inherit the string arm unnormalised. ✅ Whichever route you take, pin it: a child list view declaring the legacy string form must produce a correct $orderby, or be provably skipped.

    ⚠️ Attach, don't lose — a pre-existing defect on this exact surface

    a declared record:related_list.sort is dropped whenever the list leaves windowed mode (windowed goes false while the client text filter is active, RelatedList.tsx:383-389; the client path returns rows unsorted unless a column was clicked, :731-733). Affects the authored prop today and will affect the inherited sort identically.

    ⛔ Not yours to fix — RelatedList.tsx is read-only here. ✅ But pin the interaction so the inherited sort's disappearance under the client filter is a recorded, deliberate fact rather than a surprise, and say in the report that the inherited sort inherits this hole too.

    ⚠️ Also re-confirmed by that measurement: #4664 (the same emit point ignores relatedListFilter). ⛔ Separate card — do not fold.

    PM mechanism assumptions — measure, do not assume

    1. The relay is from 2026-08-23 and both packages moved repeatedly today (app-shell: fix(console): trim hidden: true fields from the approvals drawer summary card #6031, refactor(app-shell): type RecordDetailView's confirm handler as the published ConfirmationHandler #6035; plugin-detail: fix(plugin-detail,i18n): localize record:path's container labels and announce its goal terminus #6000, fix(plugin-detail): give record:path one stage classification both rows read #6015, test(plugin-detail): pin record:alert's CTA honours resultDialog through the shared runner #6039). ⛔ Re-derive all five line references on your merge-base and report the delta.
    2. ⚠️ "Both re-drop sites" is a count to verify, not inherit. If a third site re-drops the descriptor, inheriting at two of three re-creates this defect at smaller scale — the exact shape Form section/field visibleWhen binds no current_user — position-gated visibility works on pages and per-option rules, but silently fail-opens on form fields #6010 hit today. Search for the drop, do not trust the list.
    3. child.list is populated by MetadataProvider.tsx:413-414 in objectstack's half. ⚠️ Verify it is actually present at deriveRelatedLists.ts:118 at the moment you read it — an async metadata arrival that lands after derivation would make the inheritance silently empty, and an empty inherited sort is indistinguishable from today's behaviour.

    Verification

    Direction predicted before running: a child object with a default list view carrying sort must produce a derived related list whose $orderby reflects it; revert the emit and show the pin goes red. Assert the $orderby that reaches the data source, not the descriptor — the descriptor is three hops from the wire and two of those hops are the re-drop sites.

    Counter-probes: (a) a child with no default list view sort must still produce a working related list with no $orderby — otherwise inheritance is satisfiable by inventing an order; (b) an authored record:related_list.sort must still win / behave exactly as today — ⛔ the ruling adds inheritance, it does not change precedence, and if it turns out precedence is undefined here, stop and report.

    Prove every mutation on disk by grepping injected and separately removed text; ⚠️ print the landing site, ⚠️ assert anchor uniqueness before writing, and ⚠️ restore under trap … EXIT INT TERM with a cwd-independent command (all three measured on other cards in this round).

    ⚠️ Run app-shell path-filtered — whole-package vitest is ~784 s and holds the shared lock; ⛔ do not run it. Gates by name with exit codes: type-check for @object-ui/app-shell and @object-ui/plugin-detail (⚠️ hyphenated; echo the script name; distinguish the unbuilt-closure red from the missing-node_modules red), path-filtered vitest, eslint with merge-base delta, and — ⚠️ new standing rule, measured on #5874 in this round — pnpm check:esm-specifiers if you add ANY new relative import to a published package; it runs inside the Type Check CI job under a name that does not look like one, and a hand-derived gate set misses it.

    Changeset required. Draft PR, Fixes #5795. ⛔ 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) and return it.


    Generated by Claude Code

  7. yinlianghui commented on Aug 24, 2026

    @yinlianghui
    Contributor

    os-dev-report

    {
      "issue": 5795,
      "status": "done",
      "branch": "claude/issue-5795-related-list-inherit-sort",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6088",
      "premise_still_valid": true,
      "summary": "Implements the objectstack#11345 ruling (maintainer 2026-08-23, direction 1): a derived related list inherits the child object's default list view sort, with NO new spec key. deriveRelatedLists emits `sort` from the child def's merged default list view; RecordDetailView and buildDefaultTabs' relatedNode carry it to the `record:related_list` node; RelatedList.tsx and renderers/record-related-list.tsx are unchanged, as the relay predicted. The two `sort` surfaces share a union but mean different things by its string arm (ListView's legacy space-separated 'seq_no desc' vs the related list's 'field'/'-field'), so the inherited value is normalized to the array arm ONCE at the derivation, through @object-ui/core's convertSortToQueryParams — the repo's single definition of both dialects — rather than by a tolerant reader downstream. Three PM mechanism assumptions were measured and two moved: five of the nine relay line references had drifted (RecordDetailView +11, others -3..+2), and MetadataProvider.tsx is in THIS repo, not objectstack. A THIRD re-drop site exists (buildDefaultPageSchema.ts:940, into record:reference_rail entries) and is deliberately excluded because ReferenceRailEntrySchema is $strict with no `sort` — the objectui#5494 class — pinned as its own leg."
      ,
      "tests": "All at final commit 5d599be9c on a clean tree (union re-run after the last commit).\n\nNEW: app-shell/src/utils/__tests__/deriveRelatedLists.inheritSort.test.ts (9), app-shell/src/views/RecordDetailView.relatedListInheritedSort-5795.test.tsx (5, the end-to-end $orderby pin through the real page), plugin-detail/src/synth/__tests__/buildDefaultPageSchema.relatedSort.test.ts (6), plugin-detail/src/__tests__/RelatedList.sortDroppedOutsideWindowed.test.tsx (4, the recorded hole).\n\nGATES (exit captured before any pipe; each quotes the gate's own line):\n- `pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell' --filter '@object-ui/plugin-detail' type-check` EXIT=0 — `Scope: 2 of 47 workspace projects`, `packages/app-shell type-check$ tsc --noEmit && tsc -p tsconfig.test.json` -> `Done`, same for plugin-detail. Non-zero scope + echoed script name rules out the silent zero-match green.\n- dependency-closure build (`--filter '@object-ui/app-shell^...' --filter '@object-ui/plugin-detail^...' build`) EXIT=0, under the shared verify lock (`VERDICT command-exit 0 - held 121s - waited 77s`).\n- path-filtered vitest, 12 files incl. the pre-existing neighbours (deriveRelatedLists.test.ts, all synth tests, serverpagination, speclimit, headerSort, relationalSort, unmaterializedSort): `Test Files 12 passed (12)` / `Tests 172 passed (172)`, EXIT=0. Whole-package app-shell vitest NOT run, per dispatch.\n- eslint over the merge-base delta (7 files), plain `eslint` with no --no-inline-config: EXIT=0, zero error lines (one real `prefer-const` error found and fixed; the remaining 217 are pre-existing `no-explicit-any` warnings).\n- check:esm-specifiers 0 (run per the new standing rule, though the one new import is a package specifier, not relative), check:phantom-deps 0, check:self-import 0, check:control-bytes 0 (plus a direct `grep -naP` control-byte sweep of the delta), check:spec-symbols 0, check-changeset-presence 0 (`3 source file(s) of 2 released package(s) changed ... declares 1 changeset(s)`), check-changeset-no-major 0.\n- DECLARED NARROWING: check:eager-closure NOT run — it refuses as a *broken gauge* without apps/console/dist/eager-closure.json from a console `vite build`, which is CI's run. Safe to leave there rather than merely unrun: the one new import edge is deriveRelatedLists.ts -> @object-ui/core, and app-shell/src/utils/index.ts (the barrel that re-exports deriveRelatedLists) ALREADY imports @object-ui/core, so the edge adds nothing to that closure.\n\nREVERSE VERIFICATION — direction predicted in writing BEFORE running, three ablation legs, every cell matched. No rebuild leg needed or skipped: vitest.config.mts aliases @object-ui/app-shell and @object-ui/plugin-detail to their src/, so every subject resolves from SOURCE and no dist stands between an edit and a run (verified in the config, not assumed). Each leg asserted the mutation on disk by EXACT-SUBSTRING count before (want 1) and after (want 0) plus `git diff --numstat` = `0 1`, printed its landing site, asserted anchor uniqueness before writing, and restored under `trap ... EXIT INT TERM` with a cwd-independent `git -C` command; each leg reported `RESTORED ... 0 dirty entries`.\n  L1 remove the emit           -> inheritSort 7 failed/2 passed; wire 3 failed/2 passed; synth 6 passed; hole-pin 4 passed\n  L2 remove re-drop site 1     -> inheritSort 9 passed;          wire 3 failed/2 passed; synth 6 passed\n  L3 remove re-drop site 2     -> inheritSort 9 passed;          wire 3 failed/2 passed; synth 3 failed/3 passed\nThe legs that stayed GREEN were predicted too: the counter-probes assert ABSENCE, so a counter-probe going red would mean it was really a second subject. Only the wire test goes red on all three legs — which is exactly why the subject is the $orderby reaching dataSource.find and not the descriptor.\n\nCOUNTER-PROBES: (a) a child with no default list-view sort produces a working, scoped, WINDOWED related list carrying NO $orderby — key absent, not present-and-empty — with $filter and $top asserted as live controls so 'nothing inherited' cannot read as 'nothing fetched'; an unusable sort (number, bare string array, entry without `field`) is dropped, never guessed. (b) PRECEDENCE IS DEFINED, so no stop-and-report was needed: a hand-authored page carries its own record:related_list node and never enters this synthesizer (RecordDetailView synthesizes only when no page is assigned), and within the synthesized path the ONE producer of related[].sort is the inheritance while the synthesizer is a pure carrier. An authored sort behaves exactly as before; both facts are recorded in the synth test.\n\nA harness note worth carrying: the Related tab is opened via the URL (`?tab=related`, ADR-0054 C3) rather than a synthetic click — the list self-fetches when its tab mounts, and the child object is queried TWICE on that page (the tab strip's `$top:1,$count:true` count probe carries no ordering by design), so the helper selects the windowed page fetch explicitly. Without that, every counter-probe would have passed by reading the count probe."
      ,
      "open_questions": [],
      "out_of_scope_findings": [
        "NOT filed, already open as objectui#4286: a synthesized Reference Rail page is unpersistable — the `aside` region carries a `className` that PageRegionSchema refuses. Re-confirmed here and measured on that branch with NO `sort` anywhere, so it is independent of this change; it is why the third-site leg asserts entry shape instead of parsing the whole page.",
        "NOT filed, already open as objectui#4664: the same emit point ignores `relatedListFilter`. Re-confirmed by this measurement and deliberately not folded in, per dispatch.",
        "No new issues filed. Searched open issues for both findings before deciding (keyword + file path); both already exist."
      ]
    }

    Notes for the PM, outside the JSON:

    • Draft PR feat(app-shell,plugin-detail): derived related lists inherit the child object's default list view sort #6088 — not marked ready, not enqueued, no auto-merge. mergeable_state: behind (main moved during the run); the PM owns the update.
    • objectstack untouched. objectstack#11345 is pm:blocked on this card; per the relay it closes when this lands and its 10/20/30/40 acceptance leg passes, and the 17.0.0 migration prose becomes true as written with no prose edit.
    • The attached hole, restated so it is not lost: a declared or inherited record:related_list.sort is dropped whenever the list leaves windowed mode (the client text filter). Pre-existing on the authored prop; the inherited sort inherits it identically, and now reaches users who authored nothing. RelatedList.tsx was out of this card's file surface, so it is recorded in RelatedList.sortDroppedOutsideWindowed.test.tsx rather than fixed — those assertions pin today's behaviour and should go red when someone closes it. Worth its own card.
    • A trap I hit and fixed in my own tooling, in case it is worth a standing note: my first patch helper used JS String.replace, and a replacement string containing a $ followed by a backtick (a JSDoc citation of the field-name regex) was reinterpreted as the $\`` substitution pattern — splicing the entire preceding file back in. git diff --statstill read as a plausible insert-only diff (+149) and the injected-marker greps still counted 1. Only a structural check (the file's header block appearing twice) caught it. The helper now splices withslice()and neverreplace()`.

    Generated by Claude Code

  8. yinlianghui commented on Aug 24, 2026

    @yinlianghui
    Contributor

    PM review — ACCEPT, enqueued

    PR #6088 at 5d599be9c. Gates read once at 16:36:30Z, armed off this run's own started_at (16:23:52Z, ~12½ min).

    22 checks, 19 success + 3 skipped, 0 failing, 0 still running — read as every check green, not the required subset. Not a governed surface; marked ready and enqueued through the merge queue. ⛔ Nothing merged by hand.

    Two of my dispatch assumptions were wrong, and the dev measured rather than inherited

    ⚠️ Five of the nine line references I relayed had drifted (RecordDetailView +11, others −3..+2), and ⚠️ I said MetadataProvider.tsx lives in objectstack — it is in this repo. Both corrected by measurement on the merge-base. That is the third and fourth PM assumption falsified this round, which is the system working as intended: the dispatch's job is to name what I believe so it can be checked, ⛔ not to be right.

    The find the card did not name

    A third re-drop site exists — buildDefaultPageSchema.ts:940, into record:reference_rail entries — and it is deliberately excluded because ReferenceRailEntrySchema is $strict with no sort key. ✅ That is the objectui#5494 class, the same one #6066 landed on this morning, and it is pinned as its own leg rather than silently skipped. A silent skip here would have read as "two sites, both handled"; the pin makes the exclusion a measured fact with a reason.

    The design call, and why it is the right one

    The two sort surfaces share a union but mean different things by its string arm — ListView's legacy space-separated 'seq_no desc' versus the related list's 'field' / '-field'. The inherited value is normalised to the array arm once, at the derivation, through @object-ui/core's convertSortToQueryParams — the repo's single definition of both dialects — ⛔ rather than by a tolerant reader downstream. ✅ A tolerant reader is how you get a third dialect; normalising at the producer is how you get one.

    And the ruling was executed as ruled: no new spec key (objectstack#11345, maintainer 2026-08-23, direction 1). RelatedList.tsx and renderers/record-related-list.tsx are unchanged, exactly as the relay predicted.

    Reverse verification — three legs, every cell predicted in writing first

    leg inheritSort wire synth
    L1 remove the emit 7 failed / 2 passed 3 failed / 2 6 passed
    L2 remove re-drop site 1 9 passed 3 failed / 2 6 passed
    L3 remove re-drop site 2 9 passed 3 failed / 2 3 failed / 3

    ⚠️ The greens were predicted too, which is the part that makes this a measurement rather than a fishing trip: the counter-probes assert absence, so a counter-probe going red would have meant it was really a second subject. Only the wire test goes red on all three legs — which is precisely why the subject is the $orderby reaching dataSource.find, ⛔ not the descriptor.

    No rebuild leg was needed and that was verified, not assumed: vitest.config.mts aliases both packages to their src/, so no dist/ stands between an edit and a run. ✅ Stating why a step is absent beats omitting it silently.

    The harness note that would otherwise have been a false green

    ⚠️ The child object is queried TWICE on that page. The tab strip's count probe ($top:1, $count:true) carries no ordering by design, so the helper selects the windowed page fetch explicitly. Without that, every counter-probe would have passed by reading the count probe — a green that looks identical to a correct result. ✅ Recorded in the report rather than buried in the harness.

    Also right: the Related tab is opened via the URL (?tab=related, ADR-0054 C3) rather than a synthetic click, so the list self-fetches when its tab mounts — the real path, not a simulated one.

    Precedence — no stop-and-report needed, and the reason is on the record

    A hand-authored page carries its own record:related_list node and never enters this synthesizer (RecordDetailView synthesizes only when no page is assigned); within the synthesized path the one producer of related[].sort is the inheritance, and the synthesizer is a pure carrier. An authored sort behaves exactly as before. ✅ Both facts pinned in the synth test rather than argued in prose.

    Counter-probe on the negative case

    A child with no default list-view sort produces a working, scoped, windowed related list carrying no $orderby — key absent, ⛔ not present-and-empty — with $filter and $top asserted as live controls, so "nothing inherited" cannot read as "nothing fetched". An unusable sort (a number, a bare string array, an entry without field) is dropped, never guessed.

    Declared narrowing, correctly reasoned

    check:eager-closure was not run: it refuses as a broken gauge without apps/console/dist/eager-closure.json from a console vite build, which is CI's job. ✅ Safe to leave there rather than merely unrun — the one new import edge is deriveRelatedLists.ts → @object-ui/core, and app-shell/src/utils/index.ts (the barrel re-exporting it) already imports @object-ui/core, so the edge adds nothing to that closure. That is an argument, not a shrug.

    Out of scope, and correctly not re-filed

    Both adjacent defects were re-confirmed and found to be already open — objectui#4286 (a synthesized Reference Rail page is unpersistable: the aside region carries a className that PageRegionSchema refuses) and objectui#4664 (the same emit point ignores relatedListFilter). ✅ Searched before deciding, by keyword and file path; nothing duplicated.

    Downstream: objectstack#11345 is pm:blocked on this card and unblocks on merge.


    Generated by Claude Code

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 seatpm:dispatched

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions