Skip to content

feat(spec)!: retire ListViewSchema.navigation.view, the detail-view binding nothing ever resolved - #18619

Merged
os-bill merged 3 commits into
mainfrom
claude/issue-16885-retire-navigation-view
Sep 17, 2026
Merged

os-bill merged 3 commits into
mainfrom
claude/issue-16885-retire-navigation-view

Conversation

@os-bill

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

Copy link
Copy Markdown
Collaborator

Fixes #16885

Clause-②: no

Retires ListViewSchema.navigation.view under ADR-0049 enforce-or-remove, executing the
maintainer ruling of 2026-09-13 (director decision batch #126 item 4, comment 5651023571,
verbatim 「同意」, option B), with its item 5 superseded by comment 5657440531: the
changeset level is minor, not major.

What the key did

navigation.view was an unconstrained string whose describe promised "the form view to use
for details"
. No layer from spec to console ever resolved a view by that name. Its one read in
the shipped console passed the value into the second argument of onNavigate — the slot
that otherwise carries the navigation-MODE token — so an authored name did not select a view,
it substituted for the mode. A consumer in the same bundle reads that argument against a
closed two-value vocabulary (edit / view), so any other authored value matched neither
branch. Declared, consumed, and wrong.

Acceptance criteria — executable, with both controls

PROBE — navigation: { view: 'summary_view' } is now refused, at the key the author wrote,
with the prescription naming page assignment as the route:

PROBE  navigation:{view:"summary_view"} -> success = false
       path = navigation.view
       message = `view.list.navigation.view` was removed in @objectstack/spec 17.5.0
                 (ADR-0049 enforce-or-remove) — ... Delete the key; to choose what opens for a
                 record, assign a `record` page to the object and let `isDefault` pick the one
                 that opens — page assignment is the machinery that resolves a detail layout ...
LIT    navigation:{mode:"page"}         -> success = true
LIT    all five surviving keys          -> success = true

LIT CONTROL — the live siblings still parse. { mode: 'page' } is accepted, and so are all
five survivors together (mode, preventNavigation, openNewTab, size, width). A tombstone
that broke its siblings would satisfy the refusal assertion while being a larger bug, and
navigation is one closed shape, so that blast radius is the whole block. Pinned at all three
doors — ListViewSchema, ObjectListViewSchema, and the flattened PUT /api/v1/meta/view
overlay.

DARK CONTROL — the reading used is the regenerated packages/spec/authorable-surface/ui.json
ui/NavigationConfig:* row set. It returned: 6 rows before, 6 rows after; exactly one line
changed, ui/NavigationConfig:view -> ui/NavigationConfig:view [RETIRED]; the retired key
never appears plain, and no other ui/NavigationConfig:* row disappeared.

     "ui/NavigationConfig:openNewTab",
     "ui/NavigationConfig:preventNavigation",
     "ui/NavigationConfig:size",
-    "ui/NavigationConfig:view",
+    "ui/NavigationConfig:view [RETIRED]",
     "ui/NavigationConfig:width",

Gates observed RED before GREEN

A gate never observed failing for this change is not known to be a gate for it.

gate red green
check:generated (check:docs leg) ✗ 1 of 15 artifact(s) stale: content/docs/references/** ✓ All 15 generated artifacts are up to date. after --fix regenerated exactly that one
check:generated (check:api-surface leg) ✗ ... dist/**/*.d.ts describe DIFFERENT sources than the ones on disk (stale-dist trap, after the test file moved) ✓ after a real rebuild
check-adr-0087-registration exit 1 — declares a breaking change (BREAKING) but no adr-0087: disposition marker — proven by committing the changeset with the marker line dropped exit 0 — 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition ... registered list-view-navigation-view-retired
the three refusal pins 3 failed / 8 passed under an ablation that restored the pre-retirement live string 11 passed on the real tree

The ablation mutated packages/spec/src/ui/view.zod.ts on disk (tombstone occurrences 1 -> 0,
ablation marker 0 -> 1, blob hash moved), ran the pins, then restored under a trap — restored
blob hash byte-identical to HEAD and git diff HEAD empty. Note the direction: only the three
refusal pins went red; the eight sibling-acceptance assertions stayed green, which is the correct
shape for reverting a tombstone.

Gate results

pnpm --filter @objectstack/spec check:generated — all 15 green, including the four named on
the card: check:authorable-surface, check:api-surface, check:docs, check:liveness. Working
tree clean afterwards (no regeneration drift).

pnpm --filter @objectstack/spec typecheck — green. pnpm --filter @objectstack/spec test —
484 files, 13830 tests, all passed. pnpm lint (repo-wide, eslint . --no-inline-config) —
green, so no narrowing was taken and none is declared.

All 14 source audits check:generated deliberately does not run — green.
check:skill-examples first reported PREREQUISITE NOT MET (packages/client-react/dist unbuilt
in a fresh worktree); after building that closure it reads ✅ 258 prose examples type-check across 3 surface(s).

node scripts/check-changeset-no-major.mjs — green, no major bump.

check-widening-tells did not recur. node scripts/pm/check-widening-tells.mjs --declaration no --diff <this diff> exits 0: 10 changed file(s) — 4 judged against a declared surface (no widening tell), 6 NOT MEASURED. The T1/T2 firing on retiredKey() lines recorded on #17955 and
#17300 did not reproduce here, so there is nothing to report as a regression and the
declaration is unchanged, as ruled.

The ADR-0087 disposition is SEMANTIC, deliberately

The ruling asked for a semantic migration entry, and the shape earns it: a mechanical D2 strip
would delete the key without recording which list view lost it, and an author who wrote it
wanted a named detail layout — a want page assignment serves and a stripped key does not record.
So the entry is a D3 SemanticMigration, list-view-navigation-view-retired, and the tombstone
prescription therefore carries no os migrate meta sentence: that sentence is owed only where
a conversion covers the surface (shared/retired-key.ts module docblock; the class pin
retired-key-migrate-sentence.test.ts deliberately judges nothing when the marker is absent).
The precedent for registering a retired key with no D2 conversion is
data/AggregationNode:distinct in this same table.

Registered as ui/NavigationConfig:view in RETIRED_KEYS_BY_MAJOR[18], which also starts its
aging clock. Note for the reviewer: no major-18 semantic entry reaches spec-changes.json or
docs/protocol-upgrade-guide.md yet
— measured, not assumed: four sampled major-18 ids
(change-management-family-retired, training-family-retired, scim-provider-object-retired,
epoch-instant-keys-renamed) return 0 in both files, while major-17 ids return non-zero and the
guide stops at "Protocol 17". This entry behaving the same way is the steady state for the open
window, not a gap.

Liveness ledger — nothing to update, and why

