|
| 1 | +# Audit: ADR-0085 PR4 — real-backend browser verification of the four detail-page shapes |
| 2 | + |
| 3 | +**Date**: 2026-07-16 · **Scope**: objectui `RecordDetailView` rendering after the |
| 4 | +legacy monolith detail renderer + `renderViaSchema` kill-switch were removed |
| 5 | +(objectui#2546, ADR-0085 PR4). **Method**: drive the running `app-showcase` |
| 6 | +backend + the objectui Console (post-#2546 source) in a real headless browser. |
| 7 | +**Tracked as**: #2548 (the verification objectui#2546 deferred: "Exercising the |
| 8 | +four detail-page shapes … against real data needs a running `@objectstack` |
| 9 | +backend, which the CI/agent environment doesn't have. Deferred to #2548 with a |
| 10 | +repro runbook.") |
| 11 | + |
| 12 | +## Why this is a separate pass |
| 13 | + |
| 14 | +objectui#2546 deleted the non-schema-driven `DetailView` monolith branch, so |
| 15 | +`RecordDetailView` now has exactly **one** path: an authored |
| 16 | +`PageSchema(pageType='record')` when assigned, else `buildDefaultPageSchema()` |
| 17 | +synthesis — both rendered through `<SchemaRenderer>`. Unit + DOM suites and the |
| 18 | +served-metadata proof (`packages/dogfood/test/semantic-roles.dogfood.test.ts`) |
| 19 | +cover parse and serialization, but neither renders a page in a browser against a |
| 20 | +live backend. This pass closes that gap for the four shapes the PR called out. |
| 21 | + |
| 22 | +## Fixtures (all in `examples/app-showcase`) |
| 23 | + |
| 24 | +| Shape | Object / record | Role posture | |
| 25 | +|---|---|---| |
| 26 | +| **grouped** | `showcase_semantic_zoo` | `fieldGroups: [basics, money(collapsed)]`, `stageField: 'status'`, `highlightFields: [name,status,amount]` | |
| 27 | +| **ungrouped** | `showcase_account` (Details tab) | no `fieldGroups` → flat auto-columns | |
| 28 | +| **stageField:false** | `showcase_semantic_zoo_legacy` | `stageField: false` with a `status`-named field (must NOT get a stepper) | |
| 29 | +| **related-heavy** | `showcase_account` = "Contoso" | inbound lookups from contact / invoice / project / field_zoo | |
| 30 | + |
| 31 | +`showcase_semantic_zoo{,_legacy}` are the ADR-0085 runtime fixtures; the |
| 32 | +`showcase_account` seed provides the ungrouped + related-heavy record (Contoso: |
| 33 | +2 contacts, 2 invoices, 2 projects). |
| 34 | + |
| 35 | +## Repro runbook |
| 36 | + |
| 37 | +```bash |
| 38 | +# 1. Backend — showcase on a private port + DB, seeded dev admin. |
| 39 | +pnpm --filter @objectstack/example-showcase... build # or: pnpm build |
| 40 | +node packages/cli/bin/run.js dev \ |
| 41 | + -p 4600 -d file:/tmp/showcase-verify.db --seed-admin --compile \ |
| 42 | + --cwd examples/app-showcase |
| 43 | +curl -s localhost:4600/api/v1/health # -> {"status":"ok"} |
| 44 | + |
| 45 | +# 2. Console — objectui source (post-#2546) pointed at the backend. CORS is |
| 46 | +# enabled in dev, so the cross-origin :5180 -> :4600 calls are allowed. |
| 47 | +cd ../objectui |
| 48 | +VITE_SERVER_URL=http://localhost:4600 pnpm --filter @object-ui/console dev # :5180 |
| 49 | + |
| 50 | +# 3. Auth + fixtures (better-auth Bearer). Sign in, grab the set-auth-token |
| 51 | +# header, POST the two semantic-zoo records (not seeded), then drive the |
| 52 | +# detail URLs with Playwright: |
| 53 | +# /apps/showcase_app/<object>/record/<id> |
| 54 | +# inject localStorage['auth-session-token'] = <token> before app boot. |
| 55 | +``` |
| 56 | + |
| 57 | +The exact Playwright driver + JSON report used for this pass are attached to the |
| 58 | +#2548 PR discussion. |
| 59 | + |
| 60 | +## Results — all four shapes render correctly through the schema-only path |
| 61 | + |
| 62 | +| Shape | Observed | Verdict | |
| 63 | +|---|---|---| |
| 64 | +| **grouped** (`Zoo Grouped Alpha`) | Highlight strip `name / Active / 4,200` (= `highlightFields`); `record:path` stepper **Draft → Active → Done** (from `stageField:'status'`); ungrouped `notes` in the "More details" bucket. | ✅ renders | |
| 65 | +| **ungrouped** (Contoso · Details) | Flat two-column field layout (owner / website / HQ / tax / billing) with the "Show 4 empty fields" toggle — no section headers. | ✅ renders | |
| 66 | +| **stageField:false** (`Zoo Legacy Beta`) | Highlight strip `name / 99`; the `status` field renders as an ordinary **"STATUS: Green"** field with **no `record:path` stepper** (`[aria-label="Record path"]` absent). The `false` correctly suppresses the heuristic. | ✅ suppressed | |
| 67 | +| **related-heavy** (Contoso) | Tab strip **Details · Invoices (2) · Projects (2) · Related (2)** — primary related lists promoted to their own tabs, the rest collapsed into "Related" (ADR-0085 prominence rule); lists lazy-fetch on tab show. | ✅ renders | |
| 68 | + |
| 69 | +`hasRecordPath` matched the expectation for every case (true for grouped + |
| 70 | +related-heavy, **false** for stageField:false). No page-render errors, no error |
| 71 | +boundaries, no missing-data states. |
| 72 | + |
| 73 | +### Note on the "grouped" fixture |
| 74 | + |
| 75 | +`showcase_semantic_zoo`'s grouped fields (`status` ∈ basics, `amount` ∈ money) |
| 76 | +are *also* its `highlightFields`, so they surface in the highlight strip and the |
| 77 | +`record:details` derivation correctly drops the now-empty Basics/Money groups, |
| 78 | +leaving only the ungrouped `notes` ("More details"). This is the intended |
| 79 | +"one curated list, every surface" behavior — the same reason Contoso's |
| 80 | +highlighted `status/industry/revenue` do not repeat in its Details body. The |
| 81 | +`fieldGroups → section` derivation itself (declared order, collapse, trailing |
| 82 | +untitled bucket) is separately proven over the served pipeline in |
| 83 | +`semantic-roles.dogfood.test.ts`. |
| 84 | + |
| 85 | +## Out-of-scope observations (not defects in the detail path) |
| 86 | + |
| 87 | +- `502 /api/v1/runtime/config` and `502 /api/v1/dev/metadata-events` are |
| 88 | + **same-origin** requests the Console makes to its own origin (`:5180`); in the |
| 89 | + split-origin verify harness the Vite server has nothing there. `initRuntimeConfig` |
| 90 | + absorbs the failure by design and the app boots + renders regardless. Not seen |
| 91 | + in the normal same-origin (`--ui` / vendored console) deployment. |
| 92 | +- `401 /api/v1/approvals/requests?…` (the header approvals badge) is a peripheral |
| 93 | + poll unrelated to record rendering; the detail pages loaded their data fine. |
| 94 | + Left for separate triage. |
| 95 | + |
| 96 | +## Conclusion |
| 97 | + |
| 98 | +The removal of the legacy monolith renderer (objectui#2546) leaves all four |
| 99 | +detail-page shapes rendering correctly through the single `SchemaRenderer` / |
| 100 | +`buildDefaultPageSchema` path against a real `@objectstack` backend. **No |
| 101 | +regressions found; no objectui follow-up required.** The framework-side cleanup |
| 102 | +#2546 flagged (the stale `renderViaSchema` source comment) ships alongside this |
| 103 | +note. |
0 commit comments