Skip to content

Install gate, Phase 3 (lane 2): --json machine output#114

Open
juangaitanv wants to merge 3 commits into
install-gate-phase-3-uvfrom
install-gate-phase-3-json
Open

Install gate, Phase 3 (lane 2): --json machine output#114
juangaitanv wants to merge 3 commits into
install-gate-phase-3-uvfrom
install-gate-phase-3-json

Conversation

@juangaitanv

@juangaitanv juangaitanv commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Overview

This PR is Phase 3, lane 2 of the install-gate feature for the Corgea CLI.

Lane 2 adds machine-readable output. --json makes install-gate reports parseable by agents, scripts, and CI while preserving package-manager passthrough behavior when Corgea is not producing a gate report.

This lane does not add auth or fail-closed enforcement.

Stacked on #113. Base branch: install-gate-phase-3-uv. Review this PR's diff in isolation; it contains the Phase 3 lane 2 slice.

What Phase 3 Lane 2 Includes

  • --json report output as one pretty-printed JSON document on stdout.
  • Summary fields including counts and verdict_mode.
  • Per-result verdict objects including vulnerable matches and a remediation field. remediation is the safe version covering every advisory, or null when any advisory lacks a fix.
  • Tree metadata including mode (full or named-only), resolved_count, and transitive[] entries with origin labels.
  • stdout purity: wrapped package-manager stdout moves to stderr when Corgea owns stdout, keeping the JSON report parseable.
  • JSON error documents for guard refusals and blocking pre-report paths, including wrong manager, parse errors, unparsable lockfiles, root redirects, binary-not-found, and --force warnings over unparsable locks.
  • Passthrough correctness: when Corgea is not producing a report, the package manager's own --json flag is forwarded instead of swallowed.
  • Flag position sensitivity: wrapper-level --json controls Corgea output, while post-verb manager-level --json remains the package manager's flag.
  • VulnMatch serialization so vuln-api matches appear in JSON verdicts.

Deliberately Out Of Scope

The next lane adds:

  • token auth
  • fail-closed enforcement
  • retry behavior

Exit Criteria - Met

--json keeps stdout parseable and includes verdict_mode, tree, and remediation data.

Covered by --json tests across tests/cli_install.rs, tests/cli_verdict.rs, tests/cli_remediation.rs, tests/cli_tree.rs, tests/cli_bare_install.rs, tests/cli_provenance.rs, tests/cli_npm_ci.rs, and tests/cli_uv_sync.rs. Confirmed live against staging with the workflow's corgea npm --threshold 0s --json install ... invocation parsing cleanly. ./harness check passes.

Comment thread src/main.rs
Comment thread src/precheck/mod.rs
@juangaitanv juangaitanv force-pushed the install-gate-phase-3-uv branch from c39ab0f to d9af832 Compare June 12, 2026 14:51
juangaitanv added a commit that referenced this pull request Jun 12, 2026
…t refusals

Addresses Cursor review on #114.

- the wrapper's --json is now forwarded to the package manager on passthrough
  (non-install subcommands, bare exec): `corgea npm --json view x` reaches
  npm's --json again instead of the wrapper silently swallowing it. A
  non-install passthrough produces no Corgea report, so the flag is the
  manager's.
- the pre-report refusals that previously bypassed the JSON error helper
  (unparsable npm ci / uv sync lockfile, bare-npm root redirect, npm ci root
  redirect) now route through refuse_guard, so `--json` emits a parseable
  {"error": …} document instead of bare stderr. Machine consumers can parse
  every blocking path uniformly.
@juangaitanv juangaitanv force-pushed the install-gate-phase-3-json branch from 0e07df6 to c5715f9 Compare June 12, 2026 14:52
@juangaitanv juangaitanv force-pushed the install-gate-phase-3-uv branch from d9af832 to 36350f3 Compare June 12, 2026 16:42
juangaitanv added a commit that referenced this pull request Jun 12, 2026
…t refusals

Addresses Cursor review on #114.

- the wrapper's --json is now forwarded to the package manager on passthrough
  (non-install subcommands, bare exec): `corgea npm --json view x` reaches
  npm's --json again instead of the wrapper silently swallowing it. A
  non-install passthrough produces no Corgea report, so the flag is the
  manager's.
- the pre-report refusals that previously bypassed the JSON error helper
  (unparsable npm ci / uv sync lockfile, bare-npm root redirect, npm ci root
  redirect) now route through refuse_guard, so `--json` emits a parseable
  {"error": …} document instead of bare stderr. Machine consumers can parse
  every blocking path uniformly.
@juangaitanv juangaitanv force-pushed the install-gate-phase-3-json branch from c5715f9 to c79d501 Compare June 12, 2026 16:42
Harvested from the install-vuln-gate spike (dfac68e).

- --json prints the report as one pretty-printed JSON document on
  stdout: summary counts, verdict_mode ("public" / "recency-only"),
  per-result verdict objects (vulnerable verdicts carry matches and a
  remediation field — the safe version covering every advisory, null
  when any advisory lacks a fix), and the tree object (mode
  full/named-only, resolved_count, transitive[] with origin labels)
- stdout purity: the wrapped package manager's stdout moves to stderr
  so stdout stays parseable; guard refusals (pip add, uv install,
  wrong manager, parse errors) emit an {"error": …} document
- VulnMatch is now Serialize for the JSON matches array
…t refusals

Addresses Cursor review on #114.

- the wrapper's --json is now forwarded to the package manager on passthrough
  (non-install subcommands, bare exec): `corgea npm --json view x` reaches
  npm's --json again instead of the wrapper silently swallowing it. A
  non-install passthrough produces no Corgea report, so the flag is the
  manager's.
- the pre-report refusals that previously bypassed the JSON error helper
  (unparsable npm ci / uv sync lockfile, bare-npm root redirect, npm ci root
  redirect) now route through refuse_guard, so `--json` emits a parseable
  {"error": …} document instead of bare stderr. Machine consumers can parse
  every blocking path uniformly.
…ion holes

The --json contract is 'every exit path yields exactly one parseable
document on stdout'. Three paths broke it and two were inconsistent:

- --force over an unparsable lockfile now emits a {warning, proceeded}
  document before exec (stdout was empty).
- uv passthrough (and the no-files 'uv pip sync' branch) routes through
  passthrough_exec, so the wrapper's consumed --json is re-forwarded to
  uv ('corgea uv --json tree' reaches uv's own --json again).
- Bare yarn/pnpm installs under --json emit an empty report document
  (null tree, zero results) with the honesty note on stderr.
- Binary-not-found under --json emits an {error} document before exit
  127.
- refuse_guard pretty-prints, matching the main report's formatting;
  SKILL.md documents verdict_mode 'recency-only' as library-only.
- New position-sensitivity test pins that post-verb --json belongs to
  the manager ('corgea npm install --json x' forwards it; gate stays
  text).
@juangaitanv juangaitanv force-pushed the install-gate-phase-3-uv branch from 36350f3 to f8426b6 Compare June 12, 2026 18:28
@juangaitanv juangaitanv force-pushed the install-gate-phase-3-json branch from c79d501 to 07bb21d Compare June 12, 2026 18:28
Comment thread src/precheck/render.rs
"subcommand": report.subcommand,
"args": report.original_args,
"threshold_seconds": report.threshold.as_secs(),
"summary": {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

json summary counts mix scopes inconsistently. vulnerable/unverifiable use verdicts (includes tree), but ok/recent/skipped/errors are named only.

for all tree reports (npm ci/uv sync) this gives ok: 0 even with dozens of clean locked packages, and there's no clean count (TreeOutcome has no age, so recency is inherently named only)

can we split summary into scope matched named and tree sub-objects so each reconciles with its own array, adding a clean count.

for example:

"summary": {
  "named": { "ok", "recent", "vulnerable", "unverifiable", "clean", "skipped", "errors" },
  "tree":  { "resolved_count", "vulnerable", "unverifiable", "clean", "not_checked" }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants