Repository navigation
Artifact GC is reported in four JSON shapes, and the bytesFreed/bytes fields the contract documents are emitted by no command #1257
Copy link
Copy link
Open
Labels
agent:triagedarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)Filed by a scheduled architecture audit routine (see the architecture review discussion)bugSomething isn't workingSomething isn't workingcompatibilityPublic CLI/JSON, saved state, upgrades, or package-manager compatibility.Public CLI/JSON, saved state, upgrades, or package-manager compatibility.priority:p1uxCLI commands, help, diagnostics, output consistency, or actionable recovery instructions.CLI commands, help, diagnostics, output consistency, or actionable recovery instructions.v5-blockerMust resolve before v5: public interface/migration or ordinary patch-install-undo failure.Must resolve before v5: public interface/migration or ordinary patch-install-undo failure.
Description
Activity
- addedbugSomething isn't workingSomething isn't workingarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)Filed by a scheduled architecture audit routine (see the architecture review discussion)
on Oct 9, 2026 - added a commit that references this issue
on Oct 9, 2026 mikolalysenko commented
on Oct 9, 2026 CollaboratorAuthorMore actions[agent] Triaged p3 (general CLI JSON contract, no single ecosystem). Not a duplicate: #1066 is the same class of counter drift but a different fix (rollback's top-level counters only count the agent leg), and #948 is the broader contract-freshness tracking issue. No open PR addresses the GC report shape yet. Left open as an actionable bug.
Generated by Claude Code
- addedv5-blockerMust resolve before v5: public interface/migration or ordinary patch-install-undo failure.Must resolve before v5: public interface/migration or ordinary patch-install-undo failure.uxCLI commands, help, diagnostics, output consistency, or actionable recovery instructions.CLI commands, help, diagnostics, output consistency, or actionable recovery instructions.compatibilityPublic CLI/JSON, saved state, upgrades, or package-manager compatibility.Public CLI/JSON, saved state, upgrades, or package-manager compatibility.and removed
on Oct 9, 2026 mikolalysenko commented
on Oct 9, 2026 CollaboratorAuthorMore actionsv5 release blocker (P1). Align the emitted GC JSON and documented counters before v5 consumers rely on them. A truthful small schema is sufficient; PR #1273 is pending.
This follows the maintainer's release scope: one normally completing CLI instance, prioritizing valid-lockfile patch/install behavior, compatibility, and actionable CLI UX.
- added a commit that references this issue
on Oct 9, 2026
Metadata
Metadata
Assignees
Labels
agent:triagedarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)Filed by a scheduled architecture audit routine (see the architecture review discussion)bugSomething isn't workingSomething isn't workingcompatibilityPublic CLI/JSON, saved state, upgrades, or package-manager compatibility.Public CLI/JSON, saved state, upgrades, or package-manager compatibility.priority:p1uxCLI commands, help, diagnostics, output consistency, or actionable recovery instructions.CLI commands, help, diagnostics, output consistency, or actionable recovery instructions.v5-blockerMust resolve before v5: public interface/migration or ordinary patch-install-undo failure.Must resolve before v5: public interface/migration or ordinary patch-install-undo failure.
[agent] Filed by the scheduled architecture audit routine (CLI and core). Register: register comment.
Kind: bug. Source: new finding, register C79. It relates to C33 (#948, contract freshness) and C50 (#893, GC retention).
Problem (main @
a80b89e)The contract's unified envelope documents three byte counters:
summary.bytesDownloadedandsummary.bytesFreed(CLI_CONTRACT.md#L1189-L1200);events[].bytes, which it says is "set" ondownloadedand optional onremoved(#L1221,#L1237-L1245).None of them exists in the code.
json_envelope::Summaryhas no byte fields, andPatchEventhas nobytes. The contract's own "GC summary" recipe,repair --json | jq '{removed: .summary.removed, bytesFreed: .summary.bytesFreed, …}'(#L1711-L1718), therefore returnsbytesFreed: null. It also returnsremoved: 1however many blobs were deleted.The same GC result is reported four ways, with four key spellings:
repairremovedevent,details: {count, checked}(repair.rs#L769-L780)#L212-L217) and the human lineremoveremovedevent,details: {blobsRemoved, rolledBack, archivesRemoved}(remove.rs#L1061-L1068)#L936-L948)rollbackgc: {removedBlobs, removedDiffArchives, removedPackageArchives, bytesFreed}(rollback.rs#L1483-L1488)scan --prunegc: {prunedManifestEntries, removedBlobs, …, bytesFreed}(scan/gc.rs#L96-L106)The
PatchActiontable's "Emitted by" column has drifted too:discoveredlistsscan, but onlylistemits it.downloadedlistsgetandscan --mode agent, but onlyrepairandupdateemit it.removedlistsrollback, but rollback prints its legacy shape and no events.verifiedlistsscan --dry-run, but scan prints no events.Proof by execution (debug build at
a80b89e, run twice underenv -i, same result both times). The fixture is an agent-mode npm project: one applied patch, its before/after blobs, and four unreferenced blob files in.socket/blobs/.repair --offline --jsondeleted 5 of the 6 blobs. The contract recipe printed{"removed": 1, "bytesFreed": null, "failed": 0}, and the only event was{"action":"removed","details":{"count":5,"checked":6}}. The human run of the same fixture printsRemoved 3 unused blobs (38 B freed).remove pkg:npm/left-pad@1.3.0 --jsonemitted tworemovedevents (the entry, andblobsRemoved: 3), withsummary.removed: 1and no byte count.Symptoms
None filed. #1066 (rollback counters count only the agent leg) is the same class of counter drift.
Impact
A CI or dashboard that follows the documented recipe gets
nulland a removal count of 1. The four shapes mean any consumer has to special-case each command. The risk is low, but the documented contract is wrong today, and every new GC caller adds another shape.Proposed change
GcReport { removed_blobs, removed_diff_archives, removed_package_archives, bytes_freed }injson_envelope(or besideArtifactReferences), serialized identically everywhere.rollbackandscanbuild it instead of their hand-writtenjson!blocks; the keys are unchanged.repairandremoveattach it to their artifact-levelremovedevent as additivedetailskeys, keepingcount/checkedandblobsRemoved/archivesRemovedfor compatibility.summary.bytesFreed(the sum of the removed events'bytes), or deletesummary.bytesDownloaded,summary.bytesFreedandevents[].bytesfrom the contract. Fix the jq recipe and the "Emitted by" column to match. Recommendation: implementbytesFreed, which is additive and MINOR; deletebytesDownloaded/downloaded.bytes, because blob downloads don't carry a byte count today and Remove --download-mode and the diff download path (#792) #1049 rewrites that path.Size and scope
json_envelope.rs,repair.rs,remove.rs,rollback.rs,scan/gc.rsandCLI_CONTRACT.md.Acceptance criteria
repair --jsonandremove --jsonreport blobs removed and bytes freed under the same keys asrollback/scan --prune.bytesFreed(or the recipe is rewritten to the emitted keys), and an e2e test runs the recipe's paths.summaryandPatchEventfield documented in the contract is serialized byjson_envelope. A unit test pins the key set against the contract's schema block.rollback_duality_invariantsandcovgap_commands_scan_vendor_flowGC assertions stay green.Dependencies
downloadedevent, and Tracking: make CLI_CONTRACT.md a checked reference plus short prose #948 (contract freshness tests).