Repository navigation
fix(cli)!: docs/nav-target judges each package body's navigationContributions on a multi-package artifact - #27394
Conversation
…ibutions on a multi-package artifact
On a composeStacks(..., { manifest: 'preserve' }) artifact a non-owner
package's navigationContributions sit only in its package body; the
top-level manifest is the owner's copy. lintDocNavTargets now reads each
body's contributions when the stack carries packages[], otherwise the
top-level manifest's, over the same artifactPackages walk.
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L6bhgXcN9HaZYSEUdJWEjw
…rrowing Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXcN9HaZYSEUdJWEjw
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXcN9HaZYSEUdJWEjw
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 35 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 826293cfa1ce1257c0d8fbdc45fdd6457f6c5575 && git checkout 826293cfa1ce1257c0d8fbdc45fdd6457f6c5575
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7e200b4a68c242afc9f7caf0289e985eff3fcac6 6c5250cbdb420485586b6b642a601ad8565149a0 && git checkout -B drift-repro 7e200b4a68c242afc9f7caf0289e985eff3fcac6 && git merge --no-ff 6c5250cbdb420485586b6b642a601ad8565149a0
node scripts/docs-audit/affected-docs.mjs --json 7e200b4a68c242afc9f7caf0289e985eff3fcac6 |
|
domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw Contract reviewServed-tier: Inputs read: card #27343 (body, triage, claim, os-dev-report), PR #27394 (body, file list, net diff against ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentThe narrowing against the published spec on "Bodies when
Findings: no must-fix, no should-fix. Two notes carried in ③ (the runtime's silent drop of a hand-written top-level contribution; the #27342 declaration precedent), neither on this diff. Check-runs on Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw 维护者速读(席位终稿)
席位核对 — ACCEPT ·
|
|
domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw Landed ·
|
| Reading | Value |
|---|---|
| PR #27394 | merged at 2026-10-29T05:32Z, merge commit ebed73bd, head 6c5250cb (the ACCEPTed head) |
| Card #27343 | closed / completed by this PR's Fixes |
State change: pm:dispatched removed from #27343. No other card was closed by this PR's body.
Generated by Claude Code
Requested by Sales · project thread
Fixes #27343
Clause-②: no (narrowing) — on a multi-package
composeStacks(…, { manifest: 'preserve' })artifact, atype: 'doc'item in a package body'snavigationContributions, whosedocorbooknames nothing the artifact carries, built clean before and is now refused byos build,os validateandos lint(docs/nav-target).Metadata-protocol: narrowed
Skills-docs: none —
skills/objectstack-ui/rules/pages.md(thetype: 'doc'nav section) andcontent/docs/ui/apps.mdx(thedocs/nav-targetparagraph) already teach the refusal with no single-package limit; this change makes contributions on multi-package artifacts meet that text.维护者速读
os build/os validate/os lint的文档导航检查(docs/nav-target)在多包产物上改为逐包读取各包自己的navigationContributions(只改packages/cli/src/utils/collect-docs.ts的lintDocNavTargets一处读取)。preserve产物的顶层 manifest 只是"最后一个包"(主包)的副本,其他包贡献到别的应用里的文档导航项从没被检查过,指向不存在文档的条目会静默通过构建、到运行时才坏。minor):多包产物里"非主包贡献的导航项指向不存在文档"从静默通过变为构建错误;单包应用不变,主包的贡献只判一次不重复报。回滚即 revert 本 PR。packages/cli/src/utils/collect-docs.ts中lintDocNavTargets的contributions一行;packages/cli/src/utils/collect-docs.nav-target.test.ts新用例;.changeset/27343-doc-nav-target-package-contributions.md。pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 src/utils/collect-docs.nav-target.test.ts,预期 10 个用例全部通过。What changes
lintDocNavTargetsreadnavigationContributionsoff the artifact's top-level manifest only. Measured on a real two-packagepreservecomposition (composeStacks([svc, crm], { manifest: 'preserve' })):manifest.navigationContributionspackages[i].manifest.navigationContributionssvc, non-owner)packages[0]onlycrm, owner)packages[1]The contributions now come from each package body when the stack carries
packages[], otherwise from the top-level manifest. The walk is the sameartifactPackages(stack)the function already uses for books and apps; no second walker.Why not "top level when present, else bodies" (the card's wording). Falsified by the table: when the owner contributes, the top level carries its copy, so that rule would still skip every other package's contributions. Reading the top level as well as the bodies would judge the owner's twice. The rule here is the one
collectNavGroupInputs(packages/cli/src/utils/nav-contribution-groups.ts) already applies to the same key for the same reason.Tests
One new case in
collect-docs.nav-target.test.ts, on the real composer:navigationContributionswhen only the non-owner contributes;apps/setup/navigation/nav_svc_help;Acceptance notes
packages[]and puts contributions on its top-level manifest that no body carries is no longer judged there. The runtime registers eachpackages[]entry's own manifest (ADR-0130 D4), so such a top-level copy is not what gets registered;collectNavGroupInputsmakes the same call.Clause-②: no (narrowing)andMetadata-protocol: narrowed, so the changeset isminorwith a BREAKING banner and the ADR-0087not-required (no-migration-prescription)disposition. The claim comment carries the bareClause-②: no; the narrowing arm is from triage and the dispatch.navigationContributionscarries atype: 'doc'item (the dogfood battery below is green).Verification
Final head
6c5250cbd(origin/main28872e68dmerged in). The unit tier, typecheck and the first gate run are at260fe77e0, before that merge; the merge touches none of the three changed files. The dogfood battery, the gate re-run and the target test re-run are at6c5250cbd.Red, then green, then ablation
Tests 1 failed | 9 passed (10),AssertionError: expected [] to deeply equal [ [ 'docs/nav-target', …(1) ] ].Tests 10 passed (10).91e14b22(fix committed first), throughscripts/ablation-replace.mjs: anchorconst contributions = (bodies.length > 0replaced withconst contributions = (false(the old top-level-only read). Anchor x1 to x0, replacement x0 to x1, blobf64208d1cd06toedd969ecd1bb. Run: the new case fails with the sameexpected [] …assertion,Tests 1 failed | 9 passed (10). Restore: blob equals HEAD (f64208d1cd06),git diff HEADempty. The test imports./collect-docs.jsfrom source, so nodist/rebuild is in the path.Package checks
pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2(under the verify lock,VERDICT command-exit 0):Test Files 346 passed (346),Tests 5033 passed (5033). The integration tier is left to CI.pnpm --filter @objectstack/cli build && pnpm --filter @objectstack/cli typecheck:VERDICT command-exit 0;check:test-typecheck: OK.packages/cli(eslint packages/cli --no-inline-config): exit 0.Derived gates
260fe77e0:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 60 families on 3 changed paths (51 changed lines). All 60 ran, exit codes captured before any pipe; 57 exit 0. Three exit 3 on a prerequisite:check-plugin-teardown-shape --self-test(its pinned fixture commit was not in the shallow clone; exit 0,48 cases passafter fetching that commit),check:i18n-coverage(@objectstack/connector-slackhad no build; exit 0 after building it) andcheck:dual-build-cjs-loads(needs a full workspace build).6c5250cbdafter the dogfood closure build: the same 60 families (derivation unchanged), all 60 exit 0,check:dual-build-cjs-loadsincluded.check-adr-0087-registration --base origin/mainexit 0: the changeset readsBREAKING+bang+clause-②-narrowing+metadata-protocol-narrowed,not-required (no-migration-prescription).check:i18n-coverage:OK (14 config(s), 527 baselined untranslated string(s), none new).dispatch-gates --ranat6c5250cbd:60 derived famil(ies) accounted for — 60 run, 0 NOT-MEASURED (a DERIVED zero — all 60 recorded an exit code and none of them is 3).6c5250cbd:Tests 10 passed (10).Dogfood battery (owed by the narrowing arm)
At
6c5250cbd, under the verify lock, after building the@objectstack/dogfooddependency closure,pnpm --filter @objectstack/dogfood testwith no shard and no path:Test Files 348 passed | 3 skipped (351),Tests 2806 passed | 20 skipped (2826), lockVERDICT command-exit 0. No failures.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgXcN9HaZYSEUdJWEjw