Repository navigation
CLI_CONTRACT.md documents status paidRequired for get and scan, but get emits "paid_required" and scan never reports it #982
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 7, 2026 mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actions[agent] Triaged as
priority:p3(contract docs / general CLI). Confirmed on main9c43dfc:CLI_CONTRACT.md:1092and:1163documentpaidRequiredfor get/scan, whileget.rs:2897and:3201emit"status": "paid_required", andStatus::PaidRequired(json_envelope.rs:400) appears only in tests. I found no duplicate and no open PR. #647 (proxy fallback coverage) touches the same paths but has a different cause.
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). Document one truthful paid-plan JSON/exit contract before clients integrate v5. The published paidRequired status and actual paid_required output currently disagree.
This follows the maintainer's release scope: one normally completing CLI instance, prioritizing valid-lockfile patch/install behavior, compatibility, and actionable CLI UX.
mikolalysenko commented
on Oct 9, 2026 CollaboratorAuthorMore actions[agent] Claiming for v5 blocker burn-down (root cause: the contract's paid_required row describes an envelope status
getnever emits; get hand-writes the legacy shape twice). Branch: agent/v5-paid-required-contract. Claim-ID: 2026-10-09T16:27:48Z-4bc3ec
Generated by Claude Code
mikolalysenko commented
on Oct 9, 2026 CollaboratorAuthorMore actions
[agent] Filed by the scheduled architecture audit routine (CLI and core). Register: discussion #560 register.
Kind: bug (contract drift). Source: new finding; register C54.
Problem (main @
9c43dfc)CLI_CONTRACT.mddocuments a paid-plan refusal that no command emits, and doesn't document the one thatgetdoes emit.What the contract says. The envelope's status enum lists
"paidRequired". The errorCode table rowpaid_requiredsays: actionfailed,status=paidRequired, emitted by "get/scan".What
getemits. Both paid paths hand-write a legacy object with a snake_case status and no events orerrorCode:get.rs#L2894-L2905;get <uuid>on the public proxy (paid view or proxy 403):report_paid_required_uuid.Both print
{"status":"paid_required","found":N,"downloaded":0,"applied":0,"patches":[…]}and exit 0.What
scanemits. Nothing paid-specific. It drops inaccessible patches inselect_accessibleand reports them only as thepaidPatches/canAccessPaidPatchescounts.``Status::PaidRequiredis never constructed. The variant is referenced only by tests below#[cfg(test)](L481). Its own doc comment already says "Nothing emits it yet (getreports this via its legacystatus: "paid_required"shape; scan never does)". So the code knows; the contract was never updated.Proof by execution. On
9c43dfc,cargo test -p socket-patch-cli --test get paidran twice: 4 passed both times. Those tests pinstatus == "paid_required"and exit 0 for both get paths:get_edge_cases_e2e.rs#L241-L287andget_invariants.rs#L439-L490.`` A consumer that follows the contract and matchesstatus == "paidRequired"or `errorCode == "paid_required"` never matches.Symptoms
None filed. PR bots that implement the documented "upgrade your plan" branch silently never take it.
Impact
Small and user-visible: the documented machine contract for the paid tier is wrong in three ways (status spelling, action/errorCode, and the command list). It's another instance of C13/C33: the contract's code tables aren't checked against the code.
Proposed change
Make the contract describe what ships. No behavior change.
paid_requiredrow (L1163):getonly; legacy top-levelstatus: "paid_required"withfound/downloaded/applied/patches[], exit 0, noevents/errorCode. Keep the existingget <uuid>proxy sentence, which is accurate.scanreports paid patches only throughpaidPatchesandcanAccessPaidPatches.paidRequiredas reserved, or remove it. DeletingStatus::PaidRequiredand its two test references is optional and belongs with this change if it is removed.getJSON blocks through one helper so the shape is written once.Moving
getonto the unified envelope (wherepaidRequiredwould become real) is the owner decision in #704 and is out of scope here.Size and scope
CLI_CONTRACT.md(two lines),get.rs(one shared ~20-line emitter replacing two blocks), and optionallyjson_envelope.rs(−6 lines). Under 60 changed lines.Acceptance criteria
paid_requiredrow and status enum match the emitted JSON.get's two paid JSON blocks share one emitter.--test get paidtests stay green.paid_requiredrow and asserts thestatusspellinggetemits, in the style ofcontract_gradle_codes.rs.Dependencies
--jsontop-levelerror(scan and get emit both a string and a {code, message} object) #704; if Decide: one shape for the--jsontop-levelerror(scan and get emit both a string and a {code, message} object) #704 later movesgetonto the envelope, that PR updates this row again.