Skip to content

Install gate: move recency gate from flags to a config toggle#119

Merged
juangaitanv merged 8 commits into
mainfrom
quick-fixes
Jun 26, 2026
Merged

Install gate: move recency gate from flags to a config toggle#119
juangaitanv merged 8 commits into
mainfrom
quick-fixes

Conversation

@juangaitanv

@juangaitanv juangaitanv commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Migrate the install-gate recency check from CLI flags to a config toggle.

main already gates on recency via --threshold/--no-fail. This replaces
those flags with config in ~/.corgea/config.toml:

  • recency_gate (bool, default true) — block installs of named targets
    published within the window; set false to disable.
  • recency_threshold_days (u32, default 14) — the window, in days.
  • Env overrides: CORGEA_RECENCY_GATE, CORGEA_RECENCY_THRESHOLD_DAYS.

Behavior: blocks named install targets published within the window; the vuln
verdict takes precedence over freshness; unknown publish dates (pip backtrack)
never block; --force bypasses for a single install. --json reports
recency_threshold_days.

Also in this PR:

  • Refusal hint explains a misplaced escape flag (--force typed after the verb).
  • pyproject test-fixture fixes.
  • README / SKILL.md updated for the config toggle.
  • RELEASING.md doc

A --force/--no-fail typed after the install verb is swept into clap's
trailing-var-arg and forwarded to the package manager, so the gate never
sees it and the block stands. Users (reasonably) read 'Pass --force' and
retry with the flag in the same wrong slot.

print_escape_hint now detects an accepted escape flag sitting in the
forwarded args and prints a note naming the package manager that received
it, plus the corrected invocation with the flag between manager and verb
(corgea uv --force add requests). Covered by a pip integration test.
The recency block (a version published within --threshold blocks the
install) is gone, along with the -t/--threshold flag, the --no-fail
demotion flag, parse_threshold, and the RecencyOnly block reason. The
vuln-api verdict is now the gate's single block condition.

Every resolved package still shows its publish time for provenance
('published <age> ago at <UTC timestamp>') — now a neutral line on every
named target rather than a within-threshold warning. JSON drops
threshold_seconds and the per-result 'recent' status/count; verdict_mode
'recency-only' becomes 'none'.

Tests repurposed, not just deleted: fresh pins now assert they install;
the two npm gating tests assert the gate engaged via its Pre-checking
header / registry hit instead of a recency refusal. SKILL.md updated.
@juangaitanv juangaitanv marked this pull request as ready for review June 24, 2026 13:44
Comment thread src/precheck/render.rs
Comment thread src/precheck/verdict.rs
Comment thread src/main.rs
When pip backtracks a named target to a version the CLI never resolved,
apply_verdicts adopts the installed version on the named row but its
publish date / age belonged to the CLI-resolved version. Render then
printed e.g. `flask@3.0.2 published … at <date-for-3.0.3>` — a wrong date
presented as provenance, the one thing the recency-removal PR keeps.

Make `TargetOutcome::Resolved.age` an `Option<Duration>`, set it to `None`
in the backtrack collapse (rather than re-fetch on the gate's critical
path), and have render omit the publish line (text) / null `published_at`
+ `age_seconds` (JSON) when it's absent. Tests assert the backtracked row
carries no stale date and an exact match keeps its provenance.

Also clean the stale "recency" wording the gate removal left behind:
wrapper --help strings, SKILL.md, and the locked-install comments.
Bring back the recency block removed in dca9621, this time driven by
~/.corgea/config.toml instead of the -t/--threshold and --no-fail flags.
Two new fields, both serde-defaulted so an upgraded config inherits the
gate rather than silently disabling it:

  recency_gate = true           (CORGEA_RECENCY_GATE)
  recency_threshold_days = 14   (CORGEA_RECENCY_THRESHOLD_DAYS)

block_reason gains BlockReason::Recency as a softer second gate: a clean
named target published within the window blocks only when no vulnerable/
unverifiable finding already did. Unknown publish dates (pip backtracking,
age = None) never block, so the stale-provenance bug 1496887 fixed stays
fixed. The refusal names each fresh package and points at the config
toggle; --force still bypasses. JSON adds top-level recency_threshold_days
(null when off) to pair with each result's age_seconds.

Tests: a config unit test pins the opt-out default + back-compat; five
e2e tests cover block-on, --force bypass, threshold-0 plumbing, old-pin
allowed, and gate-off installs. The shared test harness pins the gate off
so existing gate tests stay 'every block is the verdict's doing'.

Docs: SKILL.md and README document the gate, window, and toggle.
@juangaitanv juangaitanv changed the title Install gate: remove recency gate, keep publish-date provenance Install gate: move recency gate from flags to a config toggle Jun 26, 2026
Document the tag-driven release process: Cargo.toml as the version source
of truth (PyPI via maturin dynamic; npm version from the v-tag), the three
publish workflows and their order (PyPI wheels -> GitHub Release binaries ->
npm), the manual changelog step, smoke tests, rollback, and known issues
(the npm skip-guard checks the unscoped name; bin/corgea.js reinstall hint).

@yhoztak yhoztak left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@juangaitanv juangaitanv merged commit 4629cf7 into main Jun 26, 2026
17 checks passed
@juangaitanv juangaitanv deleted the quick-fixes branch June 26, 2026 09:58
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