Skip to content

docs(spec): navigation.view stops promising a view selection nothing performs - #17796

Open
os-bill wants to merge 1 commit into
mainfrom
claude/issue-16885-list-navigation-view-resolves-nothing
Open

docs(spec): navigation.view stops promising a view selection nothing performs#17796
os-bill wants to merge 1 commit into
mainfrom
claude/issue-16885-list-navigation-view-resolves-nothing

Conversation

@os-bill

@os-bill os-bill commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator

Part of #16885 — this round is measure-and-report. Both repairs triage named (honour view, or retire it under ADR-0049) are out of bounds for it, so the card stays open.

  • Clause-②: no — this PR puts no new key on any published payload.

What this changes

One .describe() string on NavigationConfigSchema.view, plus the three generated reference-doc rows that project from it, plus a changeset.

No accept set moves. view is still z.string().optional(). Every document that parsed before parses now, with identical issues and identical output. Nothing is retired, renamed, constrained, or newly resolved.

The measurement

Declaration — packages/spec/src/ui/view.zod.ts, NavigationConfigSchema (unique in non-built source: preventNavigation occurs exactly once outside dist/, at that block).

Reads — re-measured against source, not the bundle the card quoted. Repo-local: zero (git grep over tracked files excluding **/dist/**; lit control — navigation.mode occurs 5×). objectui at the sha this repo pins in .objectui-sha (53ded82b, read with git show SHA:PATH; dist/ is untracked at that sha, so the reading is reproducible):

probe result control
navigation?.view read 1packages/react/src/hooks/useNavigationOverlay.ts:245 navigation?.mode / preventNavigation / openNewTab: 13 sites
consumers of the hook's returned .view 0 siblings width / isOverlay / mode on the same returned object: ~20 sites
formViews read keyed by an authored view name 0 — every read is formViews?.default formViews reads overall: 23 sites

The one read binds const view = navigation?.view and then passes it as the second argument of onNavigate at two branches (:287 no-config, :320 page) — the slot whose other producers in the same function are mode tokens (onNavigate(id, 'new_window') at :278 and :301).

A consumer with a closed vocabulary on that argument exists. packages/app-shell/src/views/ObjectView.tsx:2540 types it (recordId: string | number, mode: 'view' | 'edit') and branches on both values with no fallback arm — an authored name matches neither and the row click does nothing. The sibling consumer at :1957 tolerates anything non-new_window, which is why the effect is invisible on some surfaces.

Authored instances in this repo — zero, and the space is not vacuous: ListViewSchema.navigation is set nowhere in-tree (every object-form navigation: { in tracked files, 21 sites, is an i18n translation map). The scanner was proven on a synthetic fixture that lights for both view and mode before that zero was accepted.

Which exit triage's two the measurement supports

Undetermined on the repair — and that is the finding. Both exits keep live evidence, neither is discharged:

  • Meant to name a form view. The declaration does not merely hint through the key's name — it states the intent in a sentence: "Name of the form view to use for details (e.g. summary_view, edit_form)", under a /** Target View Config */ comment. Downstream, useNavigationOverlay promotes view to a first-class member of NavigationOverlayState, which reads as a slot built for renderers and never wired. And a maintainer comment at ObjectView.tsx:1968 describes the forwarding path in so many words.
  • Residue to retire. No resolution step exists anywhere, at any layer. No lint rule guards it (lint-view-refs resolves app navigation viewNamelistViews, a different key on a different surface). The spec CHANGELOG never mentions it, while the sibling size work is mentioned 3×.

⚠️ One piece of apparent counter-evidence, read at the site rather than counted: objectui's view-navigation-config-spec-parity.test.ts:139 asserts site.navigation?.view === 'summary_view'. It uses { view: 'summary_view' } only as a convenient mode-less config to pin type parity — it asserts the key is accepted, never that it resolves.

⇒ Choosing between them is the seat's act: honouring view is new capability, retiring it narrows a published schema. This PR takes neither.

Verification

At bec4767c (the final commit).

  • pnpm --filter @objectstack/spec buildVERDICT command-exit 0 (shared verify lock), then build-docs.ts✅ Generated 222 files.
  • Gate families run green, each read from the gate's own verdict line: spec check:authorable-surface · check:docs · check:generated · check:liveness · check:api-surface · check:skill-refs · check:skill-examples · check:llms-txt · check:variant-docs · check:empty-state · check:strictness-ledger · check:objectui-pin-citations · check:export-origins · check:duration-unit-keys · check:exported-any · check:dual-source-exports · check:entry-nameability · check:yaml-examples · check:browser-reachable-entries; repo check:doc-authoring · check:doc-anchors · check:docs-single-h1 · check:docs-spec-enumerations · check:docs-audit-scope · check:docs-redirects · check:docs-transcript-drift · check:nul-bytes · check:quick-reference-counts · check:corpus-claim-drift · check:published-files · check:org-identifier · check:role-word · check:slot-lookup · check:watch-hint-literal · check:spec-parsed-alias · check:page-declaration-shape · check:pm-widening-tells · check:skill-identifier-liveness · check:published-readme-links · check:type-source-resolution · check:test-source-alias · check:cross-package-test-inputs · check-adr-0087-registration --base origin/main · check-changeset-no-major --base origin/main · check-closing-keyword-parity · check-comment-mask-adoption · check-comment-mask-corpus · check-registry-log-declared · check-section-landing-index · check-plugin-teardown-shape · check-platform-object-tenancy-census · check-reference-carrier-shape · check-ci-filter-parity · check-affected-docs · check-undeclared-dep-imports · check-system-context-census.
  • pnpm --filter @objectstack/spec typecheck → exit 0. Targeted tests src/ui/view.test.ts + src/ui/interaction-config-retirement.test.ts373 passed.
  • eslint . --no-inline-config over the whole repo → exit 0, no output. ⭐ Not a narrowing: the repo-level scan actually ran at this head, so no ratchet reading is deferred.
  • check:doc-authoring caught a real defect mid-round: the first draft of the description carried the card id, which that gate forbids inside .describe() prose because the prose projects into published docs where the citation resolves to nothing. The id was moved to the adjacent TSDoc block and the gate went green.

Changeset — measured, not assumed. Owed. @objectstack/spec's published files[] ships both dist and src/**/*.zod.ts. On the rebuilt artifact the corrected sentence is present in 22 built bundles and in the published source file; the old sentence is absent from all of them. Positive control: a sibling shipped describe() (Disable standard navigation entirely) lit the same probe at 22. Negative control: a test-only it() title lit src at 1 and dist at 0.

验收备注


Generated by Claude Code

…g performs

`NavigationConfigSchema.view` described itself as "Name of the form view to use
for details". Measured against spec source and the `objectui` tree this repo
pins in `.objectui-sha`, no reader resolves an authored view name:

- Its only read is `useNavigationOverlay`, which binds `navigation?.view` and
  passes that string as the SECOND argument of `onNavigate` — the slot whose
  other producers are navigation MODE tokens.
- The hook also re-exports it on `NavigationOverlayState.view`, and no consumer
  reads that member, while its siblings on the same object (`width`,
  `isOverlay`, `mode`, `selectedRecord`) are read at ~20 sites.
- Every `formViews` read in that tree is `formViews?.default`; none is keyed by
  an authored view name.
- One shipped consumer types that argument `'view' | 'edit'` and branches on
  both with no fallback arm, so an authored name is a dead row click.

The description now says that and carries the repo's existing
`[EXPERIMENTAL — not enforced]` marker. No accept set moves: `view` is still
`z.string().optional()` and every document that parsed before parses now. The
enforce-or-remove decision (ADR-0049) is deliberately NOT taken here.

Generated reference docs regenerated from the corrected description.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation protocol:ui tooling labels Sep 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/protocol/objectui/layout-dsl.mdx (via summary_view (literal, a string literal in NavigationConfigSchema))
What this run could not see
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 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 — 136 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 8fa3fe63d9e8dbbc507c0735d918c99db41df31dpackageMentionDocs.

Which tree this was computed on

This run read content/docs from d93e47ef990523b6275afdceeb39854ccfdfd9f5 — the merge of head bec4767cc78de82366224351b85042bf546b27f2 into base 8fa3fe63d9e8dbbc507c0735d918c99db41df31d, 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 d93e47ef990523b6275afdceeb39854ccfdfd9f5 && git checkout d93e47ef990523b6275afdceeb39854ccfdfd9f5
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8fa3fe63d9e8dbbc507c0735d918c99db41df31d bec4767cc78de82366224351b85042bf546b27f2 && git checkout -B drift-repro 8fa3fe63d9e8dbbc507c0735d918c99db41df31d && git merge --no-ff bec4767cc78de82366224351b85042bf546b27f2

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

⚠️ 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 8fa3fe63d9e8dbbc507c0735d918c99db41df31d → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-bill
os-bill marked this pull request as ready for review September 12, 2026 05:29

os-bill commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator Author

Third instance, and the first where the re-declared line is a .describe() edit on an already-published key

domain:spec execution seat, 2026-09-12T05:31Z. ⛔ No label touched; this is evidence.

PR #17796 (card #16885) is blocked by the same T1 tell. check-clause2-carriers --pair 17796exit 4:

✗ C5 — card #16885 declares Clause-②: no while its diff carries 1 widening tell(s) … packages/spec/src/ui/view.zod.ts:1615 (T1)
T1 — a new key on a Zod object schema — the accept set gains a spelling an author may now write

⛔ The key is not new, and here is the measurement

NavigationConfigSchema.view has been declared for a long time. Counted with a pattern file (⛔ not an escaped literal inside $(...), which has produced false zeros for me repeatedly tonight):

corpus view: z.string().optional() lit control preventNavigation dark control
origin/main @ 8fa3fe63d9 2 1 0
the PR's branch head 2

same count on both sides. On origin/main the two sites are :1146 (a tab schema) and :1599 (NavigationConfigSchema, under its /** Target View Config */ comment). The PR changed only the .describe() string on that second line, which pushed it to :1615 — and the diff therefore re-emits the whole view: … line as an addition.

This instance sharpens the shape. The two earlier ones re-declared a key while restructuring around it. Here the diff does nothing but correct the prose inside an existing key's .describe() — arguably the most common edit anyone makes to a Zod schema — and T1 reads it as the accept set gaining a spelling. ⇒ the tell fires on a class of change that can never widen anything, because a .describe() string is not an accept set.

⛔ What this seat will not do

The only sanctioned clear is flipping Clause-②: noyes. ⛔ Refused, for the third time: it would record on a permanent carrier that this PR widened a published accept set, which is false and measurably so. A declaration is a claim about the tree, not a key that opens a gate.

⇒ PR #17796 stays draft and unlanded until this card is fixed. Same disposition as #17157 / PR #17638.

domain:spec execution seat · session_01MkQhmuuJAVDjmeWNixwDDH · readings taken 2026-09-12T05:31Z on origin/main @ 8fa3fe63d9


Generated by Claude Code

This was referenced Sep 12, 2026
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:ui size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants