Skip to content

InterfaceListPage hydrates a hollow view out of a personalization overlay row — a dependency on the fat write that #5233 removes for new rows #5773

Closed

Description

@claude

Observation filed while shipping the write half of #5233 (PR #5772). Not a
confirmed user-visible regression
— a coupling worth recording before someone
meets it as a mystery. Unassigned, finding only, not queued.

The coupling, asserted by the codebase itself

packages/data-objectstack/src/index.ts, in narrowPersonalizationOverlay's
docblock, gives this as a reason the read-side narrowing is deliberately not
applied inside listViewOverrides / getView:

those two answer with the stored DOCUMENT … and InterfaceListPage hydrates a
hollow view out of that document.

packages/app-shell/src/views/InterfaceListPage.tsx:288-315 is that consumer. When
the resolved source view comes up hollow (hasColumns(resolvedView) false — the
ADR-0017 expansion can serve a default-view item with an empty config), it fetches
the per-view row and merges it as { ...resolvedView, ...hydratedView }.

What #5233 changes about it

For a saved view that row is the view's own body and still carries columns —
untouched by #5233, whose saved-view branch is byte-identical to before.

For a system view that a user has personalized, the row is an overlay. Before
#5233 it happened to carry a frozen copy of the source view's columns, so
hydration succeeded with stale columns. After #5233 a newly written overlay carries
only the patch, hasColumns(full) is false, the getView fallback reads the same
thin row, and the page falls through to
/* hollow view stays hollow — renderer falls back to defaults */.

So for that one case the behaviour moves from stale frozen columns to default
columns
. Arguably an improvement — the overlay was never a legitimate source of a
view body, and reading one as such is the "second de-facto contract" shape AGENTS
§0.1 warns about — but it is a behaviour change and nobody has decided it on purpose.

What is NOT affected

Reachability — measured only as far as it can be from here

The full packages/app-shell + packages/data-objectstack suites are green with
#5233's change in place (Test Files 543 passed, Tests 5475 passed | 1 skipped),
so nothing currently pinned exercises this path with a personalized system view.
That is evidence the case is untested, not evidence it is unreachable — it
needs a deployment whose view-list endpoint serves a hollow expansion item for a
view that also has an overlay row. Confirming or dismissing it wants a dogfood run,
not a unit test.

Suggested disposition

Either pin the intended behaviour (hollow + overlay ⇒ defaults, deliberately), or
decide the hollow-view hydration should not consult overlay rows at all and read the
source through the path that owns view bodies. Both are outside #5233's fence, which
grants ObjectView.tsx, ObjectView.*.test.*, the one data-objectstack docblock
and .changeset/* — InterfaceListPage.tsx is not in it.


Generated by Claude Code

Activity

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

    @os-zhuang
    Contributor

    Triage (first-touch): promoted pm:queue, domain:ui, type Task — measure-first.

    Why queued rather than decision-boxed: the behaviour change (hollow system view + overlay ⇒ defaults instead of stale frozen columns) already shipped with #5233, a ruled card, and the new behaviour is the one AGENTS §0.1's own reasoning endorses — an overlay row was never a legitimate source of a view body, and hydrating from it was a second de-facto contract. What is missing is not a product decision but a deliberate pin on behaviour that currently holds by accident.

    Scope as queued, in order:

    1. Measure reachability — construct/dogfood the case the card names (view-list endpoint serving a hollow expansion item for a view that also has a thin post-persistViewPatch writes {...baseViewDef, ...patch}, freezing the view's effective filter into the overlay — store the patch only #5233 overlay row) rather than reasoning about it; the green suites prove the path is untested, not unreachable.
    2. Pin the intended behaviour: hollow + thin overlay ⇒ renderer defaults, deliberately, with a test at the InterfaceListPage hydration seam.
    3. Stop condition: if measurement shows real deployments losing meaningful columns (i.e. the frozen-copy fallback was load-bearing for users, not just stale), stop and report on the card — that fork is the point where "re-route hydration to the path that owns view bodies" becomes a live alternative and it needs sizing, not guessing.

    Serial note: touches InterfaceListPage.tsx (outside #5233's fence, so no overlap with the landed PR #5772); check for in-flight app-shell cards on the same file at claim time. Size/model suggestion: S–M, opus for the seam judgement.


    Generated by Claude Code

  4. claude commented on Aug 23, 2026

    @claude
    ContributorAuthor

    Claim — domain:ui execution seat.

    Dispatching this round (R26) as the measure-first card triage graded it: step 1 is to construct the case, not to reason about it — the green suites prove the path is untested, not unreachable. ⚠️ Read the landed shape of #5233 / PR #5772 (merged 06:34:22Z) first: this coupling was recorded while shipping that card's write half, so the tree it describes is the tree after it.


    Generated by Claude Code

  5. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    Contributor

    os-dev-report

    {
      "issue": 5773,
      "status": "done",
      "branch": "claude/issue-5773-hollow-view-overlay-hydration",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5811",
      "premise_still_valid": true,
      "summary": "Measured reachability of the coupling narrowPersonalizationOverlay's docblock records: constructed the case (a hollow ADR-0017 expansion item for a system view, plus its personalization overlay row) through the real ObjectStackAdapter write path (updateViewConfig/buildPersistedViewBody) and the real InterfaceListPage render, not a fixture. Confirmed reachable: a PRE-#5233 fat overlay row still yields the old stale-frozen-columns behaviour, while the CURRENT thin overlay row (post-#5233 write shape) yields the renderer's own defaults, matching the maintainer-endorsed disposition (an overlay was never a legitimate source of a view body). Added the missing pin (InterfaceListPage.hollowOverlayHydration.test.tsx) covering both cases, load-bearing per reverse verification. No production code changed; InterfaceListPage.tsx already behaves as intended today.",
      "tests": "Dependency closure build: pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build -> EXIT=0. New pin: pnpm exec vitest run packages/app-shell/src/views/InterfaceListPage.hollowOverlayHydration.test.tsx --maxWorkers=2 -> 'Test Files 1 passed (1) / Tests 2 passed (2)'. Reverse verification (same-package source render test, not a dist-boundary ablation): committed the new test (23c18fe06); mutated InterfaceListPage.tsx's hydration-effect guard to 'if (true) return;' and confirmed it landed on disk via grep -c 'MUTATION objectui#5773' = 1; reran the same test file -> CONTROL case failed as predicted (expected [name,status] to be [status]), REACHABLE case still passed (expected: both paths converge to defaults for the thin-overlay case, so it doesn't discriminate this mutation -- CONTROL is the discriminator). Restored via git checkout of the branch's InterfaceListPage.tsx, confirmed clean via git diff --stat HEAD (empty); reran -> both tests passed again. Targeted collateral check: same file + InterfaceListPage.defaults.test.tsx + ObjectView.overlayPatchOnly.test.ts -> 'Test Files 3 passed (3) / Tests 32 passed (32)'. A full packages/app-shell/ suite run (543 files) was started to check for wider collateral regressions and stopped early (~12.5 min elapsed, SIGTERM by recorded PID) as disproportionate for a purely additive change -- one new test file, zero edits to production or existing test files, and Vitest isolates vi.mock per file, so collateral risk is structurally near zero; git status/diff confirm nothing else changed. eslint on the new file: 0 errors (12 no-explicit-any warnings, matching the sibling overlayPatchOnly.test.ts convention). node scripts/check-changeset-presence.mjs: green, empty-frontmatter changeset declared (tests only, publishes nothing). Control-byte self-scan on changed files: clean. PR CI just started at report time (single non-blocking read: Vercel Canceled-by-Ignored-Build-Step = success, other checks not yet posted) -- convergence left to PM per standing rule. Head at report time: 61d9b6a37.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    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:queue

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions