Repository navigation
Eight more silent 50-entry truncations in the CLI's build / validate / init printers #11642
Description
Found while fixing #11529, which was scoped by triage to the build author-time warning printer alone ("widening beyond that is not asked for"). The same silent cut exists at eight other call sites in packages/cli, and none of them was in that card's scope.
The sites
All at origin/main commit 50fb191d, all .slice(0, 50) with no line naming the remainder:
| file | line | list | what happens after |
|---|---|---|---|
packages/cli/src/commands/compile.ts |
235 | ruleErrors |
exit 1 |
packages/cli/src/commands/compile.ts |
293 | unknownKeyFindings |
advisory, build continues |
packages/cli/src/commands/compile.ts |
333 | access-matrix drift |
exit 1 |
packages/cli/src/commands/compile.ts |
360 | docErrors |
exit 1 |
packages/cli/src/commands/validate.ts |
140 | ruleErrors |
exit 1 |
packages/cli/src/commands/validate.ts |
212 | docErrors |
exit 1 |
packages/cli/src/commands/init.ts |
893 | report.advisories |
scaffold continues |
packages/cli/src/commands/init.ts |
908 | report.errors |
— |
Line 217 of compile.ts is the ninth, and is the one #11529 fixes.
Why this is not just bookkeeping
Two of these are the exact defect #11529 describes, on an advisory list that does not fail the command:
compile.ts:293already prints the true total in its header —Undeclared authoring keys (N) — dropped at load (#3786)— and then shows at most 50. So the output states two numbers that disagree and explains neither, which is the shapeobjectstack build/validateprint at most 50 author-time warnings and drop the rest with no notice #11529 measured on hotcrm. These are keys that drop silently on the way to storage; an author past the cut ships metadata with the key quietly gone.init.ts:893is a scaffold-time advisory list with the same silent cut.
The six error-path sites are a weaker but real case. The code's own comment at validate.ts:126 gives the rationale the truncation undercuts:
Every failing rule reports at once. The command used to exit at the first failing gate, so an author with three unrelated problems fixed them in three round trips and could not see how deep the hole went.
A 50-entry cut with no notice restores a smaller version of that: past 50 the author cannot see how deep the hole goes, and each round of fixes reveals a new batch that reads as fresh breakage.
Suggested shape
#11529 lands printAuthoringAdvisories in packages/cli/src/utils/format.ts, which prints a list and then names the remainder when it cut one. Whatever is decided here, the mechanism already exists in the package and these sites can adopt it rather than growing a second concept. Deliberately not proposed: new flags, verbosity tiers or paging — #11529's triage ruled that out for the warning printer and the same reasoning applies.
Not filed as a sub-issue of #11529: these sites sit outside the scope that card completes, rather than inside it.
Activity
os-zhuang commentedon Aug 24, 2026
Population correction — one more site, and one listed site that should be struck
Reviewing PR #11645 I re-derived this card's population against
origin/main. The finding is real;the population is defined by the number 50, and the defect is not.
➕
compile.ts:144is missing from the ledgerThe
--strict-bodyrefusal path. Caps at 20, not 50, which is why a 50-anchored sweep could notsee it:
No remainder line. It states the true total in the header and then shows 20 — which is exactly the
shape this card already counts as a defect at
compile.ts:293("even prints the true total in itsheader and then shows 50"). Same defect, different cap value, so it belongs here or it does not, but
the two cannot be graded differently.
➖
compile.ts:173is NOT a defect — do not add itListing it would be the obvious next move from the grep above, and it is wrong. That site already
prints its remainder:
Notice plus a pointer at the complete-output path — which is the shape #11645 just introduced for the
advisory list. So
:173is the precedent, not a sibling defect. Worth naming in this card: aremedy already exists in the same file and the fix for the rest can copy it rather than invent one.
The general point, since this card is a sweep
A population selected by
slice(0, 50)answers "where does the number 50 appear", not "where isoutput truncated in silence". The two differ in both directions here — one miss at a different cap,
one false positive that already has a notice. Whoever takes this should re-derive the worklist from
the defect (a truncating render with no remainder line) and check each candidate's surrounding
lines before counting it, rather than from the literal.
⛔ Not grading this card and not changing its labels — recording the correction so it is graded on the
right population.
Generated by Claude Code
os-zhuang commentedon Aug 24, 2026
Triage: lands in
packages/cli(build/validate/initprinters) →domain:cli, type Task,pm:queue. Rationale: direct extension of the truncation class #11529 is currently fixing — eight more sites of the same silent 50-entry cap, filed from that work. No design question; the fix pattern is whatever #11529 lands.build --json), queued this round, same surface, fold-or-serial must-answer.Generated by Claude Code
os-zhuang commentedon Aug 24, 2026
Claim —
domain:clilane execution seat, session019siH5jDmk5hrayvfyojUqR, round R34 (second wave).Branch:
claude/issue-11642-truncation-remainder-notices.Declared file surface:
packages/cli/src/commands/compile.ts,packages/cli/src/commands/validate.ts,packages/cli/src/commands/init.ts,packages/cli/src/utils/format.tsif the shared printer needs a seam, + tests + a changeset.Both serial constraints are discharged — by merges, not by queue position
objectstack build/validateprint at most 50 author-time warnings and drop the rest with no notice #11529".objectstack build/validateprint at most 50 author-time warnings and drop the rest with no notice #11529 landed as fix(cli):os buildnames the author-time warnings it withheld instead of stopping dead at 50 #11645 (merged), and itsprintAuthoringAdvisoriesinpackages/cli/src/utils/format.tsis this card's spec.os build --jsondrops the undeclared-authoring-key warnings thatos validate --jsoncarries #11643 behind the same file.os build --jsondrops the undeclared-authoring-key warnings thatos validate --jsoncarries #11643 landed as fix(cli): carry the undeclared-authoring-key warnings in theos build --jsonpayload #11726 (merged 2026-08-24T14:43Z). A serial is released by the merge, and both are merged.⇒ Dispatchable now, and the dispatch requires re-measurement on the current
origin/mainrather than on either card's numbers.⛔ The population on this card is wrong in BOTH directions — that is the main instruction
Comment
5393983162records it and the dev must start from that, not from the table in the body:compile.ts:144is MISSING — the--strict-bodyrefusal path caps at 20, not 50, which is why a 50-anchored sweep could not see it. It states the true total in its header and then shows 20, with no remainder line: byte-for-byte the shape this card already counts as a defect at:293. Same defect, different cap.compile.ts:173is NOT a defect — ⛔ do not add it. It already prints… and ${n - 20} moreplus a pointer at the complete-output path. It is the in-repo precedent, and the fix for the rest should copy its shape rather than invent one.⭐ The general rule, and the reason this card needs care: a population selected by
slice(0, 50)answers "where does the number 50 appear", not "where is output truncated in silence." Re-derive the worklist from the defect — a truncating render with no remainder line — and read each candidate's surrounding lines before counting it. A grep for the literal both misses and over-collects here, measurably.Line numbers have moved twice; the card's table is two merges stale
#11645 changed
:217, and #11726 changed the:293neighbourhood — that site's variable was renamedunknownKeyFindings→unknownKeyWarningsand the list is now formatted at the computation site. Theslice(0, 50)there is byte-unchanged and still has no notice, so it is still a defect, but nothing around it reads as the card describes. Locate every site by text, never by the card's line number.#11645's notice ends by pointing the reader at
--json"for the full list". That pointer is only honest for a list--jsonactually carries. #11726 madebuild --jsoncarry the undeclared-key warnings, so it is now true for that one — but this card's remaining sites include error paths (ruleErrors,docErrors, access-matrixdrift,init.ts'sreport.errors).⇒ Per site, before pointing at
--json, verify the JSON payload actually carries that list. A notice whose remedy does not work is worse than a silent cut: it sends the author down a path that returns the same truncated view. If a site's list is not in--json, either name a remedy that exists or state the remainder without a pointer — ⛔ do not add the list to--jsonhere, that is #11643's class and a separate contract question.Clause ②
Adding remainder notices changes text output, which is not a machine contract ⇒ likely
no. It flips toyesif any site's fix touches a--jsonpayload. Answer against what you actually built, and if you find yourself wanting to add a list to--json, ⛔ stop and report instead.Standing
Worktree first, ⛔ never
git stash, ⛔ never editcontent/docs/releases/, ⛔ do not touchpackages/spec. Every pin shown red-before / green-after; ⛔ a reverse-check may not use a substring of the term under test; ⛔ a negative result is evidence only after the same instrument has produced a positive. One changeset. Draft PR withFixes #11642. ⛔ Do not mark ready, ⛔ do not arm auto-merge — that is this seat's step.Findings outside the surface: file them unlabelled for triage to grade — ⛔ not
finding, which is reserved for observation-class items (.claude/agents/os-dev.md; two devs corrected this seat's boilerplate on it today and both were right).Generated by Claude Code
claude commentedon Aug 24, 2026
{ "issue": 11642, "status": "done", "branch": "claude/issue-11642-truncation-remainder-notices", "pr": "https://github.com/objectstack-ai/objectstack/pull/11776", "pr_url": "https://github.com/objectstack-ai/objectstack/pull/11776", "premise_still_valid": true, "summary": "All nine re-derived sites now name the remainder they withheld, through one shared implementation of the notice (printTruncationNotice in packages/cli/src/utils/format.ts) that printAuthoringAdvisories was refactored onto without changing a byte of #11529's output. Every cap keeps its value; at or under a cap nothing new is printed. The card's eight rows were all genuine, so the premise held — but the population was still wrong in both directions and was re-derived from the defect (a truncating render with no remainder line): compile.ts:145 (--strict-body, cap 20) was added, and compile.ts:174 (bodyExtractionWarnings, cap 20) is the untouched precedent, not a sibling. A sweep of the rest of packages/** found four more capped renders, all of which already name their remainder, so there is no wider silent-truncation population outstanding.", "worklist_rederived": [ { "site": "compile.ts:145 issues (--strict-body refusal)", "cap": 20, "verdict": "IN", "evidence": "Missing from the card's ledger. printError states the true total ('N callable(s) lack a metadata body'), then 20 rows, no remainder line, this.exit(1) — byte-for-byte the shape the card grades as a defect at the undeclared-keys site. Only the cap value differs, which is exactly why a 50-anchored sweep could not see it." }, { "site": "compile.ts:236 ruleErrors", "cap": 50, "verdict": "IN", "evidence": "Gating rule failures, exit 1, capped loop with no notice." }, { "site": "compile.ts:306 unknownKeyWarnings", "cap": 50, "verdict": "IN", "evidence": "Advisory; header already prints the true total ('Undeclared authoring keys (N)'), so the block stated two numbers that disagreed. Renamed from unknownKeyFindings and re-formatted at the computation site by #11726; the slice(0, 50) is byte-unchanged and was still silent." }, { "site": "compile.ts:346 access-matrix drift", "cap": 50, "verdict": "IN", "evidence": "exit 1, capped loop with no notice." }, { "site": "compile.ts:373 docErrors", "cap": 50, "verdict": "IN", "evidence": "exit 1, capped loop with no notice." }, { "site": "validate.ts:140 ruleErrors", "cap": 50, "verdict": "IN", "evidence": "exit 1, capped loop with no notice; the block's own comment ('could not see how deep the hole went') is the reason the silence matters here." }, { "site": "validate.ts:212 docErrors", "cap": 50, "verdict": "IN", "evidence": "exit 1, capped loop with no notice." }, { "site": "init.ts:893 report.advisories", "cap": 50, "verdict": "IN", "evidence": "Scaffold self-test advisories, scaffold continues, no notice. The block was a byte-for-byte copy of printAuthoringAdvisories' body; it now calls that printer." }, { "site": "init.ts:908 report.errors", "cap": 50, "verdict": "IN", "evidence": "Scaffold self-test failures, no notice." }, { "site": "compile.ts:174 lowering.bodyExtractionWarnings", "cap": 20, "verdict": "OUT — the PRECEDENT, untouched", "evidence": "Already prints '… and ${n - 20} more' plus a pointer at the complete-output path ('run os build --strict-body for the full diagnostic'). Listing it would have been the obvious next move from the grep and would have been wrong. A pin asserts both of its lines are still present byte for byte, because a change to it is a change to the standard everything else now copies." }, { "site": "compile.ts:222 ruleAdvisories", "verdict": "OUT — already fixed", "evidence": "#11645 routed it through printAuthoringAdvisories. Reused, not re-done; its pin file is untouched and still green." }, { "site": "compile.ts:108 conversionNotices / :270 capPreflight.errors / :276 capPreflight.warnings / :380 docWarnings", "verdict": "OUT", "evidence": "Uncapped — the loop renders the whole list. No truncation, nothing to name." }, { "site": "validate.ts:180 capProviderErrors / :363 warnings", "verdict": "OUT", "evidence": "Uncapped." }, { "site": "init.ts:61-63 s.slice(0, 20)", "verdict": "OUT", "evidence": "String truncation of a namespace identifier, not a list render." }, { "site": "init.ts printCreatedFilesSummary; formatZodErrors (all three commands)", "verdict": "OUT", "evidence": "Uncapped; formatZodErrors even closes with 'N validation error(s) total'." }, { "site": "packages/** outside the three printers: runtime/app-plugin.ts:1155, service-storage/verify-file-references.ts:352, spec/scripts/build-schemas.ts:595/1319-1320/2244", "verdict": "OUT", "evidence": "Swept for the same defect; all four already name their remainder ('…and N more error(s)', '… and N more'). No wider silent-truncation population to file." } ], "json_pointer_check": [ { "site": "compile issues (--strict-body)", "json_carries_list": true, "payload": "{ success: false, error: 'strict-body: missing body', issues } — same block as the text face", "notice": "remainder + 're-run with --json for the full list'" }, { "site": "compile ruleErrors", "json_carries_list": true, "payload": "{ …, error: 'author-time rules failed', issues: ruleErrors, warnings: ruleAdvisories } — same exit", "notice": "remainder + --json pointer" }, { "site": "compile unknownKeyWarnings", "json_carries_list": true, "payload": "terminal success payload, warnings: [...ruleAdvisories, ...unknownKeyWarnings] (put there by #11726)", "notice": "remainder + --json pointer", "caveat": "Honest for a run that reaches the end. A run that fails at a LATER gate emits a failure payload carrying no `warnings`. Same property as #11645's own notice. Filed as #11772, not fixed here — closing it changes a --json payload shape." }, { "site": "compile drift", "json_carries_list": true, "payload": "{ …, error: 'access matrix drift', changes: drift } — same exit", "notice": "remainder + --json pointer" }, { "site": "compile docErrors", "json_carries_list": true, "payload": "{ …, error: 'docs validation failed', issues: docErrors } — same exit", "notice": "remainder + --json pointer" }, { "site": "validate ruleErrors", "json_carries_list": true, "payload": "{ valid: false, errors: ruleErrors, warnings: ruleAdvisories } — same exit", "notice": "remainder + --json pointer" }, { "site": "validate docErrors", "json_carries_list": true, "payload": "{ valid: false, errors: docErrors, warnings: ruleAdvisories } — same exit", "notice": "remainder + --json pointer" }, { "site": "init report.advisories", "json_carries_list": false, "payload": "os init declares NO --json flag: static flags = template, install, package-manager", "notice": "remainder stated, NO pointer (printAuthoringAdvisories(..., null))" }, { "site": "init report.errors", "json_carries_list": false, "payload": "same — no --json face", "notice": "remainder stated, NO pointer; the block's existing 'this is a CLI bug, please report it' line is the action that does exist here" } ], "clause_2": "no", "files_touched": [ "packages/cli/src/utils/format.ts", "packages/cli/src/commands/compile.ts", "packages/cli/src/commands/validate.ts", "packages/cli/src/commands/init.ts", "packages/cli/test/truncation-remainder-notices.test.ts (new)", ".changeset/cli-truncation-remainder-notices.md (new)" ], "pins_added": { "file": "packages/cli/test/truncation-remainder-notices.test.ts — 32 tests, run throughout alongside #11645's untouched 7", "green_after": "Test Files 2 passed (2) · Tests 39 passed (39)", "red_before_1_sites": "Three command files restored to origin/main, helper and pins kept (git restore --source=origin/main --worktree; mutation proved on disk first: 0 printer calls per file, 6+2+2 capped for-of loops back). Result: Test Files 1 failed | 1 passed, Tests 10 failed | 29 passed — all nine per-site pins red plus the sweep, which names the missing site in its own words: \"AssertionError: compile.ts:145 truncates issues with no remainder line: expected ' for (const w of issues.slic…' to match /… and |not shown|printTruncationNotice/\". Tree restored by an EXIT/INT/TERM trap; git status clean afterwards.", "red_before_2_ablation": "The printers are new, so 'the function does not exist' is not a meaningful red. Instead printTruncationNotice was ablated to a no-op (return; at the top of the body) and the suite re-measured: Test Files 2 failed (2), Tests 13 failed | 26 passed. Ten of the reds are the new over-the-cap pins; THREE are from #11529's untouched pin file, which is what proves the refactor really routed the existing behaviour through the shared implementation rather than merely agreeing with it today.", "ablation_rebuild_and_disk_proof": "No build stands between the mutation and the measurement: both suites import '../src/utils/format.js', i.e. SOURCE (grepped and shown in the run log), so no dist is involved and none was rebuilt. The mutation was proved on disk before measuring, anchored on the text meant to change — injected marker OS_ABLATION_11642 count 1, 'export function printTruncationNotice' still count 1, its four call sites still count 4 — never on an editor exit code. The script carried trap '<restore>' EXIT INT TERM so a cap kill mid-mutation could not leave the tree ablated. Restore leg verified both ways: marker count 0 and git hash-object equal to the committed blob (1dbe354f1f41ab4ed159eff3f9bb085796cc803e), then 39/39 green again.", "short_list_control": "Green in BOTH states, which is the point — a printer that ALWAYS printed a notice would satisfy every red-going pin above. Per printer: a list shorter than the cap prints no notice at all; a list exactly AT the cap prints every row and no notice; an empty list prints nothing (not an empty notice); and the rows are unchanged — 50th present, 51st absent, notice adds rather than replaces. All of these stayed green under the ablation.", "instrument_discipline": "Every per-site pin asserts a positive first (text independently known present at that site, deliberately not a substring of the term under test) before any absence is read as evidence. The --json-flag pin proves itself on compile.ts and validate.ts before reporting the absence in init.ts." }, "checks_run": "All at d087304ea, the final commit; the diff-scoped and ratchet families were re-run AFTER it, because the changeset gates only see the changeset once committed. (1) pnpm --filter '@objectstack/cli^...' build — dependency closure built before any verdict was read. (2) pnpm --filter @objectstack/cli typecheck — 'tsc --noEmit', exit 0 with the script name echoed; it covers src only, so the new test file's type evidence is check:type-check-debt --re-measure reporting 'none above its recorded number' for TEST_DEBT['@objectstack/cli']. (3) Full package suite, pnpm --filter @objectstack/cli exec vitest run --maxWorkers=4 — 172 files / 1972 tests passed. (4) pnpm lint — the full repo-wide 'eslint . --no-inline-config', exit 0 in 81s; NOT narrowed, so no narrowing declaration is owed. (5) The 19 path-derived families from 'node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack' (derived from the real change set, no hand-written list) plus the five convention-triggered ones for adding a test file — all green: changeset-gate-self-tests, cli-test-child-env, cross-package-test-inputs, i18n, i18n-coverage, objectui-changeset, published-files, slot-lookup, test-source-alias, type-check-coverage, type-check-debt, type-source-resolution, check-adr-0087-registration, check-changeset-no-major, check-empty-changeset, check-plugin-teardown-shape, docs-audit/check-affected-docs, release-rehearsal-clone --self-test, nul-bytes, engine-double-contract, where-matcher, query-options-erasure. (6) check:i18n and check:i18n-coverage first refused as PREREQUISITE NOT MET (unbuilt CLI, then connector-mcp / connector-openapi / the showcase closure) — recorded because that refusal is not a pass; both green only after those closures were built. Every heavy command ran through scripts/pm/os-verify-lock.sh; every exit code was captured before any pipe. A raw ESC byte that the editor materialised into the test file was caught by the control-byte scan and replaced with its escaped spelling before commit.", "tests": "See pins_added and checks_run. Green at d087304ea: new pins 32/32 plus #11529's untouched 7/7; full @objectstack/cli suite 172 files / 1972 tests passed; pnpm lint exit 0 repo-wide. Reverse verification ran twice from the COMMITTED state — once against pre-fix command files (10 red / 29 green) and once as an ablation of the shared notice (13 red / 26 green, including 3 assertions in #11529's own untouched pin file). The ablation involved no build because the suite resolves the module from source; the mutation was confirmed on disk by grepping the injected marker and the surviving function and call sites, and the restore leg by git hash-object matching the committed blob.", "open_questions": [], "out_of_scope_findings": [ "filed as #11772: `os build --json`'s failure payloads carry no `warnings`, so the advisory notices' `--json` pointer is a dead end when a later gate fails — distinct from the open #11727 (that one is about lists computed under a !flags.json guard; this is about lists that DO reach the payload, on one exit only). Filed unlabelled, unassigned.", "filed as #11774: report CSV/HTML export infers its columns from the first 50 rows and applies that projection to all of them, so a field first appearing at row 51 is dropped from every row of the exported artifact (packages/plugins/plugin-reports/src/report-service.ts pickFields, feeding renderCsv and renderHtmlTable; renderReport's default branch is CSV). Filed unlabelled, unassigned." ] }Generated by Claude Code
claude commentedon Aug 24, 2026
Amended after PM review — supersedes the report in comment 5397905813; the only change to the work is a comment at the
unknownKeyWarningssite (95ea4cc).{ "issue": 11642, "status": "done", "branch": "claude/issue-11642-truncation-remainder-notices", "pr": "https://github.com/objectstack-ai/objectstack/pull/11776", "pr_url": "https://github.com/objectstack-ai/objectstack/pull/11776", "premise_still_valid": true, "summary": "All nine re-derived sites now name the remainder they withheld, through one shared implementation of the notice (printTruncationNotice in packages/cli/src/utils/format.ts) that printAuthoringAdvisories was refactored onto without changing a byte of #11529's output. Every cap keeps its value; at or under a cap nothing new is printed. The card's eight rows were all genuine, so the premise held — but the population was still wrong in both directions and was re-derived from the defect (a truncating render with no remainder line): compile.ts:145 (--strict-body, cap 20) was added, and compile.ts:174 (bodyExtractionWarnings, cap 20) is the untouched precedent, not a sibling. A sweep of the rest of packages/** found four more capped renders, all of which already name their remainder, so there is no wider silent-truncation population outstanding. PM review round: one comment-level change requested and pushed as 95ea4cc0b — the unknownKeyWarnings site's comment claimed the --json pointer was honest without saying that it resolves on the SUCCESS exit only, which is the same shape (a claim true in one branch, read as general) as the silence this card removes. The comment now states the condition, names #11772, and contrasts the six error-path sites whose --json branch sits in the same block. Comment only: every added and removed line is a // line.", "worklist_rederived": [ { "site": "compile.ts:145 issues (--strict-body refusal)", "cap": 20, "verdict": "IN", "evidence": "Missing from the card's ledger. printError states the true total ('N callable(s) lack a metadata body'), then 20 rows, no remainder line, this.exit(1) — byte-for-byte the shape the card grades as a defect at the undeclared-keys site. Only the cap value differs, which is exactly why a 50-anchored sweep could not see it." }, { "site": "compile.ts:236 ruleErrors", "cap": 50, "verdict": "IN", "evidence": "Gating rule failures, exit 1, capped loop with no notice." }, { "site": "compile.ts:306 unknownKeyWarnings", "cap": 50, "verdict": "IN", "evidence": "Advisory; header already prints the true total ('Undeclared authoring keys (N)'), so the block stated two numbers that disagreed. Renamed from unknownKeyFindings and re-formatted at the computation site by #11726; the slice(0, 50) is byte-unchanged and was still silent." }, { "site": "compile.ts:346 access-matrix drift", "cap": 50, "verdict": "IN", "evidence": "exit 1, capped loop with no notice." }, { "site": "compile.ts:373 docErrors", "cap": 50, "verdict": "IN", "evidence": "exit 1, capped loop with no notice." }, { "site": "validate.ts:140 ruleErrors", "cap": 50, "verdict": "IN", "evidence": "exit 1, capped loop with no notice; the block's own comment ('could not see how deep the hole went') is the reason the silence matters here." }, { "site": "validate.ts:212 docErrors", "cap": 50, "verdict": "IN", "evidence": "exit 1, capped loop with no notice." }, { "site": "init.ts:893 report.advisories", "cap": 50, "verdict": "IN", "evidence": "Scaffold self-test advisories, scaffold continues, no notice. The block was a byte-for-byte copy of printAuthoringAdvisories' body; it now calls that printer." }, { "site": "init.ts:908 report.errors", "cap": 50, "verdict": "IN", "evidence": "Scaffold self-test failures, no notice." }, { "site": "compile.ts:174 lowering.bodyExtractionWarnings", "cap": 20, "verdict": "OUT — the PRECEDENT, untouched", "evidence": "Already prints '… and ${n - 20} more' plus a pointer at the complete-output path ('run os build --strict-body for the full diagnostic'). Listing it would have been the obvious next move from the grep and would have been wrong. A pin asserts both of its lines are still present byte for byte, because a change to it is a change to the standard everything else now copies." }, { "site": "compile.ts:222 ruleAdvisories", "verdict": "OUT — already fixed", "evidence": "#11645 routed it through printAuthoringAdvisories. Reused, not re-done; its pin file is untouched and still green." }, { "site": "compile.ts:108 conversionNotices / :270 capPreflight.errors / :276 capPreflight.warnings / :380 docWarnings", "verdict": "OUT", "evidence": "Uncapped — the loop renders the whole list. No truncation, nothing to name." }, { "site": "validate.ts:180 capProviderErrors / :363 warnings", "verdict": "OUT", "evidence": "Uncapped." }, { "site": "init.ts:61-63 s.slice(0, 20)", "verdict": "OUT", "evidence": "String truncation of a namespace identifier, not a list render." }, { "site": "init.ts printCreatedFilesSummary; formatZodErrors (all three commands)", "verdict": "OUT", "evidence": "Uncapped; formatZodErrors even closes with 'N validation error(s) total'." }, { "site": "packages/** outside the three printers: runtime/app-plugin.ts:1155, service-storage/verify-file-references.ts:352, spec/scripts/build-schemas.ts:595/1319-1320/2244", "verdict": "OUT", "evidence": "Swept for the same defect; all four already name their remainder ('…and N more error(s)', '… and N more'). No wider silent-truncation population to file." } ], "json_pointer_check": [ { "site": "compile issues (--strict-body)", "json_carries_list": true, "payload": "{ success: false, error: 'strict-body: missing body', issues } — same block as the text face", "notice": "remainder + 're-run with --json for the full list'" }, { "site": "compile ruleErrors", "json_carries_list": true, "payload": "{ …, error: 'author-time rules failed', issues: ruleErrors, warnings: ruleAdvisories } — same exit", "notice": "remainder + --json pointer" }, { "site": "compile unknownKeyWarnings", "json_carries_list": true, "payload": "terminal success payload, warnings: [...ruleAdvisories, ...unknownKeyWarnings] (put there by #11726)", "notice": "remainder + --json pointer", "caveat": "Honest for a run that reaches the end. A run that fails at a LATER gate emits a failure payload carrying no `warnings`. Same property as #11645's own notice. Filed as #11772, not fixed here — closing it changes a --json payload shape. PM review: the caveat is now stated AT THE SITE in compile.ts, not only here and in the PR body (95ea4cc0b)." }, { "site": "compile drift", "json_carries_list": true, "payload": "{ …, error: 'access matrix drift', changes: drift } — same exit", "notice": "remainder + --json pointer" }, { "site": "compile docErrors", "json_carries_list": true, "payload": "{ …, error: 'docs validation failed', issues: docErrors } — same exit", "notice": "remainder + --json pointer" }, { "site": "validate ruleErrors", "json_carries_list": true, "payload": "{ valid: false, errors: ruleErrors, warnings: ruleAdvisories } — same exit", "notice": "remainder + --json pointer" }, { "site": "validate docErrors", "json_carries_list": true, "payload": "{ valid: false, errors: docErrors, warnings: ruleAdvisories } — same exit", "notice": "remainder + --json pointer" }, { "site": "init report.advisories", "json_carries_list": false, "payload": "os init declares NO --json flag: static flags = template, install, package-manager", "notice": "remainder stated, NO pointer (printAuthoringAdvisories(..., null))" }, { "site": "init report.errors", "json_carries_list": false, "payload": "same — no --json face", "notice": "remainder stated, NO pointer; the block's existing 'this is a CLI bug, please report it' line is the action that does exist here" } ], "clause_2": "no", "files_touched": [ "packages/cli/src/utils/format.ts", "packages/cli/src/commands/compile.ts", "packages/cli/src/commands/validate.ts", "packages/cli/src/commands/init.ts", "packages/cli/test/truncation-remainder-notices.test.ts (new)", ".changeset/cli-truncation-remainder-notices.md (new)" ], "pins_added": { "file": "packages/cli/test/truncation-remainder-notices.test.ts — 32 tests, run throughout alongside #11645's untouched 7", "green_after": "Test Files 2 passed (2) · Tests 39 passed (39)", "red_before_1_sites": "Three command files restored to origin/main, helper and pins kept (git restore --source=origin/main --worktree; mutation proved on disk first: 0 printer calls per file, 6+2+2 capped for-of loops back). Result: Test Files 1 failed | 1 passed, Tests 10 failed | 29 passed — all nine per-site pins red plus the sweep, which names the missing site in its own words: \"AssertionError: compile.ts:145 truncates issues with no remainder line: expected ' for (const w of issues.slic…' to match /… and |not shown|printTruncationNotice/\". Tree restored by an EXIT/INT/TERM trap; git status clean afterwards.", "red_before_2_ablation": "The printers are new, so 'the function does not exist' is not a meaningful red. Instead printTruncationNotice was ablated to a no-op (return; at the top of the body) and the suite re-measured: Test Files 2 failed (2), Tests 13 failed | 26 passed. Ten of the reds are the new over-the-cap pins; THREE are from #11529's untouched pin file, which is what proves the refactor really routed the existing behaviour through the shared implementation rather than merely agreeing with it today.", "ablation_rebuild_and_disk_proof": "No build stands between the mutation and the measurement: both suites import '../src/utils/format.js', i.e. SOURCE (grepped and shown in the run log), so no dist is involved and none was rebuilt. The mutation was proved on disk before measuring, anchored on the text meant to change — injected marker OS_ABLATION_11642 count 1, 'export function printTruncationNotice' still count 1, its four call sites still count 4 — never on an editor exit code. The script carried trap '<restore>' EXIT INT TERM so a cap kill mid-mutation could not leave the tree ablated. Restore leg verified both ways: marker count 0 and git hash-object equal to the committed blob (1dbe354f1f41ab4ed159eff3f9bb085796cc803e), then 39/39 green again.", "short_list_control": "Green in BOTH states, which is the point — a printer that ALWAYS printed a notice would satisfy every red-going pin above. Per printer: a list shorter than the cap prints no notice at all; a list exactly AT the cap prints every row and no notice; an empty list prints nothing (not an empty notice); and the rows are unchanged — 50th present, 51st absent, notice adds rather than replaces. All of these stayed green under the ablation.", "instrument_discipline": "Every per-site pin asserts a positive first (text independently known present at that site, deliberately not a substring of the term under test) before any absence is read as evidence. The --json-flag pin proves itself on compile.ts and validate.ts before reporting the absence in init.ts." }, "checks_run": "All at d087304ea, the final commit; the diff-scoped and ratchet families were re-run AFTER it, because the changeset gates only see the changeset once committed. (1) pnpm --filter '@objectstack/cli^...' build — dependency closure built before any verdict was read. (2) pnpm --filter @objectstack/cli typecheck — 'tsc --noEmit', exit 0 with the script name echoed; it covers src only, so the new test file's type evidence is check:type-check-debt --re-measure reporting 'none above its recorded number' for TEST_DEBT['@objectstack/cli']. (3) Full package suite, pnpm --filter @objectstack/cli exec vitest run --maxWorkers=4 — 172 files / 1972 tests passed. (4) pnpm lint — the full repo-wide 'eslint . --no-inline-config', exit 0 in 81s; NOT narrowed, so no narrowing declaration is owed. (5) The 19 path-derived families from 'node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack' (derived from the real change set, no hand-written list) plus the five convention-triggered ones for adding a test file — all green: changeset-gate-self-tests, cli-test-child-env, cross-package-test-inputs, i18n, i18n-coverage, objectui-changeset, published-files, slot-lookup, test-source-alias, type-check-coverage, type-check-debt, type-source-resolution, check-adr-0087-registration, check-changeset-no-major, check-empty-changeset, check-plugin-teardown-shape, docs-audit/check-affected-docs, release-rehearsal-clone --self-test, nul-bytes, engine-double-contract, where-matcher, query-options-erasure. (6) check:i18n and check:i18n-coverage first refused as PREREQUISITE NOT MET (unbuilt CLI, then connector-mcp / connector-openapi / the showcase closure) — recorded because that refusal is not a pass; both green only after those closures were built. Every heavy command ran through scripts/pm/os-verify-lock.sh; every exit code was captured before any pipe. A raw ESC byte that the editor materialised into the test file was caught by the control-byte scan and replaced with its escaped spelling before commit. AMENDMENT ROUND at 95ea4cc0b: the worktree had been torn down, so it was recreated on the branch and the dependency closure REBUILT before any verdict was read — the first pin run in the fresh tree failed to collect with 'Failed to resolve entry for package @objectstack/spec', the documented unbuilt-closure trap, and that failure is recorded rather than reported as a code result. After the build: pins 39/39 green, pnpm --filter @objectstack/cli typecheck exit 0 (script name echoed), and the ratchet plus diff-scoped families re-run ON THE NEW HEAD before this report was updated — nul-bytes, slot-lookup, where-matcher, query-options-erasure, check-empty-changeset, check-changeset-no-major, check-adr-0087-registration, cross-package-test-inputs, cli-test-child-env, all green. The full package suite and repo-wide lint were not re-run for a comment-only diff; CI covers them, and this narrowing is declared rather than silent. PR confirmed from the remote after the push: draft true, auto_merge null, head 95ea4cc0b.", "tests": "See pins_added and checks_run. Green at d087304ea: new pins 32/32 plus #11529's untouched 7/7; full @objectstack/cli suite 172 files / 1972 tests passed; pnpm lint exit 0 repo-wide. Reverse verification ran twice from the COMMITTED state — once against pre-fix command files (10 red / 29 green) and once as an ablation of the shared notice (13 red / 26 green, including 3 assertions in #11529's own untouched pin file). The ablation involved no build because the suite resolves the module from source; the mutation was confirmed on disk by grepping the injected marker and the surviving function and call sites, and the restore leg by git hash-object matching the committed blob. Amendment round (95ea4cc0b, comment-only): pins 39/39 green again after rebuilding the dependency closure in the recreated worktree; no new pin, because no behaviour moved — the diff contains no non-comment line in either direction, shown by filtering the unified diff for lines that are not '//' lines and getting none.", "open_questions": [], "out_of_scope_findings": [ "filed as #11772: `os build --json`'s failure payloads carry no `warnings`, so the advisory notices' `--json` pointer is a dead end when a later gate fails — distinct from the open #11727 (that one is about lists computed under a !flags.json guard; this is about lists that DO reach the payload, on one exit only). Filed unlabelled, unassigned.", "filed as #11774: report CSV/HTML export infers its columns from the first 50 rows and applies that projection to all of them, so a field first appearing at row 51 is dropped from every row of the exported artifact (packages/plugins/plugin-reports/src/report-service.ts pickFields, feeding renderCsv and renderHtmlTable; renderReport's default branch is CSV). Filed unlabelled, unassigned." ], "final_commit": "95ea4cc0b", "pr_state_at_report": "draft: true, auto_merge: null, 3 commits — this seat neither flipped ready nor armed anything" }Generated by Claude Code
test(cli): update the #11642 payload-literal pin for the widened warn…
fix(cli): carry the capability-provider and package-docs warnings in …