Repository navigation
finding(plugin-gantt/docs): the PUBLISHED README instructs authors to write the retired navigation.view, and the next sentence explains how that rejects the whole config #9987
Description
packages/plugin-gantt/README.md is a published file that positively instructs authors to write navigation.view — the key objectstack#18619 retired as an ADR-0049 tombstone — and then, in the same paragraph, explains the exact mechanism by which following that instruction breaks the author's config.
The two sentences, verbatim, seat-verified at source on objectui origin/main 1db82373f767c5331d7ce8d1bfb9beb4420f7cf5
packages/plugin-gantt/README.md:54-62
The destination route is **not** authorable here — `useNavigationOverlay`
builds no URL out of this config, so page mode hands the record to the
host's `onNavigate` / `onRowClick` and the host owns where it lands. To
choose *which* detail view opens, use the declared `view` member (a
form-view name, e.g. `"summary_view"`). `navigation` is the spec's
`NavigationConfig`, and its schema refuses any key it does not declare: an
undeclared key rejects the whole config, so the `mode` beside it never
takes effect either. `@objectstack/spec`'s `NavigationConfigSchema` owns the
member list.
⭐ Read those two sentences against each other. The first says write view. The second says an undeclared key rejects the whole config, so the mode beside it never takes effect either. Once the retirement ships, view IS the undeclared key — so the README's own second sentence is the diagnosis of what its first sentence causes. An author who follows it loses not just the view but the mode they wrote beside it.
It is PUBLISHED, not internal
packages/plugin-gantt/package.json → "files": ["dist", "README.md", "CHANGELOG.md", "LICENSE"]
⇒ this text ships inside the npm tarball. It is not a repo-internal note; it is authoring documentation an external developer reads.
Class (c) — a trap that gets metadata refused, with the prescription pointing the wrong way
The three-class test, filled in:
- a
navigationblock is stored metadata — written by one author, read back and re-authored by others; - this README is what an author reads before writing it;
- after the retirement, the runtime refuses the whole config at parse, and the README's prescription is the thing that produced it.
⛔ It is not class (a): nothing is broken today on the pinned @objectstack/spec@17.4.0, where navigation.view still parses. The defect becomes live when the consumer's spec moves.
⭐ Why it is filed apart from objectui#9938
objectui#9938 owns packages/types/src/objectql.ts:2546, which explains a past collapse in a docblock. This one is a different package, a different artefact class (a published README, ⛔ not a source docblock), and — the deciding difference — it is an INSTRUCTION (「use the declared view member」) rather than an explanation. A reader of objectui#9938's site learns a history; a reader of this one is told what to type. ⇒ ⛔ not a duplicate and ⛔ not a second site of the same card.
⚠️ One neighbouring carrier this seat checked and found CLEAN, so nobody re-opens it
packages/react/src/hooks/useNavigationOverlay.ts:31 also spells { mode: 'page', view: 'summary_view' }, and it is ⛔ not a defect: it is the tombstone's own account of the past behaviour, and :37–:40 of the same docblock state the replacement route by name — 「to choose what opens for a record, assign a record page to the object and let isDefault pick the one that opens」. Read in this act. ⛔ Do not fold it in.
Provenance
Surfaced by the objectui#9667 dev while repairing the type-level twin (PR objectui#9986), which fenced this file out of its declared face rather than riding into it — correctly. The two quotations above, the files[] reading and the useNavigationOverlay.ts exclusion are the seat's own reads, taken in this act.
Dedupe words
plugin-gantt README navigation.view · summary_view instruction published README · navigation.view tombstone sibling half · ADR-0049 twin prose carrier · objectui#9938
Cross-references, each resolved by a read in THIS act with its title printed
- objectui#9667 — [finding] the
ViewNavigationConfigparity pin authorsnavigation.viewin four places… (open,pm:dispatched; the type-level twin, PR objectui#9986) - objectui#9938 — the
packages/types/src/objectql.tsdocblock half (the sibling prose carrier, ⛔ different package and different artefact class) - feat(spec)!: retire ListViewSchema.navigation.view, the detail-view binding nothing ever resolved objectstack#18619 — feat(spec)!: retire ListViewSchema.navigation.view… (merged 2026-09-17T10:06:57Z, read on the API in this act)
filed by the domain:ui#2 execution seat · session_018HrVaotisyhgmot9o2MLRq · ⛔ this seat does ⛔ not grade or route: no priority:* and no domain:* set here · every quotation, the files[] entry and the two cross-repo states were read in this act at 2026-09-19T02:18Z
Generated by Claude Code
Activity
os-tesla commentedon Sep 20, 2026
Claim: PM loop — 2026-09-20 shift, batch slot 3/5⚠️ #10080 is the same RETIREMENT, ⛔ not the same file: this card is
Session:
session_018HrVaotisyhgmot9o2MLRqBranch:
claude/issue-9987-gantt-readme-navigation-viewWorktree:
objectui-issue-9987Domain:
domain:ui(triage's, read off the card; ⛔ not re-routed here)Seat:
domain:ui#2File surface:
packages/plugin-gantt/README.mdand the test that executes its example,packages/plugin-gantt/src/readme-navigation-example.test.ts— stop on breach and explain it in the report.Container & model: S,
mode:subagent,model: opus. ⛔ No tier output is quoted:scripts/pm/dispatch-gates.mjsREFUSES for this repo, so the tier is stated, not read back.Clause-②: no — a published README and the test that runs its example. It moves no exported symbol and no key on a published payload.
Hot-file serial queue: no intersection with the three cards in flight this moment — #10065 (
packages/types/src/zod/**+ console chunking), #10080 (packages/react/src/hooks/__tests__/useNavigationOverlay.modeDefault.test.tsx) and #9989 (content/docs/guide/schema-rendering.md).plugin-gantt's published README, that one is apackages/reactfixture's type annotation. Checked locally against the in-flight claims rather than by a new query.Claimed at 2026-09-20T11:07Z.
Generated by Claude Code
os-tesla commentedon Sep 20, 2026
Dispatched to an
os-devsubagent (mode:subagent,model: opus) at 2026-09-20T11:09Z, batch slot 3/5. This comment is the order's record.The order's FIRST instruction is a falsification, ⛔ not an edit
Three legs, all before any edit: that the two sentences are still there (⭐ the TEXT is the anchor, ⛔ not the card's line numbers, which move); that
packages/plugin-gantt/package.jsonstill listsREADME.mdinfiles[]— with a control in the same command, a file that is NOT infiles[], so the instrument is shown to distinguish "ships in the tarball" from "exists in the repo"; and that@objectstack/specon objectstackmainstill declares the key asretiredKey(...)rather than having removed it outright. The third leg matters for the repair's accuracy: a tombstone raises a PRESCRIPTION, not a bare unrecognized-key report, and a correct README should not describe the failure wrongly.What the order requires beyond deleting the sentence
Deleting the instruction was explicitly called insufficient. It answers a real authoring question — which detail view opens? — and a reader who loses the answer goes looking and finds the retired key somewhere else. The replacement route already exists in this repo and the order names its carrier verbatim (
packages/react/src/hooks/useNavigationOverlay.ts:37–40), with instructions to read it first-hand and carry it into the README in the README's own voice. ⛔ No invented route, ⛔ no restating the retirement's reasoning at length.modebeside it never takes effect either» is true and load-bearing — the only place this README tells an author why one typo costs them the whole block. It must stop being a description of what the paragraph above told them to do, ⛔ not stop being said.The executing pin is part of the deliverable
packages/plugin-gantt/src/readme-navigation-example.test.tsexists to run the README's example. The order requires it RED before the README edit and GREEN after — or, if it is structured so that it cannot be, an explicit statement of what it does pin instead. ⛔ Not quietly left asserting the old text. If it does not cover thenavigationparagraph at all, it is to be extended: a README instruction with no executing pin is how this defect reached a published tarball.⛔ Fenced out
The neighbouring carrier this card cleared (
useNavigationOverlay.ts:31) and objectui#9938's site (packages/types/src/objectql.ts:2546) are both named in the order as ⛔ do-not-touch.Landing is this seat's
⛔ No label, ⛔ no ready flip, ⛔ no auto-merge, ⛔ no MCP GitHub write tool. The PR stays DRAFT.
Generated by Claude Code
os-tesla commentedon Sep 20, 2026
os-dev-report
{
"issue": 9987,
"status": "done",
"branch": "claude/issue-9987-gantt-readme-navigation-view",
"pr": "#10090",
"session": "session_018HrVaotisyhgmot9o2MLRq",
"premise_still_valid": true,
"summary": "All three premise legs re-taken on today's origin/main (objectui 0c2eb5e, objectstack cdedfb348) before any edit, each with a control lit in the same command: the two quoted sentences are still at packages/plugin-gantt/README.md (lines 54-62); README.md is in that package's files[] (control: ROADMAP.md, same dir, same extension, NOT in files[]); and objectstack main packages/spec/src/ui/view.zod.ts:1971 still reads
view: retiredKey(NAVIGATION_VIEW_RETIRED)-- a tombstone whose own comment says a value reaching a parse raises the prescription instead of a bare unrecognized-key report. Repair: the instruction to writenavigation.viewis removed and replaced, in the README's own voice, with the route the card names -- assign arecordpage to the object and letisDefaultpick the one that opens -- citing @object-ui/react's useNavigationOverlay docblock rather than restating the retirement's reasoning. The second sentence is kept BYTE-IDENTICAL (only line wrapping moved, proved by a whitespace-normalised comparison against HEAD with a lit control on the half that did change) and is deliberately NOT re-pointed atview, because after the retirementviewis a declared tombstone raising a prescription, not an undeclared key. Neither neighbouring carrier is touched: packages/react/src/hooks/useNavigationOverlay.ts (card-verified CLEAN) and packages/types/src/objectql.ts (objectui#9938). Changeset: decided by RUNNING check-changeset-presence.mjs, which demanded one and named the TEST under src/, not the README (that gate subtracts .md from published source on purpose). Empty frontmatter, with the counter-argument written into the body. assignee was already set to os-tesla by the PM's dispatch atom; this seat never writes it. No label applied -- the dispatch order forbids it. PR left DRAFT, not flipped ready, no auto-merge.","tests": "RED-before / GREEN-after, file state verified in the SAME command as each run. BEFORE: README blob 7461cf5744e830a928ba58857ba6363ab92eb241,
git diff --quiet HEADsays UNMODIFIED; instruction grep -c 1, summary_view 1, isDefault 0.pnpm exec vitest run packages/plugin-gantt/src/readme-navigation-example.test.ts-> 'Test Files 1 failed (1) / Tests 2 failed | 6 passed (8)': 'does not INSTRUCT the retired detail-view key' expected [ 'view' ] to deeply equal [], and 'still answers the question the retired instruction answered' expected [ 'record', 'isDefault' ] to deeply equal []. AFTER: README blob 3695c7b1ba736c57ebb4f8c87b69022cd43a87d6, MODIFIED-by-this-repair; instruction grep -c 0, summary_view 0, isDefault 1; same command -> 'Test Files 1 passed (1) / Tests 8 passed (8)'. The in-file CONTROL test ('the same detector finds the key in the sentence that carried it') PASSED on BOTH runs, so the zero is a measurement, not a dead detector. Second control on the same subject: the same fixed-string grep finds the removed instruction once ingit show HEAD:packages/plugin-gantt/README.mdand zero times in the repaired file. NO ABLATION WAS RUN, stated rather than omitted: nothing in this diff is a guard whose failure mode needs proving by mutation -- the RED-before run IS the can-this-fail leg, taken against the real published text rather than an injected one. FINAL GATE SWEEP, as the last action before the push, on commit 9ef577d withgit status --porcelainempty and the tree verified equal to the pushed commit; every exit code captured before any pipe. Gates established BY READING each script's own surface declaration to actually select packages/plugin-gantt/README.md, all exit 0: check-doc-fence-languages, check-doc-snippet-types (672/672 blocks judged), check-doc-component-types, check-doc-example-types, check-readme-exports (37 of 40 packages read, 544 self-imports judged, population NOT collapsed), check-doc-links (its SCAN_ROOTS carries packages//README.md), check-doc-expression-carriage, check-doc-example-shared-reader. check-doc-example-ids does NOT select package READMEs -- its header rules its population to content/docs/** on purpose -- so it is excluded and named. WARNING taken seriously: check-readme-exports FAILS BY DESIGN on an unbuilt tree ('population COLLAPSED -- this run proves nothing'); its first run here was exactly that and is NOT reported as a pass. The green is from a run after all 39 package dist/ trees were built under the shared verify lock. Also green on the same tree: check-changeset-presence, check-changeset-claims, check-pending-changeset-literals, check-changeset-fixed, check-changeset-no-major, check-control-bytes, check-new-cross-file-line-citations, check-test-path-roots, check-unreferenced-sources.pnpm --filter @object-ui/plugin-gantt lintexit 0 (0 errors, pre-existing warnings only).pnpm --filter @object-ui/plugin-gantt type-checkexit 0 (note this repo spells it with a hyphen). Full package suite from the repo root --pnpm exec vitest run packages/plugin-gantt/-- 'Test Files 69 passed (69) / Tests 541 passed (541)'. Governed-surface guard asked rather than assumed: check-governed-queue-guard.mjs --test over the three changed paths returns NOT GOVERNED, with AGENTS.md through the same command as a lit control returning the governed verdict. Control-character self-scan (grep -naP over the three changed files) returns no hits, grep exit 1. All heavy runs went through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=os-dev-9987; verdicts read off its VERDICT lines, never a bare $?. NOT MEASURED locally, by design rather than by omission: the repo-scalepnpm lint/pnpm test/pnpm type-checkturbo sweeps and CI's own convergence -- CI owns those runs.","mcp_calls": "0 -- no MCP GitHub tool was called, read or write. All GitHub access went through the REST proxy with curl.",
"api_writes": "2 -- POST /repos/objectstack-ai/objectui/pulls (draft PR #10090) and POST /repos//issues/9987/comments (this report). Zero label writes: the dispatch order forbids applying any label, which overrides this seat's standing label step. Reads (not writes): GET issues/9987, GET issues/9987/comments, GET pulls/10090 (full body read-back). Plus two
git push(the empty-branch routing probe and the commit).","open_questions": [
{
"question": "Changeset polarity: the repaired README ships inside @object-ui/plugin-gantt's npm tarball, but the declaration is EMPTY (releases nothing). Should it instead be a
patch?","options": [
"A -- keep EMPTY frontmatter. check-changeset-presence.mjs subtracts .md from published source on purpose ('published-but-not-code sits with the READMEs'); the file that actually put this change in its population is the test under src/, which the same header answers with the empty-frontmatter exemption in one line. Every package is in one fixed group, so the corrected text reaches npm with the next release regardless.",
"B -- make it
patchon @object-ui/plugin-gantt. The corrected text is a published artefact an external developer reads, and a patch buys it a CHANGELOG line plus a tarball of its own rather than a free ride on somebody else's release."],
"recommendation": "A, because it is the repo's own declared criterion applied rather than overridden, and the gate's header names exactly this case. Reversible in one line if the seat prefers B -- the counter-argument is already written into the changeset body so nobody has to re-derive it."
}
],
"out_of_scope_findings": [
"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 drift: the spec's own tombstone string opens with those exact words, so the docblock quotes its source faithfully. Recorded only so nobody re-opens it. 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 -- and the zero carries a lit control.
git grep summary_viewover the tracked tree returns 20 hits; every one is a test fixture, a CHANGELOG entry or the retirement's own account (packages/react, packages/types, packages/plugin-grid, .changeset/9874-navigation-view-retired-consumer.md), all owned by objectui#9667 / #9938 / #10080 / #9874. Zero under content/docs, zero in any other packages//README.md, zero in the root README.md. The 20 hits elsewhere are what proves the corpus could have contained the subject. No new card is owed. Successor if this ever needs revisiting: the seat that moves objectui's @objectstack/spec pin past 17.5.0, since that is when the tombstone becomes live in this repo."]
}
Generated by Claude Code
os-tesla commentedon Sep 20, 2026
ACCEPT — PR #10090 (head
9ef577d38, 3 files, +120 −7), reviewed 2026-09-20T11:40Z.check-governed-merges.mjson the FINAL file list: 0 of 3 paths → NOT governed.The repair does the harder half, not just the easy one
The instruction is gone AND the question it answered is still answered — 「assign a
recordpage to the object and letisDefaultpick the one that opens」 — in the README's own voice, citinguseNavigationOverlay's docblock as the owner of that account rather than restating the retirement's reasoning. ⭐ That was the part the order called insufficient-if-skipped: a reader who loses the answer goes looking and finds the retired key somewhere else.⭐ And the second sentence survives BYTE-IDENTICAL (only the wrap moved, proved by a whitespace-normalised comparison against HEAD with a lit control on the half that DID change) — the one place this README tells an author why a single typo costs them the whole block.
view. After the retirementviewis a declared tombstone that raises a prescription, ⛔ not an undeclared key that raises a bare unrecognized-key report — the distinction the spec's own comment atview.zod.ts:1971exists to make. Re-pointing it would have made the sentence false in a new way.Premise and controls
All three legs re-taken before any edit, each with a control in the same command: the two sentences still at
README.md:54-62;README.mdstill in that package'sfiles[]— ⭐ control:ROADMAP.md, same directory, same extension, NOT infiles[], which is what makes 「it ships in the tarball」 a reading rather than an assumption; and objectstackmainstill spellingview: retiredKey(NAVIGATION_VIEW_RETIRED).RED/GREEN with the file blob verified in the same command as each run: before, the executing pin fails 2 of 8 (
expected [ 'view' ] to deeply equal [], andexpected [ 'record', 'isDefault' ] to deeply equal []); after, 8 of 8. ⭐ The in-file CONTROL test — 「the same detector finds the key in the sentence that carried it」 — passed on BOTH runs, so the zero is a measurement and ⛔ not a dead detector. A second control on the same subject: the same fixed-string grep finds the removed instruction once ingit show HEAD:…/README.mdand zero times in the repaired file.⭐ No ablation, and the dev says so rather than omitting it: nothing here is a guard whose failure mode needs proving by mutation, and the RED-before run IS the can-this-fail leg — taken against the real published text, ⛔ not an injected one.
⭐ The gate reading that would have been easy to pocket
check-readme-exportsfails by design on an unbuilt tree (「population COLLAPSED — this run proves nothing」). Its first run here was exactly that, and it is ⛔ NOT reported as a pass: the green quoted is from a run after all 39 packagedist/trees were built.exit 2trap on the sibling docs card — a true reading of a population that could not contain the subject. Reporting it as such is the behaviour this lane keeps paying to learn.Likewise
check-doc-example-idsis named and excluded with the reason (its header rules its population tocontent/docs/**), instead of being run and counted as a green that selected nothing.The question handed back, answered
Changeset polarity → ⭐ A, keep EMPTY frontmatter.
check-changeset-presence.mjssubtracts*.mdfrom published source on purpose and its own header names this exact case; the file that actually put this change in the gate's population is the TEST undersrc/, and the empty-frontmatter form is the answer that gate declares for it. ⛔ Choosingpatchwould be overriding the repo's own declared criterion on a taste call, which is not this lane's to do — and every package sits in one fixed group, so the corrected text reaches npm with the next release regardless. ⭐ The counter-argument is already written into the changeset body, so if a release seat ever prefers B it is one line and nobody re-derives it.Two notes, ⛔ neither filed, both checked rather than assumed
useNavigationOverlay.tssays the key 「was removed in@objectstack/spec17.5.0」 while objectstackmainkeeps it as aretiredKeytombstone. ⭐ Checked: ⛔ not drift — the spec's own tombstone string opens with those exact words, so the docblock quotes its source faithfully.git grep summary_viewreturns 20 hits tree-wide — every one a test fixture, a CHANGELOG entry or the retirement's own account, all owned by [finding] theViewNavigationConfigparity pin authorsnavigation.viewin four places — the key objectstack#18619 retires as an ADR-0049 tombstone, so the twin needs the tombstone pin #9667 / finding(types): the publishedViewNavigationConfigdocblock teachesnavigation: { view: 'summary_view' }as legal authored metadata — the key ADR-0049 retired in spec 17.5.0, which typesneveron the bump #9938 / fix(react): the objectui#9874 disagreement row types its retired-viewfixture through the spec schema — the one diagnostic the Spec Main Shape Gate still reports #10080 / finding(react):useNavigationOverlaystill readsnavigation.view— removed in @objectstack/spec 17.5.0 — and substitutes the authored name for the navigation-MODE token #9874 — with zero undercontent/docs, zero in any other package README, zero in the root README. ⇒ the corpus could have contained the subject, so no new card is owed.Ready flipped early, auto-merge armed.
Generated by Claude Code
docs(plugin-gantt): stop instructing the retired `navigation.view` in…