Repository navigation
CLI_CONTRACT.md documents status paidRequired for get and scan, but get emits "paid_required" and scan never reports it #982
Description
[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.md documents a paid-plan refusal that no command emits, and doesn't document the one that get does 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:- package/CVE search where nothing is accessible:
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. - package/CVE search where nothing is accessible:
-
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 paid ran twice: 4 passed both times. Those tests pin status == "paid_required" and exit 0 for both get paths: get_edge_cases_e2e.rs#L241-L287 and get_invariants.rs#L439-L490.`` A consumer that follows the contract and matches status == "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.
- Rewrite the
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. - State that
scanreports paid patches only throughpaidPatchesandcanAccessPaidPatches. - In the status enum (L1092), mark
paidRequiredas reserved, or remove it. DeletingStatus::PaidRequiredand its two test references is optional and belongs with this change if it is removed. - Route the two hand-written
getJSON blocks through one helper so the shape is written once.
Moving get onto the unified envelope (where paidRequired would 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 optionally json_envelope.rs (−6 lines). Under 60 changed lines.
Acceptance criteria
- The contract's
paid_requiredrow and status enum match the emitted JSON. -
get's two paid JSON blocks share one emitter. - The 4
--test get paidtests stay green. - Add a test that reads the contract's
paid_requiredrow and asserts thestatusspellinggetemits, in the style ofcontract_gradle_codes.rs.
Dependencies
- Independent of Decide: one shape for the
--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. - Feeds Tracking: make CLI_CONTRACT.md a checked reference plus short prose #948 (checked contract reference) and Tracking: emit every --json error code from one typed ErrorCode registry checked against the contract tables #930 (typed error codes).
Activity
audit-core: C36 decision #983, new finding C54 (#982), C55 split
refactor: re-rank queue at 03:58Z, capacity full
mikolalysenko commentedon Oct 7, 2026
[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
mikolalysenko commentedon Oct 9, 2026
v5 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 commentedon Oct 9, 2026
[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-4bc3ecGenerated by Claude Code
mikolalysenko commentedon Oct 9, 2026
[agent] Draft PR: #1299
Generated by Claude Code