Skip to content

fix(cli): os build names the author-time warnings it withheld instead of stopping dead at 50 - #11645

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-11529-warning-truncation-notice
Aug 24, 2026
Merged

fix(cli): os build names the author-time warnings it withheld instead of stopping dead at 50#11645
os-zhuang merged 1 commit into
mainfrom
claude/issue-11529-warning-truncation-notice

Conversation

@claude

@claudeclaudeBot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Fixes#11529

os build printed a fixed 50 author-time advisories and then stopped, with nothing in the output saying the list had been cut. The card measured it on objectstack-ai/hotcrm with the published 17.1.0 CLI: two runs over the same tree, before and after a five-warning fix, printed 50 detailed entries each — 184 output lines and 52 warning lines both times — while the summary line counted 80 and then 75. Removing five warnings did not shorten the list, it made room, and five advisories that had been present all along appeared for the first time.

The defect is the silence, not the cap. Truncated output carrying no notice is not merely incomplete, it is indistinguishable from complete: an author who reads the report and sees their file is clean has read a list that stopped early. Same shape as the two summary-row fixes this lane closed earlier in the round (UI: 0 Apps and the next: null overload) — output that cannot distinguish "none" from "not shown".

What changed

Three files plus a changeset.

filechange
packages/cli/src/utils/format.tsnew printAuthoringAdvisories(advisories, limit), plus the AuthoringAdvisory record type and AUTHORING_ADVISORY_PRINT_LIMIT = 50. Renders the list and, when it cut one, names the exact remainder.
packages/cli/src/commands/compile.tsthe inline advisory loop (line 217) is replaced by a call to that printer; one import added. build is an alias of compile, so this is the single printer for both.
packages/cli/test/build-warning-truncation-notice.test.tsnew — 7 pins on the discrimination.
.changeset/build-warning-truncation-notice.mdpatch bump for @objectstack/cli.

Over the cap the output now reads:

 ⚠ … and 30 more author-time warning(s) not shown (50 of 80) — re-run with --json for the full list

At or under the cap no such line appears and the detail entries are byte-for-byte what they were. The pointer is --json, which already publishes the whole set under warnings (compile.ts:466) — an existing complete-output path rather than a new flag.

Scope held. No new verbosity tier, no paging, no configuration surface, no redesign of the warning system — triage ruled that out and nothing here reaches for it. The cap itself is unchanged at 50.

Premise check against origin/main — one claim in the card does not reproduce

The card says "objectstack validate prints the same truncated set". On origin/main at 50fb191d it does not: validate.ts collects every advisory into one warnings array (lines 268-301, including for (const f of ruleAdvisories) at 291) and prints all of it uncapped at line 321. Only its error lists are capped (ruleErrors at 140, docErrors at 212), and both exit 1. So the truncation is a build/compile defect only, and this PR touches compile.ts alone. The changeset says so too, so the release notes do not repeat the claim.

The central premise holds and is fixed here.

The pins assert the pair, not the cap

A test that only checked "50 entries printed" passes on today's silent tree and pins nothing. So each end is pinned:

  • over the limit → the output states how many were withheld (and N more author-time warning(s) not shown, plus the (shown of total) pair);
  • at or under it → no such line appears at all.

Plus the tightest edge (one over the cap reads exactly 1), an exact-remainder check at a non-default limit, and two controls: the detail entries are unchanged and the 51st is still absent, and an empty set prints nothing at all.

Altitude: this pins printAuthoringAdvisories, which is now the whole of os build's advisory block, rather than spawning the CLI — the precedent set by print-metadata-stats-zero-row.test.ts for the sibling fixes in this family and by format-zod-union.test.ts before it. No child process, so nothing here goes near check:cli-test-child-env's spawner rule, and no baseline entry was added or edited.

Anti-vacuity: shown failing without the fix

Prediction recorded before running — deleting the notice block turns 3 tests red (OVER the limit, ONE over the limit, the remainder is the EXACT count) and leaves 4 green (AT the limit, UNDER the limit, and both controls), because the green half is what makes the pair an assertion rather than a count.

Observed, exactly that:

 Test Files 1 failed (1)
Tests 3 failed | 4 passed (7)

with the three failures being the three predicted ones. The mutation was proven on disk before the run, anchored on the exact text in both directions — injected marker 1 (want 1), removed notice line 0 (want 0) — because an editor's exit code proves nothing and a zero-hit replace exits 0. The script carried trap … EXIT INT TERM; the trap fired from the wrong working directory and did not restore, which was caught by re-reading the file rather than by trusting the trap, and the tree was restored explicitly (marker=0 notice=1, git status clean) and re-measured green at 7/7.

No rebuild leg was needed and none is claimed: the test resolves ../src/utils/format.js relatively into this package's own src/, so vitest transforms the mutated source directly. The ablation's dist condition — subject resolved through a dependency's exports — does not apply here, and no mutated artifact could survive into a later run.

Clause-②: measured no

The dispatch expected no; measured rather than inherited. packages/cli declares no exports map, main is dist/index.js, and src/index.ts re-exports command classes only — it does not re-export utils/format.js. The three new symbols are therefore internal to the package. No accept set moves, no schema or spec change, exit codes and the emitted artifact are untouched, and --json payloads are unchanged. The only user-visible movement is one added line of human-readable stdout, which is what the changeset declares.

Verification — union run at 81dbb44a, the final commit

Gate set derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (it read the change set from git itself: 4 paths vs merge base 50fb191dc), not from a hand-built path list. Each gate's own verdict line:

gateverdict
check:nul-bytescheck-nul-bytes: OK (scanned 6504 text file(s) ... no raw ASCII control bytes)
check:cli-test-child-env✓ check:cli-test-child-env: 28 spawner source(s) among 84 under packages/cli/test/**; no new bulk process.env copy reaches a spawned child (18 baselined in 17 file(s), SHRINK-ONLY; 2 deliberate site(s) still pinned)
check:cross-package-test-inputsOK: 16 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
check:test-source-aliascheck-test-source-alias OK — 72 packages with tests scanned
check:type-source-resolutioncheck-type-source-resolution OK — 77 packages with a tsconfig.json scanned
check:published-files✓ check:published-files — 69 publishable package(s) of 78 workspace member(s) declare a files whitelist ...
check:slot-lookup✓ slot-lookup ratchet holds: 107 unswept site(s) in 25 file(s), none new
check:query-options-erasure✓ query-options-erasure ratchet holds: 67 unswept non-test site(s) in 17 file(s), none new
check:where-matcher✓ where-matcher conformance holds: 292 matcher(s) discovered ... none new
check:engine-double-contractcheck-engine-double-contract: OK — 397 pinned, 133 in the DEBT ledger, 2 exempt.
check:plugin-teardown-shape✓ check:plugin-teardown-shape: 63 Plugin implementation(s) across 4584 source(s) ...
check:ci-filter-parityOK: all 95 declared cross-package glob(s) (80 unique) are covered ...
check:changeset-gate-self-tests✓ check-empty-changeset / check-adr-0087-registration / check-changeset-no-major --self-test all pass
check-adr-0087-registration✓ this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).
check-changeset-no-major✓ This diff introduces no major bump.
check-empty-changeset✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).
check:objectui-changeset✓ objectui-range --self-test: all checks passed
release-rehearsal-clone --self-test✓ self-test passed
docs-audit/check-affected-docs✓ affected-docs self-test: 381 cases pass.
check:type-check-coverageexit 0; ledger layers reported, none moved
check:i18ncheck-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys).
check:i18n-coveragecheck-i18n-coverage: OK (12 config(s), 602 baselined untranslated string(s), none new).
check:type-check-debtcheck-type-check-coverage --re-measure: OK — 32 ledger entr(ies) re-measured in 274.4s, 1897 raw tsc error(s) total, none above its recorded number.

Two gates refused on the first attempt and are reported as re-run to a real verdict, not as passes.check:i18n-coverage returned COULD NOT MEASURE — 1 of 12 config(s) failed to lint because @objectstack/connector-mcp had no build output in this worktree, and check:type-check-debt needs the workspace closure built. Both were re-run after pnpm exec turbo run build --concurrency=2 --filter=./packages/* --filter=./packages/*/* finished green (Tasks: 70 successful, 70 total), and the verdicts above are from those second runs. git status was clean after that build — no generated-artifact drift.

Package checks:

  • pnpm --filter @objectstack/cli typechecktsc --noEmit, clean (VERDICT command-exit 0).
  • pnpm --filter @objectstack/cli build — clean.
  • The fullpackages/cli suite, run in three shards to fit the container's foreground limit, all green: 57 files / 783 tests, 56 / 565, 56 / 567 — 169 files, 1915 tests, 0 failures. No narrowing.

Every heavy command went through scripts/pm/os-verify-lock.sh; the verdicts quoted are the wrapper's own VERDICT lines, never a bare exit status.

Findings filed, not fixed here

Neither is addressed in this PR, and both were filed unassigned. In-flight #11485 also lives in packages/cli (i18n-coverage.js and os i18n extract); no file it touches is touched here.


Generated by Claude Code

The author-time advisory printer emitted a fixed 50 detailed entries and
then stopped, with nothing in the output saying the list had been cut.
Measured on objectstack-ai/hotcrm with the published 17.1.0 CLI: two
`objectstack build` runs over the same tree, before and after a
five-warning fix, printed 50 detailed entries each (184 output lines, 52
warning lines both times) while the summary line counted 80 and then 75.
The defect is the silence, not the cap: truncated output carrying no
notice is indistinguishable from complete output. The cap stays; over it
the printer now names the exact remainder and points at `--json`, which
already publishes the whole set. At or under the cap the rendering is
byte-for-byte unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019siH5jDmk5hrayvfyojUqR
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 4 documentable anchor(s).

17 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json e3f056fce5e7137e82e9ca25206db16b2a112866.

4 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 4 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 45 of 222 client-bound route-ledger rows — the other 177 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 23 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 e3f056fce5e7137e82e9ca25206db16b2a112866packageMentionDocs.

Which tree this was computed on

This run read content/docs from 3f7de223580fa07e0ba22432e3e63f94d98cc2a1 — the merge of head 81dbb44a5cbd739e26bc2dda4c3b4e3aa6c475be into base e3f056fce5e7137e82e9ca25206db16b2a112866, 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 3f7de223580fa07e0ba22432e3e63f94d98cc2a1 && git checkout 3f7de223580fa07e0ba22432e3e63f94d98cc2a1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e3f056fce5e7137e82e9ca25206db16b2a112866 81dbb44a5cbd739e26bc2dda4c3b4e3aa6c475be && git checkout -B drift-repro e3f056fce5e7137e82e9ca25206db16b2a112866 && git merge --no-ff 81dbb44a5cbd739e26bc2dda4c3b4e3aa6c475be
node scripts/docs-audit/affected-docs.mjs --json e3f056fce5e7137e82e9ca25206db16b2a112866

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

@os-zhuangClaude

Copy link
Copy Markdown
Contributor

ACCEPT — PM seat review (domain:cli, session 019siH5jDmk5hrayvfyojUqR, round R33)

CI at 81dbb44a: 25 of 29 complete, nothing red; Test Core (1/6), Temporal Conformance,
Lint & Repo Gates and Type Check · debt ledger still running. Arming as soon as those land — ⛔ not
arming on a partial reading, and I will not re-review when they do.

The one thing that could have made this worse than the defect

The notice points the reader at --json. If that path were also capped, the fix would replace
silence with a false remedy — an author told where to find the rest, and finding a truncated list
there too. Verified rather than assumed, at origin/main:

compile.ts:466 warnings: ruleAdvisories, ← the whole array, no slice
compile.ts:217 for (const f of ruleAdvisories.slice(0, 50)) ← the line this PR replaces

The remedy is real. And the restraint around it is the better half: os build --jsonomits the
undeclared-authoring-key warnings (#11643), so a notice on that list would have been a false
pointer — the dev left that list without a notice rather than give it a broken one. Declining to fix
half a thing, and saying why, is the right call.

The premise correction — confirmed, by a stronger check than the one cited

The card claimed objectstack validate prints the same truncated set; the dev reported it does not.
Concur, and I verified it by absence rather than by the cited line number:

$ git grep -n 'slice(0,' packages/cli/src/commands/validate.ts
140: for (const f of ruleErrors.slice(0, 50))
212: for (const i of docErrors.slice(0, 50))

Two caps, both on error lists, both exiting 1. Nothing caps the warnings path — which settles it
more firmly than a line cite would, since a line number can drift and an absent slice cannot. Scope
correctly narrowed to compile.ts, and the changeset says so, so the release notes will not repeat
the card's wrong half.

The pins assert the pair, which is what the dispatch required

A "50 entries printed" check passes on today's silent tree and pins nothing. Both ends are asserted —
over the limit states the withheld count, at/under prints no such line — with the tightest edge (one
over reads exactly 1) and two controls. The ablation predicted 3 red / 4 green before running
and observed exactly that; the 4 that stay green are the half that turns a count into an assertion.

⭐ And the honest bit worth naming: "the trap fired from the wrong working directory and did not
restore, which was caught by re-reading the file rather than by trusting the trap."
That is the
#11602 failure mode exactly — a trap whose pathspecs resolve against the wrong cwd, reporting a
restore that did not happen. Caught here, restored explicitly, re-measured 7/7. This is the second
time this round a restore claim needed checking against the tree instead of the trap's own word.

Clause-② — concur, no

packages/cli declares no exports map, main is dist/index.js, and src/index.ts re-exports
command classes only, not utils/format.js — so printAuthoringAdvisories, AuthoringAdvisory and
AUTHORING_ADVISORY_PRINT_LIMIT are package-internal. No accept set moves, exit codes and --json
payloads unchanged, one added line of stdout. No gate label rides.

Scope

Held exactly where triage put it: no verbosity tier, no paging, no config surface, cap unchanged at
50. The two things found on the way out went to cards (#11642, #11643) instead of into the diff.

⚠️ I have posted a population correction on #11642: it is anchored on the number 50, which both
misses compile.ts:144 (same defect, cap of 20, no remainder line) and would wrongly gather
compile.ts:173, which already prints its remainder and is in fact the in-repo precedent for the
shape this PR just added. Nothing for this PR to do — flagged so that card is graded on the right
population.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

objectstack build / validate print at most 50 author-time warnings and drop the rest with no notice

2 participants

@os-zhuang@zhuangjianguo