view/list/navigation is one live row with no classified children, because check-liveness
drills one level. That is #17424's subject; it is cited here and not fixed, exactly as ruling
item 4 directs. Since the walk never reaches navigation's children, no ledger row exists for
view to mark dead, and none is owed — check:liveness is green with the tombstone in place,
with no UNCLASSIFIED row. packages/spec/liveness/** is untouched (held by #18582).

The objectui contract twin is in the SIBLING repo — reported, not touched

Ruling item 3 asked where the contract twins live. They are in objectui, not here. The
reading:

  • objectui/packages/types/src/__tests__/view-navigation-config-spec-parity.test.ts at the pinned
    .objectui-sha (53ded82bf7a494f54e344e19099dbf00854b8694) is the twin. It asserts
    Equal<ViewNavigationConfig, NavigationConfig> — ViewNavigationConfig is this spec type,
    re-exported by reference since objectui#4588.
  • That file authors the retired key as a legal value in four places, e.g.
    const navigation: ViewNavigationConfig = { view: 'summary_view' };. Once objectui resolves a
    spec carrying this tombstone, those lines become tsc errors under that package's type-check.
  • In this repo there is no twin pin covering the key: repo-wide, ViewNavigationConfig occurs
    only in packages/console/CHANGELOG.md (a release-owned file), and the tracked
    sdui.manifest.json declares navigation as a flat object input with no drill into view,
    so check:react-declaration-parity is structurally blind to it. Nothing here to update.

The Console Pin Gate is not affected and does not run on this PR. Measured: that job is gated
on the console paths filter (.objectui-sha plus four scripts), none of which this diff touches;
and it runs scripts/build-console.sh — a build, which does not type-check the sibling's
tsconfig.test.json. objectui's src at the pinned SHA contains no value write of
navigation.view outside that test file (only docblocks), and reads of .view still compile
against never | undefined. So this PR does not turn main red.

What is still owed, in the other repository: the twin gains the tombstone pin before objectui
picks up a spec carrying this retirement. Its dependency is @objectstack/spec: ^17.0.0, so a
published 17.5.0 is in range.

File surface

Every path touched, including the pre-declared OPEN set.

path why
packages/spec/src/ui/view.zod.ts the retiredKey() tombstone + its prescription constant
packages/spec/src/migrations/entries/retired-keys/18.ui__NavigationConfig__view.ts new — RETIRED_KEYS_BY_MAJOR[18] registration (build-schemas gate (b))
packages/spec/src/migrations/entries/semantic/18.list-view-navigation-view-retired.ts new — the ADR-0087 D3 semantic entry
packages/spec/src/ui/view.test.ts the pins: refusal at three doors, plus the lit controls
packages/spec/src/migrations/registry.ts OPEN set — generated by gen:migration-registry, never hand-edited
.changeset/16885-retire-navigation-view.md OPEN set — minor, **BREAKING** banner, FROM -> TO, ADR-0087 disposition
packages/spec/authorable-surface/ui.json OPEN set — regenerated by build
content/docs/references/ui/view.mdx OPEN set — regenerated by gen:docs
content/docs/references/api/protocol.mdx OPEN set — regenerated by gen:docs (the inline navigation summary drops view?: string)
content/docs/references/data/object.mdx OPEN set — same, one row

Held by other in-flight work and not touched: packages/spec/scripts/build-schemas.ts (#17969),
packages/spec/liveness/** and packages/spec/scripts/liveness/check-liveness.mts (#18582),
packages/spec/src/api/protocol.zod.ts (#18597). Ruling item 2 was already discharged — PR #17796
reads closed, not merged — so the retirement is written fresh.

Acceptance notes

Noted, not filed: nothing in the touched files.

One finding to file separately, unrelated to this diff and not fixed here:
pnpm check:cross-package-test-inputs reaches opposite verdicts on an identical source tree
depending only on whether packages/spec/dist/ happens to be built — exit 1 with it present
(115 entries), exit 0 with the same directory moved aside, same commit, restored afterwards.
The flagged radius is packages/spec/dist/ descended from
packages/cli/test/init-created-files-summary.e2e.test.ts, which this diff does not touch. No
pnpm build step precedes pnpm check:cross-package-test-inputs in lint.yml's lint job, so
CI always runs it against an unbuilt tree and it passes vacuously — the declaration hole it exists
to catch is structurally invisible on every PR. That is the "a verifier that silently degrades is
worse than no verifier" shape from AGENTS.md Route & surface ownership rule 3.

维护者速读(草稿)

改了什么 —— ListViewSchema.navigation.view 退役:键仍在 shape 里,但变成 ADR-0049 墓碑,
写它是 tsc 报错、传值进来是带处方的 parse 拒收。处方点名替代路线:给对象发布 record 页面,
用 isDefault 决定打开哪一个。同批落地 ADR-0087 的 D3 语义迁移条目、changeset(minor +
**BREAKING**)、以及三道门上的拒收 pin。

为什么改 —— 这个键承诺"用哪个表单视图打开详情",而从 spec 到 console 没有任何一层按名字解析
视图。它唯一的读取把值塞进了 onNavigate 的第二个参数 —— 那个槽位装的是导航模式令牌。
所以作者写的视图名不是在选视图,而是在顶替模式;而同一个 bundle 里有消费者拿这个参数去对
edit/view 两值词表,任何别的值两个分支都不匹配。仓库内零编写实例,唯一的外部作者已自行删除。
维护者 2026-09-13 裁决 B:零拉力即移除。

风险与代价(含回滚) —— 风险低。行为面没有回归可言:这个键从来没选中过任何东西,决定详情怎么
呈现的是 mode 和 size,两者一字未动,并且有 pin 钉着五个存活键仍然接受。代价是发布面:这是
breaking,升级时仍写着该键的元数据会在三道门(含 PUT /api/v1/meta/view)被拒。回滚就是
revert 本 PR —— 墓碑、注册条目、changeset 与生成产物都在同一个提交序列里,没有跨仓副作用。
一个待办留在姊妹仓:objectui 的契约孪生 pin 把 { view: 'summary_view' } 写成合法值,需要在
objectui 取到带墓碑的 spec 之前补上墓碑 pin。本 PR 不触发 Console Pin Gate,不会让 main 变红。

席位意见 ——

你要做的 ——


Generated by Claude Code

WIP: tombstone + ADR-0087 registration. Generated artifacts follow.

Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3
Co-authored-by: Claude <noreply@anthropic.com>
Tombstone, ADR-0087 registration, regenerated artifacts and the pins.

Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation protocol:ui tests tooling labels Sep 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 8 documentable anchor(s). ⚠️ 2 changed file(s) yielded no anchor (packages/spec/authorable-surface/ui.json, packages/spec/src/migrations/entries/retired-keys/18.ui__NavigationConfig__view.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

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

  • content/docs/api/plugin-endpoints.mdx (via /api/v1/meta/view (route, a path literal in acceptanceCriteria; a path literal in semantic))
  • content/docs/concepts/metadata-lifecycle.mdx (via /api/v1/meta/view (route, a path literal in acceptanceCriteria; a path literal in semantic))
  • content/docs/kernel/services-checklist.mdx (via /api/v1/meta/view (route, a path literal in acceptanceCriteria; a path literal in semantic))
  • content/docs/protocol/objectui/index.mdx (via /api/v1/meta/view (route, a path literal in acceptanceCriteria; a path literal in semantic))
  • content/docs/protocol/objectui/layout-dsl.mdx (via summary_view (literal, a string literal in NavigationConfigSchema))
  • content/docs/ui/forms.mdx (via /api/v1/meta/view (route, a path literal in acceptanceCriteria; a path literal in semantic))
What this run could not see
  • 2 changed file(s) yielded no anchor (packages/spec/authorable-surface/ui.json, packages/spec/src/migrations/entries/retired-keys/18.ui__NavigationConfig__view.ts) — pages documenting those are invisible to this run
  • 4 name(s) were too generic to anchor anything (single lowercase words)
  • 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 21b7c12b4f779b86409db2a7bb9235fe80ef8a54 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 21b7c12b4f779b86409db2a7bb9235fe80ef8a54

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

@os-bill os-bill added domain:spec priority:p2 Medium: important, M3 labels Sep 17, 2026 — with Claude
@os-bill
os-bill marked this pull request as ready for review September 17, 2026 09:38
@os-bill
os-bill added this pull request to the merge queue Sep 17, 2026
Merged via the queue into main with commit 3d8779d Sep 17, 2026
45 checks passed
@os-bill
os-bill deleted the claude/issue-16885-retire-navigation-view branch September 17, 2026 10:06
os-tesla pushed a commit to objectstack-ai/objectui that referenced this pull request Sep 20, 2026
… the published README (objectui#9987)

The README's record-navigation paragraph told an author to write the
member that `@objectstack/spec` retired under ADR-0049
(objectstack-ai/objectstack#18619) — and then, in the same paragraph,
explained the mechanism by which an undeclared key rejects the whole
config. The first sentence caused what the second diagnosed. This file
is listed in `files[]`, so the instruction shipped in the npm tarball.

The instruction is replaced by the route the retirement actually leaves
standing, carried in from `@object-ui/react`'s `useNavigationOverlay`
docblock: assign a `record` page to the object and let `isDefault` pick
the one that opens. Deleting the sentence alone was not enough — it
answered a real authoring question, and a reader who loses the answer
goes looking and finds the retired key elsewhere.

The second sentence is kept byte-identical (modulo re-wrapping): it is
the only place this README tells an author why a typo costs them the
whole block. It just stops being a description of what the paragraph
above it told them to do.

`readme-navigation-example.test.ts` now measures the PROSE as well as
the fence. The fence tests could never have caught this — they extract
the ```json block, and the block never carried the key. The pinned
`@objectstack/spec@17.4.0` still declares `view`, so nothing
schema-derived can say it is retired in this tree; it is named by hand,
exactly as `basePath` already is, and the absence carries its own
control: the same detector is run over the sentence that used to carry
the instruction and must find it there.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 28, 2026
… the published README (objectui#9987) (objectstack-ai#10090)

Fixes objectstack-ai#9987

Clause-②: no — a published README and the test that runs its example. It
moves no exported symbol and no key on a published payload.

## What the paragraph did

`packages/plugin-gantt/README.md` is listed in that package's `files[]`,
so it ships inside the npm tarball. Its record-navigation paragraph
**instructed** an author to write the member `@objectstack/spec` retired
under ADR-0049 (objectstack-ai/objectstack#18619) — and then, in the
same paragraph, explained the mechanism by which an undeclared key
rejects the whole config. The first sentence caused what the second
diagnosed, and a reader who stops at the instruction never reaches the
contradiction.

## Premise re-taken on today's `origin/main` before any edit

All three legs, each with its control lit in the same command:

1. **The two sentences are still there** — at
`packages/plugin-gantt/README.md` lines 54–62 on `0c2eb5eee` (the card
cited `1db82373f`; the text, not the line number, is the anchor). Both
quoted verbatim in the card matched byte for byte.
2. **`README.md` really is in `files[]`** — `["dist", "README.md",
"CHANGELOG.md", "LICENSE"]`. ⭐ Control in the same reading: `ROADMAP.md`
sits in the same directory, has the same extension, and is **not** in
`files[]` — so the instrument distinguishes shipped from not-shipped
rather than saying yes to every `.md` in the folder. `demo/`, `docs/`,
`scripts/`, `src/` and the two tsconfigs are likewise absent.
3. **The spec declares a tombstone, not a removal** — objectstack `main`
(`cdedfb348`) `packages/spec/src/ui/view.zod.ts:1971` reads `view:
retiredKey(NAVIGATION_VIEW_RETIRED)`, and the comment above it says why:
the key stays in the shape so `tsc` types it `never` and **a value
reaching a parse raises the prescription instead of a bare
unrecognized-key report**. That distinction is load-bearing for this
repair, and is why the surviving sentence below is not re-pointed at
`view`.

## The repair

The instruction is gone. In its place is the route the retirement
actually leaves standing, carried in from the carrier the card names —
`@object-ui/react`'s `useNavigationOverlay` docblock — in the README's
own voice, without restating the retirement's reasoning:

> *Which* detail layout opens is not authorable here either: assign a
`record` page to the object and let `isDefault` pick the one that opens.
Page assignment is what resolves a detail layout; this block only
decides **how** that detail is surfaced (`mode`, `size`) —
`@object-ui/react`'s `useNavigationOverlay` docblock owns that account.

⚠️ **Deleting the sentence was not the repair on its own.** It answered
a real authoring question, and a reader who loses the answer goes
looking and finds the retired key elsewhere. The replacement answers the
same question.

⚠️ **The second sentence is kept, byte-identical.** «an undeclared key
rejects the whole config, so the `mode` beside it never takes effect
either» is true and it is the only place this README tells an author why
a typo costs them the whole block. Only its line wrapping moved — proved
below. It now stands as the generic strictness warning it always was,
and stops being a description of what the paragraph above it told the
reader to do. ⛔ It is deliberately **not** re-pointed at `view`: after
the retirement `view` is a declared tombstone raising a prescription,
not an undeclared key, so aiming this sentence at it would describe the
failure wrongly.

⛔ **Neither neighbouring carrier is folded in.**
`packages/react/src/hooks/useNavigationOverlay.ts` line 31 spells the
same shape and the card checked it CLEAN — it is the tombstone's own
account of past behaviour, with the replacement stated four lines below
it. `packages/types/src/objectql.ts` is objectui#9938's site. Neither is
touched by this diff.

## The test is the other half of the deliverable

`packages/plugin-gantt/src/readme-navigation-example.test.ts` existed,
was green, and would have stayed green through this defect for a
structural reason worth stating: it extracts the `json` fence, and **the
fence never carried the key**. The instruction lived entirely in prose.
So the file now measures the prose as well.

⚠️ **Nothing schema-derived can say `view` is retired in this tree.**
The pinned `@objectstack/spec@17.4.0` still DECLARES it — measured, not
assumed: `NavigationConfigSchema`'s shape reads
`["mode","view","preventNavigation","openNewTab","size","width"]` and
`safeParse({ mode: 'page', view: 'summary_view' })` succeeds. So
`declaredMembers()` reports the key legal, and every schema-derived
statement about it would be vacuous here. It is therefore named by hand
— exactly as `basePath` already is in this same file — and the empty
result carries its own control:

- `does not INSTRUCT the retired detail-view key, in prose or fence` —
no code span in the section equals `view`, and `summary_view` appears
nowhere in it.
- `CONTROL: the same detector finds the key in the sentence that carried
it` — the same `codeSpans()` is run over the removed sentence, verbatim,
and must return `['view']`. If the detector ever stops firing there, the
clean section above says nothing at all.
- `still answers the question the retired instruction answered` — the
section must still name `record` and `isDefault`. These are identifiers,
not wording: the sentence may be rewritten freely as long as it still
hands the reader the route.

## Evidence

**RED before / GREEN after, file state verified in the SAME command as
each run:**

```
BEFORE   README blob 7461cf5 · vs HEAD: UNMODIFIED
         instruction grep -c 1 · summary_view 1 · isDefault 0
         Test Files 1 failed (1) · Tests 2 failed | 6 passed (8)
           × does not INSTRUCT the retired detail-view key, in prose or fence
               expected [ 'view' ] to deeply equal []
           × still answers the question the retired instruction answered
               expected [ 'record', 'isDefault' ] to deeply equal []
           ✓ CONTROL: the same detector finds the key in the sentence that carried it

AFTER    README blob 3695c7b · vs HEAD: MODIFIED-by-this-repair
         instruction grep -c 0 · summary_view 0 · isDefault 1
         Test Files 1 passed (1) · Tests 8 passed (8)
```

The control test passed on **both** runs: the detector was lit before
the repair and is still lit after, so the zero is a measurement and not
a broken instrument.

**The kept sentence is byte-identical, with its own control.**
Whitespace-normalised, the substring from `` `navigation` is the spec ``
to `owns the member list.` compares EQUAL between
`HEAD:packages/plugin-gantt/README.md` and the worktree — while the same
instrument applied to the half that *should* have changed reports
DIFFERENT. And the same fixed-string grep that finds the removed
instruction (`` use the declared `view` member ``) once in the file at
HEAD finds it zero times in the repaired file.

**No ablation was run, and that is a statement rather than an
omission.** Nothing in this diff is a guard whose failure mode needs
proving by mutation — the RED-before run already is the "can this fail"
leg, taken against the real published text rather than an injected one,
and it failed for exactly the two reasons named.

## Gates — re-run as the LAST action before the push

Tree verified equal to the pushed commit `9ef577d38`, `git status
--porcelain` empty, at the time of the run. Exit codes captured before
any pipe.

Gates that **do** select a package README (established by reading each
script's own surface declaration, ⛔ not assumed):

| gate | exit |
|---|---|
| `check-doc-fence-languages.mjs` | 0 |
| `check-doc-snippet-types.mjs` | 0 — 672/672 blocks judged |
| `check-doc-component-types.mjs` | 0 |
| `check-doc-example-types.mjs` | 0 |
| `check-readme-exports.mjs` | 0 — 37 of 40 packages read, 544
self-imports judged, population NOT collapsed |
| `check-doc-links.mjs` | 0 — its `SCAN_ROOTS` carries
`packages/*/README.md` |
| `check-doc-expression-carriage.mjs` | 0 |
| `check-doc-example-shared-reader.mjs` | 0 |

⚠️ `check-readme-exports.mjs` FAILS by design on an unbuilt tree
(`population COLLAPSED -- this run proves nothing`). The green above is
from a run after all 39 package `dist/` trees were built; the collapsed
run is not being reported as a pass.

⛔ `check-doc-example-ids.mjs` does **not** select package READMEs — its
own header rules the population to `content/docs/**` on purpose — so it
is not in the list above.

Everything else run on the same tree: `check-changeset-presence` 0,
`check-changeset-claims` 0, `check-pending-changeset-literals` 0,
`check-changeset-fixed` 0, `check-changeset-no-major` 0,
`check-control-bytes` 0, `check-new-cross-file-line-citations` 0,
`check-test-path-roots` 0, `check-unreferenced-sources` 0, `pnpm
--filter @object-ui/plugin-gantt lint` 0 (0 errors, pre-existing
warnings only), `pnpm --filter @object-ui/plugin-gantt type-check` 0,
and the package suite from the repo root — `pnpm exec vitest run
packages/plugin-gantt/` — **69 files, 541 tests, all passing**.

Governed-surface guard, asked rather than assumed:
`check-governed-queue-guard.mjs --test` over the three changed paths
returns **NOT GOVERNED**, with `AGENTS.md` run through the same command
as a lit control returning the governed verdict.

## Changeset — decided by running the gate, not from memory

`check-changeset-presence.mjs` demanded one, and the file it named is
the **test under `src/`**, not the README: that gate subtracts `*.md`
from published source on purpose («published-but-not-code sits with the
READMEs»), and its header answers a change confined to tests with the
empty-frontmatter exemption in one line. So
`.changeset/9987-gantt-readme-navigation-view.md` carries an EMPTY
frontmatter and says so in prose, including the part that argues the
other way — the README genuinely is in the tarball. No runtime
behaviour, no export and no published-contract field moved, and every
package here is in one fixed group, so the corrected README reaches npm
with the next release either way. ⭐ A reviewer who wants this to carry a
`patch` can flip it in one line; the reasoning is written down rather
than assumed.

## Acceptance notes

- **noted, not filed** —
`packages/react/src/hooks/useNavigationOverlay.ts` says the key «was
removed in `@objectstack/spec` 17.5.0» while objectstack `main` keeps it
as a `retiredKey` tombstone. Checked and it is **not** a defect: the
spec's own tombstone string opens with those exact words, so the
docblock is quoting its source faithfully. Recorded only so nobody
re-reads it as drift. Carrier for the retire-vs-remove distinction in
this repo: objectui#9684, whose PR is this tree's HEAD commit.
- **noted, not filed** — no other published carrier in this repo
instructs the retired key. `summary_view` across the tracked tree
returns 20 hits and every one of them is a test fixture, a CHANGELOG
entry or the retirement's own account (`packages/react`,
`packages/types`, `packages/plugin-grid`, `.changeset/9874-…`) — all
owned by objectui#9667 / objectstack-ai#9938 / objectstack-ai#10080 / objectstack-ai#9874. Zero hits under
`content/docs`, zero in any other `packages/*/README.md`, zero in the
root `README.md`; the 20 hits elsewhere are the lit control for that
zero. No new card is owed.
- Nothing else in the diff's blast radius was touched. The two carriers
the card fenced out stay fenced out.

---
_Generated by [Claude
Code](https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq)_

Co-authored-by: Claude <noreply@anthropic.com>
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 domain:spec priority:p2 Medium: important, M3 protocol:ui size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

List view navigation.view is declared in spec but resolves no form view — its only read lands it in the onNavigate navigation-MODE argument

2 participants