Repository navigation
publish-smoke-pack parses pnpm pack --json stdout whole, so @objectstack/spec's prepare (PR #22706) reds the scaffold and release smokes on every pull — re-entry of #22705, unblocks #22658 #22752
Description
Activity
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsTriage: first grade,
bug·priority:p1·domain:devx·area:devpath·pm:queue. A valid re-entry of #22705, and the publish lane's first pack is redTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-11T01:51Z. ⛔ Not a claim, ⛔ not a dispatch.- Re-entry check: both lines hold.
Unblocks: #22658: open, p2, dispatched.Guards:the publish lane:scripts/publish-smoke.shpacks every publishable package through the samepackOne.- This is exactly the re-entry the close named (
6100515504).
- Re-read on
mainbfc15d275b:packages/spec/package.json:268declares"prepare": "pnpm gen:migration-registry".scripts/publish-smoke-pack.mjspackOneparses the wholepnpm pack --jsonstdout.- pnpm runs
prepareon pack and prints its banner first, so the parse throws.
- This amends my close
6100515504.- I wrote "No
package.json… declaresprepack" and keyed the re-entry onprepack. - The class is any lifecycle script
pnpm packruns, andprepareis one. The direction already covered it ("parse the last JSON document"), but my reading of the trap's reach was too narrow.
- I wrote "No
- Why p1: the release smoke reds at its first pack, so no release (the v18 prerelease included) passes its publish smoke until this lands. Release work is graded p1. The scaffold smoke is red on every pull since
ed1de8c2d. - Lane:
scripts/code-quality gates aredomain:devxby the anchor rule. - Direction, as filed:
- Parse the last JSON document on stdout, or use a measured quiet flag.
- ⛔
--ignore-scriptsonly if measured equal: it would skip thepreparethat the registry needs. - ⛔ The spec's
preparestays, because ruling B on [Decision] the migration registryregistry.tsis the last committed generated aggregate on the ADR-0087 D3 path: keep it and finish B′, or generate it at build #22554 needs it. - A self-test with a fake
pnpmthat prints a banner and then the JSON. - ⛔ No change to what the smokes judge.
- Done when:
os-create-smokeis green on a pull at or aftered1de8c2d, and the self-test is red with the fix ablated.
- Re-entry check: both lines hold.
- addedarea:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingSomething isn't workingpriority:p1High: required for production / M2High: required for production / M2and removed
on Oct 11, 2026 objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsClaim: PM loop round 1
Session:session_01VQftDB6niKg91LeQANrS7d
Account:os-zhuang(the seat's linked user asGET /useranswers it; always the card's assignee)
Branch:claude/issue-22752-pack-json-last-document
Worktree:objectstack-issue-22752
Domain:domain:devx
Seat:domain:devx#1
File surface:scripts/publish-smoke-pack.mjs, covering thepackOneparse and a self-test with a fakepnpmthat prints a lifecycle banner before the JSON. The self-test goes in the file's own--self-test, or in a sibling underscripts/, plus the self-test wiring thatcheck-self-test-wired/check-self-test-workflow-commandsdemand. ⛔ No change topackages/spec/package.json(itspreparestays: ruling B on #22554). ⛔ No change to what the smokes judge. (Stop on breach; explain in the report.)
Container & model:S, not mechanical: the parse choice is a measured design call, so M treatment,mode:subagent,model: opus(the default tier;dispatch-gates --tier --repo objectstack-ai/objectstack scripts/publish-smoke-pack.mjsatbf515e724prints "no path-derived mandate")
Clause-②: no
Rootscripts/tooling publishes nothing from any released package, and a parse fix widens no published accept set. Changeset:skip-changesetper the lane charter (rootscripts/).
Responsibility:scripts/publish-smoke-pack.mjspackOne(this repository's smoke tooling) parses the whole stdout ofpnpm pack --json| none: no measured pnpm flag keeps lifecycle output off stdout; the dev measures one before relying on it | every pull request reaches it throughos-create-smoke.yml, and every release throughscripts/publish-smoke.sh; both are red today
Thread-read: 6104362780
Serial constraints cleared: PR #22691 (domain:skills, #22658's package PR, head60b8d9a1f) also edits this file, at the docblock count line near:36(69 → 70). That is disjoint frompackOne(:121–:137), so this is ordinary concurrency and the later lander mergesmain. #22691 is also this fix's beneficiary: its scaffold smoke is red until this lands. Nopm:dispatchedclaim in any lane names this file. #22085's surface (release.yml,scripts/sync-release-index-currency.mjs) is disjoint. Open PRs' file lists read at 2026-10-11T02:23Z.objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 22752,
"status": "done",
"branch": "claude/issue-22752-pack-json-last-document",
"pr": "#22753",
"session": "session_01VQftDB6niKg91LeQANrS7d (subagent; the parent's id, as the relay's route read it from CLAUDE_CODE_REMOTE_SESSION_ID)",
"premise_still_valid": true,
"summary": "Fixed in one file. scripts/publish-smoke-pack.mjs moves the pack parse into an exported pure helper, parsePackStdout(stdout, name). The helper returns the LAST JSON document on pnpm pack --json stdout: it walks the brace offsets from the end, and the first suffix that JSON.parse accepts whole is the result. packOne calls the helper. The filename check, the loud named refusal that carries stdout, the population, the overrides map and everything the smokes judge are unchanged. The spec's prepare is untouched. The self-test battery grows 4 -> 10, pinned at 10, including a case that drives packOne through a fake pnpm on PATH which prints a banner and then the JSON. Verdicts: H1 CONFIRMED. Verbatim stdout head ofpnpm pack --json --pack-destination Din packages/spec at bf515e7 (pnpm 10.31.0; exit 0, stderr empty, 974 lines; WT = worktree path): "\n> @objectstack/spec@17.7.0 prepare WT/packages/spec\n> pnpm gen:migration-registry\n\n\n> @objectstack/spec@17.7.0 gen:migration-registry WT/packages/spec\n> tsx scripts/build-migration-registry.ts\n\nbuild-migration-registry --self-test: ok\n✓ src/migrations/registry.ts is current (420 semantic, 258 retired-key, 222 retired-def)\n{\n \"name\": \"@objectstack/spec\",". Parsing that stdout whole throws Unexpected token '>' (the base behaviour), and parsePackStdout returns the filename. H2 CONFIRMED with a refinement. pnpm 10.31.0's pack handler (dist/pnpm.cjs) runs prepack+prepare before the tarball and postpack right after it, all inside api() with stdio inherit. The CLI writes the returned JSON only after the handler returns, and the update notifier runs for install/add only. A throwaway package with all three hooks measured PREPACK/PREPARE/POSTPACK output all ABOVE the document, nothing after it. Census of 81 workspace members (69 publishable): no prepack/postpack anywhere; prepare only on @objectstack/spec and the private root; prepublishOnly on @objectstack/console (pack does not run it). The refinement is measured: a script whose last write has no newline glues onto the document's opening brace (postpackprintf \"{ not json\"leaves the line{ not json{). So the scan is per brace, not per line start. Selector choice: last document over the filename-carrying document. Trailing output is unreachable, so the last-document selector keeps it LOUD, whereas a filename-keyed search would skip it silently. The filename check still runs on the selected document. H3 CONFIRMED. --silent, --reporter=silent, --loglevel=error, --loglevel=silent, --reporter=ndjson and --reporter=append-only, each placed before or afterpack, all leave the banner and the script output on stdout. --ignore-scripts is REFUSED by pnpm 10.31.0 pack (ERROR Unknown option: 'ignore-scripts', exit 1). The env spelling npm_config_ignore_scripts=true works but skips prepare (src/migrations/registry.ts is gitignored, so prepare generates it), so it is not measured equal and is not used. H4: fake-pnpm case added through packOne itself (no deviation). selfTest() is now async and the dispatch awaits it, with the handshake unchanged in kind. The header and the no-path-population reason now say 'fixtures plus one fake pnpm in a temp dir', and the PR body explains why the declaration still holds. H5: pnpm -r list --json parsed whole cleanly in this worktree, and list runs no lifecycle script, so it is untouched (Acceptance notes). H6: main moved to 149294c without touching this file. #22691's count line is disjoint.",
"tests": "All at HEAD 34fff6b. (1)pnpm check:publish-smoke-pinexits 0 and prints 'SELF-TEST PASSED (10 cases)': 4 original cases plus 'a lifecycle banner above the document is skipped', 'the document alone still parses', 'script output glued to the opening brace is skipped', 'output AFTER the document reddens, by name, with stdout attached', 'stdout with no JSON document reddens, by name, with stdout attached' and 'packOne reads the document past a fake pnpm that prints a banner first'. (2) Ablation, run after the fix was committed:node scripts/ablation-replace.mjs --file REPO/scripts/publish-smoke-pack.mjs --anchor 'parsed = JSON.parse(stdout.slice(at));' --replacement 'parsed = JSON.parse(stdout);' -- node REPO/scripts/publish-smoke-pack.mjs --self-test. The mutation landed: anchor 1 -> 0, blob 8ef4ed21a713 -> a4e5fc75a5b6. Result: FAIL on the banner case, the glued-brace case and the fake-pnpm packOne case; the other 7 ok; 'SELF-TEST FAILED', command exited 1. Restore: 'blob after restore 8ef4ed21a713 == blob at HEAD', 'git diff HEAD empty', git status clean; the re-run prints 'SELF-TEST PASSED (10 cases)'. No build or dist is involved: a plain root script, run from source. (3) Real pnpm over the full population:os-verify-lock.sh -c 'node scripts/publish-smoke-pack.mjs D'printed 'Packing 69 publishable package(s)', 'packed 69/69', 'Wrote 69 override(s)', and 'VERDICT command-exit 0 · held the lock 18s' (shared-box seconds). That is the scaffold smoke's step 1, which fails at base on @objectstack/spec. (4) Real spec stdout fed to both parsers: whole-stdout JSON.parse THROWS; parsePackStdout returns {name:'@objectstack/spec', filename:D/objectstack-spec-17.7.0.tgz}. (5) bash scripts/create-scaffold-smoke.sh: NOT MEASURED. Reason: it refuses without packages/cli/dist, and it needs a full monorepo build plus registry installs per template, which exceeds the container's ~10-minute foreground cap on a shared box. Declared to CI: this PR's os-create-smoke job 'Scaffold outside the monorepo, install, build' was in_progress at report time.",
"gates": "Derived withnode scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths, so the change set comes from git: 1 path vs merge base bf515e7; the shallow checkout was noted. 31 commands, all exit 0, each exit code written to disk before any pipe: check-ci-filter-parity, check-closing-keyword-parity (+--self-test), check-comment-mask-corpus, check-declaration-mirrors (+--self-test), check-scripts-symbol-anchors (+--self-test), check-self-test-wired (+--self-test), check-self-test-workflow-commands (+--self-test), check-whole-set-label-write (+--self-test), pm/bare-root-worklist --self-test, and pnpm check: agent-test-spelling, bash32-floor, cli-command-ids, cross-package-test-inputs, driver-memory-census, entry-guard, gitlink-declared, lockstep-package-count ('all 3 occurrence(s) agree with the true count (69)'), nul-bytes, parse-guard, pm-dispatch-gates ('1828 cases pass', exit 0 captured to disk), pnpm-filter-targets, publish-smoke-pin ('SELF-TEST PASSED (10 cases)'), ratchet-remedy-authority, refd-timer-probe, watch-hint-literal. Reconciliation:--ranexits 0 and prints '✓ dispatch-gates --ran: 31 derived famil(ies) accounted for — 31 run, 0 NOT-MEASURED (a DERIVED zero — all 31 recorded an exit code and none of them is 3)'. ESLint, narrowed and proven: (a)eslint --print-config scripts/publish-smoke-pack.mjsputs the file in scope with 2 rules (no-restricted-imports, comment-swallow/no-code-inside-block-comment); (b)eslint --no-inline-config --format json scripts/publish-smoke-pack.mjsreports files 1, errors 0, warnings 0; (c) invariance: no type-aware linting (no parserOptions.project/projectService; eslint.config.mjs ~:327 says so), and both rules are per-file, so no untouched file's verdict can move. Repo-wide pnpm lint belongs to CI. Control-byte self-scan on the file: zero hits. CI at report time: 'Scaffold outside the monorepo, install, build' in_progress, 'Lint & Repo Gates' in_progress, 'Packed-tarball smoke (opt-in)' skipped.",
"line_budget": "scripts/publish-smoke-pack.mjs: +120 / -26 = net +94, within the +120 budget. Ten of the 26 deletions are in-place rewrites (check -> await check, the pin 4 -> 10, the header and marker wording).",
"files_changed": ["scripts/publish-smoke-pack.mjs"],
"deviations": [
"skip-changeset label NOT applied. Commandnode scripts/pm/label-write.mjs --repo objectstack-ai/objectstack --issue 22753 --add skip-changesetwas denied by the Claude Code auto-mode classifier ('External System Writes') before it ran, so no request was sent. Per the os-dev rule there was no retry by another route, and the seat should apply the label. Basis: root scripts/ ships in no package's files[], and PR #22753 carries no changeset.",
"Gate derivation ran WITHOUT path arguments: the os-dev definition says the script derives from merge-base and must not be fed a hand list, while the dispatch said to pass changed paths. Same answer, 31 commands over 1 path (scripts/publish-smoke-pack.mjs). The conflict is noted here, not resolved silently.",
"bash scripts/create-scaffold-smoke.sh was not run locally (reason in tests (5)). Its failing step 1 was run instead, in isolation, with real pnpm over all 69 packages, and it is green.",
"Attribution: commit trailers use the AGENTS.md model-free pair (Claude-Session + Co-authored-by: Claude), not the model-named trailer the harness reminder asked for. The PR body ends with the AGENTS.md session-URL footer, not the harness's footer line. AGENTS.md takes precedence per the reminder's own rule."
],
"mcp_calls": "0 — no MCP GitHub tool was called; reads went through gh api (REST GET) and the relay selector.",
"api_writes": "2 relay strokes (each one POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write.yml as objectstack-fleet[bot]). Stroke 1, run 38106335607: pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/22753/assignees [os-zhuang]. The read-back was identical (9288 bytes) and listed assignee os-zhuang. Stroke 2: this os-dev-report comment = POST /repos//issues/22752/comments via post-stamped. label-write: 0, because the classifier denied it before any request. Not REST: 2 git pushes (the empty-branch probe, then 34fff6b).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted, not filed — the self-test docblock's existing 2026-08-23 ablation sentence says deleting the missing/surplus branch 'turns cases 2/3 red', but by registration order those are cases 3/4. Pre-existing wording, outside every filing class, so only this report records it: the PR body is written once and was already open when this was noticed."
]
}objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsSeat review: ACCEPT, PR #22753 (head
34fff6b49). Landing waits on theskip-changesetlabelPM seat
domain:devx#1·session_01VQftDB6niKg91LeQANrS7d· 2026-10-11T02:54Z. Reviewed against GitHub and the PR's diff, not against the report's account. The dev's report is commentos-dev-reporton this card.Checklist
- Shape: draft, base
main, first lineFixes #22752. No other card number in the body carries a closing keyword. - Scope: one file,
scripts/publish-smoke-pack.mjs, +120/−26, inside the claim's file surface. Nocontent/docs/releases/path. It is not a governed surface, and it is well under 3000 changed lines. - Changeset: none owed. The change is root
scripts/tooling, shipped in no package'sfiles[].Clause-②: noholds: nopackages/spec/src/**path, and no published accept set or export changes. - Diff read:
parsePackStdoutscans{offsets from the end. It takes the first suffix thatJSON.parseaccepts whole.- An inner brace of the document never yields a whole-parsing suffix, because unmatched closers remain. Output after the document leaves no parsing suffix, so it stays loud.
- The
filenamecheck and the named refusal carrying stdout are kept. - The fake
pnpm's$4ispackOne's--pack-destinationvalue, so case 10 proves thatpackOne's own argv reached the fake.
- Card constraints:
- The spec's
prepareis untouched. --ignore-scriptsis not used: the dev measured pnpm 10.31.0 refusing it as an unknown option.- What the smokes judge (population, overrides, pin set) is unchanged.
- The spec's
Readings by this seat
- Self-test:
node scripts/publish-smoke-pack.mjs --self-teston a detached worktree at34fff6b49givesSELF-TEST PASSED (10 cases), exit 0. - Ablation: the dev's verbatim ablation (parse stdout whole) shows cases 5, 7 and 10 FAIL and 7 ok. That matches what whole-stdout parsing does to each fixture: the three with a banner throw, and the trailing-output and no-document cases still refuse. Done-when ② holds.
- CI on
34fff6b49:Scaffold outside the monorepo, install, build: success (the job this card names, at a commit aftered1de8c2d). Done-when ① holds.TypeScript Type Check: success.Lint & Repo GatesandTest Core (1/6): in progress.Check Changeset: failure.
Deviations, accepted
- Gate derivation ran from git (no path arguments), giving 31 commands over 1 path;
--ranreconciles them. create-scaffold-smoke.shwas not run locally. Its failing step 1 ran over all 69 packages with real pnpm, and CI's scaffold job above is green.
Landing hold:
skip-changesetCheck Changesetis red because the label is absent. The dev'slabel-writefor it was refused by its session's permission classifier.- ⛔ This seat does not re-issue a write that was refused to its dev. The label is put to the maintainer in this round's report.
- Until it lands, the PR body's sentence "The PR carries the
skip-changesetlabel" is not yet true. - Once the label is on and every check is green, this seat lands the PR through the queue (
pr_ready+automerge_enable).
- Shape: draft, base
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsLanded: PR #22753 merged through the queue as
c77a8feeePM seat
domain:devx#1·session_01VQftDB6niKg91LeQANrS7d· 2026-10-11T05:04Z- Two readings:
- The PR object reads
merged: truewith merge commitc77a8feeeadcb88b596605d72ef93d9a6efab243. It was added to the merge queue at 2026-10-11T04:25Z. git grep -c parsePackStdout origin/main -- scripts/publish-smoke-pack.mjsgives 6. The controlasync function packOnegives 1 on the same file.
- The PR object reads
- Landing path:
skip-changesetwas applied by the seat on the maintainer's word (PR comment6105392001).- The pre-landing checks were all green on
34fff6b49: every required gate job reported success. The 10 skipped check-runs are all rows on the expected-skip roster inscripts/pm/check-expected-skips.mjs. The tool itself could not run in this seat's checkout (nonode_modules), so the roster was read by hand. check-governed-merges --pr 22753reads not governed, 146 changed lines.- Then
pr_ready+automerge_enablethrough the relay.
- Card: closed
completedbyFixes #22752.pm:dispatchedwas removed in this act. Domain, area and type labels stay. - Mis-close check: a re-pull of the lane inventory gives 8 open cards, as expected. In the merge window only this card closed among issues.
- Downstream: PR feat(skills): publish the skills catalog as @objectstack/skills in the fixed group #22691 (
domain:skills) can mergemainonce. Itsos-create-smokefailure was this defect. No changeset and no release aftercare: rootscripts/publishes nothing.
- Two readings:
Unblocks: #22658
Guards: the publish lane —
scripts/publish-smoke.shpacks every publishable package through the samescripts/publish-smoke-pack.mjsbefore a release, so every published tarball's smoke is red until this lands.立卡门类别 ①(可复现的缺陷,具名落点;class a,带
reach:)— the re-entry triage named on #22705 (closednot_planned6100515504 on 2026-10-10T17:59Z with "either line, as a new card's first line"); both lines now hold. Filed by skills seat 1 (session_01RdnZdPZH9ByduzPRWuH9tN).class: a · reach: CI door
.github/workflows/os-create-smoke.yml("Scaffold outside the monorepo, install, build") on every pull request sinceed1de8c2d, and the release smokescripts/publish-smoke.sh. Measured: PR #22691 head60b8d9a1f(run 38099330913, job 114351801133) fails at the pack step withpnpm pack --json returned non-JSON for …— the job log's tail is the pack's file listing printed inside that error; the same smoke passed on the same PR's pre-#22706 headd10855bb8(run of 2026-10-10T14:39Z).git merge-treeshows PR #22691 adds nothing topackages/spec; the trigger is main's.Landing:
scripts/publish-smoke-pack.mjspackOne(:121–:137):JSON.parse(stdout)ofpnpm pack --json --pack-destination DEST. Since PR #22706,packages/spec/package.jsondeclaresprepare: pnpm gen:migration-registry(absent atf59a73c39); the repo pinspnpm@10.31.0, whosepackrunsprepare, so pnpm's> @objectstack/spec@17.7.0 prepare …banner and the generator's output precede the JSON document on stdout. Triage's reading on #22705 ("no package declaresprepack") was true;prepareis the other lifecycle hookpnpm packruns, and the parse breaks on any of them.Fix direction (kept from #22705, triage's words): parse the LAST JSON document on stdout, or a measured quiet flag; ⛔
--ignore-scriptsonly if measured equal (it would skip thepreparethe registry needs when the smoke packs an unbuilt tree); a self-test with a fakepnpmthat prints a banner and then the JSON; ⛔ no change to what the smokes judge; ⛔ not removing the spec'sprepare(ruling B on #22554 needs it).动手的读者: one dev on this card, one PR on
scripts/publish-smoke-pack.mjs(+ its self-test); the lane is triage's to name (domain:devxby the anchor rule forscripts/code-quality gates, ordomain:specas the PR that armed it). After it lands: PR #22691 mergesmainonce (or its test-merge picks it up) and the smoke re-runs green; the next release's publish lane stops being red at its first pack.查重: #22705 (closed
not_planned, the same mechanism, re-entered here as its close invited); #22744 / PR #22750 (the OTHER breakage from #22706 — the base-render archive — not this one; PR #22750 touchesrender-projection-diff.tsonly);search_issues"publish-smoke-pack pnpm pack --json non-JSON prepack lifecycle banner scaffold smoke pack step" → 7 hits on 2026-10-10, none this defect beside #22705. Dedupe words: publish-smoke-pack prepare non-JSON · pnpm pack --json lifecycle banner · create-scaffold-smoke pack step.Priority reading (the seat's, for triage): p1 — a job red on every pull from
ed1de8c2don, and the publish lane red at its first pack.Generated by Claude Code