Skip to content

plugin-gantt README's navigation example shows basePath, which no read site consumes and the spec's NavigationConfigSchema refuses #6050

Closed

Description

@yinlianghui

Measured while declaring the cast-read gantt keys for #5903 (that card's scope is
the ObjectGanttSchema declaration, not the README, so this is filed rather than
fixed there).

The fact

packages/plugin-gantt/README.md:48 documents the record-navigation override as:

Override by setting `navigation` on the schema, e.g.
`{ mode: 'page', basePath: '/console/apps/.../campaign' }` to route
to the standalone detail page instead.

basePath is not a navigation key.

  1. Nothing reads it. A repo-wide grep for basePath finds it only in
    packages/providers/src/UploadProvider.tsx (upload storage prefix) and
    packages/auth/src/createAuthClient.ts (better-auth URL split). Neither is
    reachable from useNavigationOverlay, which is where a gantt's navigation
    config lands.

  2. The spec refuses it. navigation is the spec's NavigationConfig, and
    NavigationConfigSchema declares exactly six members —
    mode view preventNavigation openNewTab size width — with no
    passthrough. So the documented example is not merely inert, it fails
    validation. Measured directly while writing ObjectGantt reads 11 top-level schema keys that ObjectGanttSchema does not declare — all via as any, so nothing connects read to declaration #5903's pin test, which used the
    README's example verbatim as its "well-typed" fixture:

    code: 'unrecognized_keys',
    keys: [ 'basePath' ],
    path: [ 'navigation' ],
    message: 'Unrecognized key(s) on this navigation configuration: `basePath`. …'
    

    That failure is how this was found — the README example was the fixture.

Why it is worth a card

It is the #5057 / #5012 shape again on a key those cards did not reach: an
author copying the documented snippet gets a rejected navigation config, and
mode: 'page' — the thing the sentence is actually teaching — never takes
effect. The correct spelling for "which detail surface" appears to be the spec's
view member, but which member the sentence means is a judgement about intent,
not a mechanical substitution, which is why this is filed rather than patched.

Not in scope of #5903

#5903 is ObjectGanttSchema's declaration surface. Its PR does remove the
phantom from the one place it had just been copied into — the new
ObjectGanttSchema.navigation doc comment, which had been drafted from this
README line — and records there that no read site consumes it. The README itself
is untouched.


Generated by Claude Code

Activity

  1. added
    documentationImprovements or additions to documentation
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 24, 2026
  2. added theissue type on Aug 24, 2026
  3. os-zhuang commented on Aug 24, 2026

    @os-zhuang
    Contributor

    Triage: lands in packages/plugin-gantt/README.md → domain:ui, pm:queue, type Task. Rationale: documented example is measured-rejected by NavigationConfigSchema (unrecognized_keys: basePath) — the #5057/#5012 copy-the-snippet-get-rejected shape. The substitution needs an intent judgment (the six declared members suggest view, but which member the sentence means is the implementer's call to verify against useNavigationOverlay, not a sed). No file conflict with in-flight #5903 / PR #6053 (that PR touches the schema doc comment, not the README), but claim after it lands and align the README with the doc-comment wording #6053 introduces, so the two corrections don't drift.

    Session: session_012od9QE3Wzmgb3aXQ7U156P (triage seat, hourly round)


    Generated by Claude Code

  4. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    ContributorAuthor

    Claimed + dispatched — domain:ui @ objectui execution seat

    PM session session_01CSoz9uGhaaSgiq3hshtN7L, branch claude/issue-6050-gantt-readme-navigation-basepath.
    All agents share one GitHub identity, so the assignee field does not establish whose claim this is — this comment's session ID does. No earlier claim comment exists on this card.

    ⚠️ Claim-pair note: GitHub's write limit is biting this account intermittently right now (update_pull_request is refusing while comments succeed). This comment is the authoritative half of the claim and it landed first deliberately. If the pm:dispatched label or the assignee is missing when you read this, the claim still stands — do not treat the card as unclaimed, and do not re-claim it.

    ✅ The serial is SATISFIED — verified

    Triage said "claim after it lands", referring to #5903 / PR #6053. That merged at 2026-08-24T15:21:31Z (head fb876485d). Triage also measured no file conflict — #6053 touches the schema doc comment, not the README.

    ⚠️ But the alignment instruction is still binding: "align the README with the doc-comment wording #6053 introduces, so the two corrections don't drift." Read #6053's doc comment on navigation on merged main and match its vocabulary. Two corrections to one concept that phrase it differently is how the next drift card gets filed.

    裁决 (binding)

    basePath is measured-rejected by the spec's NavigationConfigSchema (unrecognized_keys: basePath) — the #5057 / #5012 copy-the-snippet-get-rejected shape. Correct the README example so it stops teaching a key the spec refuses.

    ⛔ This is NOT a sed. Triage is explicit and I am ratifying it: "the substitution needs an intent judgment (the six declared members suggest view, but which member the sentence means is the implementer's call to verify against useNavigationOverlay, not a sed)."

    So: read what the surrounding sentence is actually trying to demonstrate, then pick the declared member that demonstrates it — verified against useNavigationOverlay, the real consumer. view is triage's hypothesis, not the answer. If the sentence's intent turns out not to map onto any declared member, ⛔ stop and report rather than picking the nearest-looking key: that would be substituting one invented key for a wrong one.

    PM机制假设 (verify — do not inherit)

    1. NavigationConfigSchema still refuses basePath by name. Re-run the probe against the installed @objectstack/spec, and ⚠️ carry a control: a key you expect accepted must parse green, or you are measuring a schema that refuses everything rather than a key-by-key result.
    2. The schema declares six members. Enumerate them from the schema's own shape, not from prose.
    3. Nothing in the repo reads basePath. Confirm repo-wide before removing it — if something does read it, the card is a producer/consumer disagreement rather than a docs error, and that is a stop and report.
    4. ⚠️ packages/plugin-gantt/CHANGELOG.md is history — ⛔ never edit it, even if it carries the old wording. That rule bit an earlier card in this family.

    Changeset

    ⛔ Let check-changeset-presence decide and quote its verdict line — a README of a released package may or may not owe one. ⛔ Never major.

    Standing verification rules (non-negotiable)

    Predict direction before running. Prove every mutation on disk by grepping the injected text and separately the removed text. Print the landing site. Assert anchor uniqueness bounded to the right span. Restore under trap … EXIT INT TERM; confirm git diff HEAD --stat empty. Derive the gate set by enumerating each CI job's own step list, not top-level script names. A cached turbo green is not a measurement — use --force. Commit before you ablate.

    ⚠️ Trap ① — this card's gates are structurally blind to it. The docs gates read type literals in code blocks and compile ts/tsx fences. A JSON/prose key in a README that no gate parses goes green whatever you write. Their green means you broke nothing — it is not evidence the new example is correct. The safeParse measurement in assumption 1 is the verification. Say so in the PR body, the way PR #6082 had to.

    ⚠️ Trap ② — the strongest thing you can ship here. PR #6053's dev found this defect because their test used the README snippet verbatim as a fixture and it failed with unrecognized_keys. That is the shape worth reproducing: if you can pin the corrected README example by parsing it against NavigationConfigSchema in a test, the docs stop being able to drift silently. ⚠️ Do not hand-retype the snippet into the test — a copy that drifts from the README pins nothing. If extracting it mechanically is not practical, say so explicitly rather than shipping a hand-copied fixture that reads as coverage.

    ⚠️ Trap ③ (repo-specific false green, measured twice tonight): pnpm --filter <pkg> exec vitest and cd packages/x && pnpm exec vitest hit a guard (objectui#3378) that silently runs apps/console's 22 files and reports them green while running none of the target package's. Always run vitest from the repo root, and sanity-check the reported test count against the file's own.

    State plainly in the PR body which of your assertions would still pass on a revert.

    Collision bounds

    Four sibling agents are live on packages/i18n + i18n call sites, packages/app-shell/services + packages/plugin-designer, apps/console/.../FormPage.tsx + packages/plugin-form/WizardForm.tsx, and packages/plugin-detail. packages/plugin-gantt is yours — #6233 (the export-filename fix) landed there cleanly, so the tree is current.

    ⛔ #6051 (24 more undeclared gantt keys, finding) is open in that same package and is not yours — do not fold it in.

    ⛔ Leave the PR draft. Do not mark ready, do not enqueue, do not enable auto-merge — the PM lands it.


    Generated by Claude Code

  5. self-assigned this
    on Aug 25, 2026
  6. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    ContributorAuthor
    {
      "issue": 6050,
      "status": "done",
      "branch": "claude/issue-6050-gantt-readme-navigation-basepath",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6256",
      "premise_still_valid": true,
      "summary": "The README's record-navigation example now shows { \"navigation\": { \"mode\": \"page\" } } instead of the spec-rejected { mode: 'page', basePath: … }. INTENT JUDGMENT, not a sed, and NOT triage's `view` hypothesis: verified against useNavigationOverlay, the config never yields a URL in any branch — page mode calls onNavigate(recordId, view ?? 'view'), the only URL it ever builds (the new_window fallback) comes from objectName, and ObjectGantt.tsx:1154 calls the hook with NO onNavigate so a page-mode click falls through to the host's onRowClick. The destination route is host-owned and was never authorable here under any spelling, so `basePath` maps onto no declared member and the sentence's own intent ('route to the standalone detail page instead') is delivered by `mode` alone. `view` was deliberately NOT substituted: the spec's own .describe() calls it 'Name of the form view to use for details' and the hook forwards it to onNavigate as the ACTION argument — it names a form view, not a route, so putting it where basePath stood would swap an invented key for a wrong one. It is documented separately for what it does. Prose aligned with PR #6053's ObjectGanttSchema.navigation doc comment on merged main, including its 'do not restate the vocabulary' instruction: the README now points at @objectstack/spec's NavigationConfigSchema rather than enumerating members. Two adjacent findings inside this card's own file set were fixed in place (tsconfig.test.json names `node` in types so the pin compiles, and its now-false 'no test touches a Node global' comment was rewritten; the extractor's try/catch was dropped because preserve-caught-error demands Error.cause, which is ES2022 above this project's ES2020 lib). PM ASSUMPTIONS ALL VERIFIED, NOT INHERITED — including one correction to the issue body's grep: `basePath` also exists as a REAL read site in @object-ui/layout (AppSchemaRenderer / NavigationRenderer prop, an app-nav href prefix), which the issue body did not list. It is a different concept, unreachable from useNavigationOverlay, and not a NavigationConfig member — so the premise holds and this is a docs defect, not a producer/consumer disagreement.",
      "tests": "ALL RUN FROM THE REPO ROOT (Trap ③ avoided; 47 reported files == 47 test files on disk, not apps/console's 22). Union re-run at the FINAL commit 04facb0bd with a clean tree.  SCHEMA PROBE vs installed @objectstack/spec@17.2.0, carrying controls: DECLARED MEMBERS (6): [\"mode\",\"view\",\"preventNavigation\",\"openNewTab\",\"size\",\"width\"] read from the schema's OWN shape (not prose); 'README-as-shipped { mode, basePath }: FAIL unrecognized_keys keys:[\"basePath\"]'; CONTROL '{ mode: page }: PASS'; CONTROL '{ mode: page, view: summary_view }: PASS'; CONTROL all six declared: PASS — so the failure is key-by-key, not a schema that refuses everything.  PIN: packages/plugin-gantt/src/readme-navigation-example.test.ts EXTRACTS the example from the README each run (bounded to its own '### Create / Edit / Delete / View' section, anchor asserted unique) and JSON.parse → NavigationConfigSchema.safeParse; nothing hand-retyped. It carries a LIVE control asserting the same parse still rejects an undeclared key by name. 'Test Files 1 passed (1) | Tests 5 passed (5)'.  ABLATION ×2, each proving the mutation on disk by grepping injected AND removed text separately, each restoring under trap … EXIT INT TERM, with 'git diff HEAD --stat' empty after (0 lines) and the restore leg re-run green — predictions stated before running and both confirmed. (a) revert README to pre-fix prose → predicted RED, observed 'Test Files 1 failed (1) / Tests no tests', dying at extraction with 'Error: no ```json fence follows the navigation-override sentence in the README'. (b) keep the fence, reinject basePath INTO it → predicted 3 fail / 2 pass, observed exactly 'Tests 3 failed | 2 passed (5)' (× is a shape NavigationConfigSchema ACCEPTS · × names only members the schema declares · × does not reintroduce the route-prefix key). NO REBUILD WAS NEEDED and none is claimed: the subject is a file read off disk at runtime plus the prebuilt @objectstack/spec dist, so there is no stale-dist/ path a mutation could hide behind; both legs ran from a committed state.  GATES (each quoted from the gate's own verdict line, exit codes captured before any pipe): check-changeset-presence '✅ 1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/6050-gantt-navigation-basepath.md.' (its FIRST run was the decision: '❌ 1 source file(s) of 1 released package(s) changed, and this change adds no changeset: @object-ui/plugin-gantt — packages/plugin-gantt/src/readme-navigation-example.test.ts', so a changeset IS owed — scored patch, never major); check-changeset-no-major '✅ No changeset declares a `major` bump.'; check-doc-fence-languages '✅ check:doc-fences — … No unknown fence spelling hides one.' plus --self-test '26 cases pass'; check-doc-component-types '✅ Every documented component type is registered.'; check-doc-links 'Links are valid across 15 scan roots.'; check-control-bytes '✅ OK (scanned 5159 tracked text file(s); skipped 85 binary)'; check-type-check-coverage '✅ test type-check coverage: 41/41 packages compile their tests'; check-lint-coverage '✅ lint coverage: 46/46 packages linted, 0 with outstanding errors (0 total).'; check-vi-mock-specifiers ✅; check-shell-escape-residue ✅; pnpm --filter @object-ui/plugin-gantt type-check exit 0 AND echoed 'tsc --noEmit && tsc -p tsconfig.test.json' (proving it really ran — a zero-match --filter exits 0 silently); eslint on the new test exit 0; full package suite 'Test Files 47 passed (47) | Tests 402 passed (402)'. Self-scan for control bytes on all three authored files: clean.  ⚠️ TRAP ① HOLDS AND IS SAID SO IN THE PR BODY: every gate above is structurally blind to this defect — check-doc-snippet-types compiles ts/tsx fences and check-doc-component-types reads `type` literals, and that gate's own header records schema-key validity as 'a different question with a different answer … left unruled on purpose'. Their green means I broke nothing; the safeParse measurement and the pin are the verification.  DECLARED NARROWING: check-readme-exports was NOT run locally (its CI job builds every package first, and this README adds no self-import example for it to judge); repo-wide `pnpm lint` likewise left to CI. Local eslint was run on the authored file. CI convergence is the PM's per the standing contract — reporting at draft-PR time, gate status in_progress.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #6258: ObjectGantt.tsx:1148-1151 repeats two comment lines verbatim, leaving a mid-sentence fragment in the navConfig block (finding, cosmetic, unassigned)",
        "filed as #6259: ObjectGantt's default navConfig is spelled `{ mode: 'drawer', width: 'min(960px, 60vw)' }` using the spec-deprecated `width` rather than the `size` bucket that replaced it — and since resolveOverlayWidth gives explicit `width` priority over `size`, the deprecated branch stays load-bearing on the path most gantts take (finding, may be deliberate, unassigned)"
      ]
    }

    ⛔ Left as a draft: not marked ready, not enqueued, no auto-merge. Base has moved since branch point (mergeable_state: behind) — deliberately not updated, the PM lands it.

    ⚠️ One platform note for the write-limit watch: a direct curl to the REST API from this seat returned 403 GitHub access is not enabled for this session, while the MCP GitHub tools succeeded for the same operations. The PR, both findings and this comment all went through MCP. Nothing was half-applied.

    ⛔ #6051 was not folded in. packages/plugin-gantt/CHANGELOG.md was not edited.


    Generated by Claude Code

  7. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    ContributorAuthor

    os-dev-report

    ⚠️ This supersedes the previous comment, which is otherwise identical. That one led with the <!-- os-dev-report --> HTML-comment marker and the marker did not survive the round trip — reading the comment back returned a body beginning \n\n followed straight by the fence, so the marker is invisible to a scan. There is no comment-edit tool on this seat (issue_write writes issues, not comments), so the report is reposted with the marker as literal text instead. Ignore the earlier copy.

    {
      "issue": 6050,
      "status": "done",
      "branch": "claude/issue-6050-gantt-readme-navigation-basepath",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6256",
      "premise_still_valid": true,
      "summary": "The README's record-navigation example now shows { \"navigation\": { \"mode\": \"page\" } } instead of the spec-rejected { mode: 'page', basePath: … }. INTENT JUDGMENT, not a sed, and NOT triage's `view` hypothesis: verified against useNavigationOverlay, the config never yields a URL in any branch — page mode calls onNavigate(recordId, view ?? 'view'), the only URL it ever builds (the new_window fallback) comes from objectName, and ObjectGantt.tsx:1154 calls the hook with NO onNavigate so a page-mode click falls through to the host's onRowClick. The destination route is host-owned and was never authorable here under any spelling, so `basePath` maps onto no declared member and the sentence's own intent ('route to the standalone detail page instead') is delivered by `mode` alone. `view` was deliberately NOT substituted: the spec's own .describe() calls it 'Name of the form view to use for details' and the hook forwards it to onNavigate as the ACTION argument — it names a form view, not a route, so putting it where basePath stood would swap an invented key for a wrong one. It is documented separately for what it does. Prose aligned with PR #6053's ObjectGanttSchema.navigation doc comment on merged main, including its 'do not restate the vocabulary' instruction: the README now points at @objectstack/spec's NavigationConfigSchema rather than enumerating members. Two adjacent findings inside this card's own file set were fixed in place (tsconfig.test.json names `node` in types so the pin compiles, and its now-false 'no test touches a Node global' comment was rewritten; the extractor's try/catch was dropped because preserve-caught-error demands Error.cause, which is ES2022 above this project's ES2020 lib). PM ASSUMPTIONS ALL VERIFIED, NOT INHERITED — including one correction to the issue body's grep: `basePath` also exists as a REAL read site in @object-ui/layout (AppSchemaRenderer / NavigationRenderer prop, an app-nav href prefix), which the issue body did not list. It is a different concept, unreachable from useNavigationOverlay, and not a NavigationConfig member — so the premise holds and this is a docs defect, not a producer/consumer disagreement.",
      "tests": "ALL RUN FROM THE REPO ROOT (Trap ③ avoided; 47 reported files == 47 test files on disk, not apps/console's 22). Union re-run at the FINAL commit 04facb0bd with a clean tree.  SCHEMA PROBE vs installed @objectstack/spec@17.2.0, carrying controls: DECLARED MEMBERS (6): [\"mode\",\"view\",\"preventNavigation\",\"openNewTab\",\"size\",\"width\"] read from the schema's OWN shape (not prose); 'README-as-shipped { mode, basePath }: FAIL unrecognized_keys keys:[\"basePath\"]'; CONTROL '{ mode: page }: PASS'; CONTROL '{ mode: page, view: summary_view }: PASS'; CONTROL all six declared: PASS — so the failure is key-by-key, not a schema that refuses everything.  PIN: packages/plugin-gantt/src/readme-navigation-example.test.ts EXTRACTS the example from the README each run (bounded to its own '### Create / Edit / Delete / View' section, anchor asserted unique) and JSON.parse → NavigationConfigSchema.safeParse; nothing hand-retyped. It carries a LIVE control asserting the same parse still rejects an undeclared key by name. 'Test Files 1 passed (1) | Tests 5 passed (5)'.  ABLATION ×2, each proving the mutation on disk by grepping injected AND removed text separately, each restoring under trap … EXIT INT TERM, with 'git diff HEAD --stat' empty after (0 lines) and the restore leg re-run green — predictions stated before running and both confirmed. (a) revert README to pre-fix prose → predicted RED, observed 'Test Files 1 failed (1) / Tests no tests', dying at extraction with the extractor's own 'no json fence follows the navigation-override sentence in the README'. (b) keep the fence, reinject basePath INTO it → predicted 3 fail / 2 pass, observed exactly 'Tests 3 failed | 2 passed (5)' (× is a shape NavigationConfigSchema ACCEPTS · × names only members the schema declares · × does not reintroduce the route-prefix key). NO REBUILD WAS NEEDED and none is claimed: the subject is a file read off disk at runtime plus the prebuilt @objectstack/spec dist, so there is no stale-dist/ path a mutation could hide behind; both legs ran from a committed state.  GATES (each quoted from the gate's own verdict line, exit codes captured before any pipe): check-changeset-presence '✅ 1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/6050-gantt-navigation-basepath.md.' (its FIRST run was the decision: '❌ 1 source file(s) of 1 released package(s) changed, and this change adds no changeset: @object-ui/plugin-gantt — packages/plugin-gantt/src/readme-navigation-example.test.ts', so a changeset IS owed — scored patch, never major); check-changeset-no-major '✅ No changeset declares a `major` bump.'; check-doc-fence-languages '✅ check:doc-fences — … No unknown fence spelling hides one.' plus --self-test '26 cases pass'; check-doc-component-types '✅ Every documented component type is registered.'; check-doc-links 'Links are valid across 15 scan roots.'; check-control-bytes '✅ OK (scanned 5159 tracked text file(s); skipped 85 binary)'; check-type-check-coverage '✅ test type-check coverage: 41/41 packages compile their tests'; check-lint-coverage '✅ lint coverage: 46/46 packages linted, 0 with outstanding errors (0 total).'; check-vi-mock-specifiers ✅; check-shell-escape-residue ✅; pnpm --filter @object-ui/plugin-gantt type-check exit 0 AND echoed 'tsc --noEmit && tsc -p tsconfig.test.json' (proving it really ran — a zero-match --filter exits 0 silently); eslint on the new test exit 0; full package suite 'Test Files 47 passed (47) | Tests 402 passed (402)'. Self-scan for control bytes on all three authored files: clean.  ⚠️ TRAP ① HOLDS AND IS SAID SO IN THE PR BODY: every gate above is structurally blind to this defect — check-doc-snippet-types compiles ts/tsx fences and check-doc-component-types reads `type` literals, and that gate's own header records schema-key validity as 'a different question with a different answer … left unruled on purpose'. Their green means I broke nothing; the safeParse measurement and the pin are the verification.  DECLARED NARROWING: check-readme-exports was NOT run locally (its CI job builds every package first, and this README adds no self-import example for it to judge); repo-wide `pnpm lint` likewise left to CI. Local eslint was run on the authored file. CI convergence is the PM's per the standing contract — reporting at draft-PR time, gate status in_progress.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #6258: ObjectGantt.tsx:1148-1151 repeats two comment lines verbatim, leaving a mid-sentence fragment in the navConfig block (finding, cosmetic, unassigned)",
        "filed as #6259: ObjectGantt's default navConfig is spelled `{ mode: 'drawer', width: 'min(960px, 60vw)' }` using the spec-deprecated `width` rather than the `size` bucket that replaced it — and since resolveOverlayWidth gives explicit `width` priority over `size`, the deprecated branch stays load-bearing on the path most gantts take (finding, may be deliberate, unassigned)"
      ]
    }

    ⛔ Left as a draft: not marked ready, not enqueued, no auto-merge. Base has moved since branch point (mergeable_state: behind) — deliberately not updated, the PM lands it.

    ⚠️ Platform note for the write-limit watch: a direct curl to the REST API from this seat returned 403 GitHub access is not enabled for this session, while the MCP GitHub tools succeeded for the same operations. The PR, both findings and both report comments went through MCP. Nothing was half-applied.

    ⛔ #6051 was not folded in. packages/plugin-gantt/CHANGELOG.md was not edited.


    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

documentationImprovements or additions to documentationdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions