fix(cli): the package-docs step line reports what it collected, not what it attempted - #18445
Conversation
…hat it attempted `os build`/`os compile` printed `Collecting package docs (ADR-0046)...` unconditionally and BEFORE the call it announces, so a build that collected nothing emitted the same reassuring sentence as one that collected four documents. Move the print after `collectAndLintDocs` and carry `docsResult.docs.length` in it. Pinned from both ends (absent `src/docs/`, empty `src/docs/`, two docs) so a test asserting only that the sentence was printed cannot pass on the defective tree, and the printed count is compared against the emitted artifact's `docs` set rather than read as text alone. Claude-Session: https://claude.ai/code/session_01DvvamiacK328idtBYJBxV3 Co-authored-by: Claude <[email protected]>
The `transcript=os-build` block on `content/docs/deployment/cli.mdx` quotes the docs step line. The sample fixture behind that page ships no `src/docs/`, so the line it now prints is `0 collected` — measured by rebuilding the fixture the page's own callout names (blank starter + the ticket object, view and action from `build-with-claude-code`, renamed into `my_app_`) and reading the count off the run, not by picking a plausible number. Claude-Session: https://claude.ai/code/session_01DvvamiacK328idtBYJBxV3 Co-authored-by: Claude <[email protected]>
📓 Docs Drift CheckThis PR changes 1 package(s): 17 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 3 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 24 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 351fca64fc7621e68cfbf6ec182de81b924c1349 && git checkout 351fca64fc7621e68cfbf6ec182de81b924c1349
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b6fe311a9ab7bebdfb22631030ac19ebeb87c6a5 1d2d6946bb7cb2c03a11cea6b0691d601a70f22e && git checkout -B drift-repro b6fe311a9ab7bebdfb22631030ac19ebeb87c6a5 && git merge --no-ff 1d2d6946bb7cb2c03a11cea6b0691d601a70f22e
node scripts/docs-audit/affected-docs.mjs --json b6fe311a9ab7bebdfb22631030ac19ebeb87c6a5
|
A CLI spawn through tsx costs seconds, so the five cases timed out against vitest's 5s default. Same per-test spellings the sibling spawners in this directory use (120_000, and 180_000 for the three-run case). Claude-Session: https://claude.ai/code/session_01DvvamiacK328idtBYJBxV3 Co-authored-by: Claude <[email protected]>
Fixes #18432
Clause-②: no — this PR puts no new key on a published payload. It changes only a progress line's text and the doc transcript that quotes it; no input the build accepts or refuses changes. Declared by the dispatching seat in claim
5696261079.What changed
os build/os compileprinted its package-docs step line before the call it announces, and the line carried no count:So a build that collected nothing emitted the same reassuring sentence as one that collected four documents. The print now happens after the collection and reports it:
This is the reassurance half of #18170. #18428 landed the audible half, where an uncollected docs directory speaks for itself; this card was fenced out of that PR because the transcript quoting the step line lived on a page PR #18416 had staked. That page merged at 2026-09-16T10:48Z, so the fence is lifted.
The doc half is a measurement, not a copy-edit
content/docs/deployment/cli.mdxcarries the step line inside a fence taggedtranscript=os-build. The number written there is the sample fixture's real count, measured, not chosen:os buildrun.blankstarter frompackages/create-objectstack/src/templates/blank(the two-fieldmy_app_noteobject and the three connector plugins) plus the four-field ticket object, the view and the action fromcontent/docs/getting-started/build-with-claude-code.mdx§3, renamed out of that page'ssupport_desk_namespace intomy_app_, with thesupportapp deliberately not added. Built out-of-tree in the session scratchpad against this branch'spackages/, run throughpackages/cli/bin/run-dev.js.Running author-time rules (45),Data: 2 Objects 6 Fields,UI: 1 Views 1 Actions,Runtime: 3 plugins.blanktemplate nor the walkthrough ships asrc/docs/directory, and the stack declares no inlinedocs:. The measured run is the transcript's own case of the sentence the card drafted.Measured run, verbatim:
The transcript-drift gate does NOT hold this number — and that is correct
Dispatch Zone 2 assumed
check:docs-transcript-driftwould hold the new value. Measured and falsified, so it is reported rather than relied on: that gate'sTOKENSarray inscripts/docs-audit/check-docs-transcript-drift.mjscarries exactly one row,author-time-rule-count, matchingauthor-time rules \((\d+)\). It compares the45in this block and nothing else.That is not a hole this PR opens. The gate is scoped by design to values a live registry derives;
0 collectedis fixture identity, in the same class as2 Objects 6 Fieldsand1 Views 1 Actions, which the page's existing callout already covers ("everything else is fixture identity and reproduces"). ATOKENSrow here would have to build a sample project rather than import a registry, which is a different gate. Named so the next author does not read the drift gate's green as vouching for this digit.The number that gate does hold is unchanged by this PR, and
check:docs-transcript-driftwas run.Tests
packages/cli/test/build-docs-step-count.e2e.test.tspins the behaviour as a pair, because a test that only asserted "the step line was printed" passes on the defective tree — the defective tree printed it unconditionally:src/docs/at all0 collectedsrc/docs/present and empty0 collected2 collectedEach run's printed count is also compared against the emitted
dist/objectstack.json, so a number that drifted away from the set it describes cannot pass as text. The pre-fix spelling — the sentence with nothing after the ellipsis — is pinned absent.--jsonis asserted to stay one JSON document carrying no step text.The suite spawns the CLI, so it reuses
childEnv()and thebin/run-dev.js+ tsx shape ofbuild-json-advisory-parity.e2e.test.tsrather than inventing one..e2e.test.tsname puts it in this package's nightly tier:packages/cli/vitest.config.tsroutes the population throughOS_TEST_TIERS, so the queue and per-PR runs collect the 212 non-tier files and this file is not among them; the nightly onmaincollects the tier files, where it lands inintegrationbyvitest-tiers.ts's SPAWN predicate. That is the package's own measured cost design — every one of its 60 spawners lives there — so a new spawner follows it rather than having me make a unilateral call about the Test Core critical path. Locally it was run under that switch, andpnpm check:tier-file-adoptionis green.Declared deviation from the claim's file surface
Claim
5696261079declares a two-file surface. This PR carries four files, and the two extra ones are declared rather than quiet:packages/cli/test/build-docs-step-count.e2e.test.ts— the pin the dispatching seat itself asked for in its suggested route ("the assertion that would have caught the original defect"), and what AGENTS.md's Post-Task Checklist step 1 owes..changeset/18432-docs-step-line-reports-count.md—@objectstack/cliis a published package and this moves user-visible build output, so apatchchangeset is owed andskip-changesetwould be wrong.No production file outside the two the claim names is touched.
content/docs/releases/andpackages/spec/are untouched.Verification
Gate derivation is from the actual changed files, recomputed by the tool from the merge base rather than a hand-written list, and reconciled back with
--rancarrying each command's own exit code (captured before any pipe):92 of 93 green. The one non-zero is the known pre-existing red:
pnpm check:cross-package-test-inputspackages/cli/test/init-created-files-summary.e2e.test.tsdescendingpackages/spec/dist/— a file this diff never touches, and the new test file appears nowhere in the report. Filed as #18353 / #18348.Five gates first answered
PREREQUISITE NOT METagainst a partly-built tree (check:skill-examplesexit 1 on a missingpackages/client-react/dist,check:dual-build-cjs-loads/check:i18n/check:i18n-coverage/check:i18n-walk-parityexit 3). Those are nothing was measured, not passes and not findings, so they were re-run afterpnpm buildand all five are exit 0.pnpm check:docs-transcript-drift,pnpm check:doc-anchors,pnpm check:doc-authoring,pnpm check:docs-single-h1andpnpm check:nul-bytesare green on the edited page.pnpm lint— the repo-wideeslint . --no-inline-config, which the derivation never names — was run whole, not narrowed: exit 0, no findings. No.cache/objectui-*existed in this worktree to pollute it.Tests
Reverse verification — the pin can fail, proved rather than asserted. The fix was committed first, then the pre-fix ordering was restored on disk and the suite re-run:
--jsoncase is a control, not a regression detector —--jsonprints no step line on either tree, so a run in which it also went red would mean the ablation had hit something other than the ordering. The four behavioural pins are the ones that must move, and they did. The mutation was proved to reach disk by occurrence count on the text being replaced, not by the editor's exit code; the restore is proved by blob-hash equality plus an emptygit diff HEAD, not by a return code, and it runs from anEXIT INT TERMtrap with an absolute path.Where the measurement of N happened, reproducibly
The fixture was built in the session scratchpad, outside the repository, and deleted; nothing of it is in this diff.
Acceptance notes
check:cross-package-test-inputsexits 1 on a tree wherepackages/spec/distis built. Already filed as [finding] check:cross-package-test-inputs passes in CI and fails on a built tree — its verdict is a function of gitignored build state #18353 / [regression] check:cross-package-test-inputs 的裁决取决于被 gitignore 的空目录 packages/spec/dist 存不存在 —— 构建过的工作树一律红,CI 绿只因那一步跑在构建之前(#18340 引入) #18348; no seventh card.transcript=os-buildblock elides two summary rows the current CLI prints for that fixture (Logic: 0 FlowsandSecurity: 0 Positions 0 Permissions) and the twofield-no-consumersadvisories it raises. Elision is explicitly within what the drift gate's design allows for a hand-authored transcript ("an elided, annotated, sometimes abbreviated paste"), so this is an observation about the page's editorial choices, not a defect, and it is out of this card's declared scope. Carrier: the next PR that re-measures this block, or adocs-accuracy-auditpass overcontent/docs/deployment/cli.mdx.Authored by Claude Code in session
session_01DvvamiacK328idtBYJBxV3.Generated by Claude Code