feat(ops): add a versioned Vercel Preview retention runner
User description
Summary
Adds a reviewed, versioned runner for the post-merge Vercel Preview retention pass, replacing the session-local scratchpad script. Part of #627.
pnpm run vercel:retention:dry-runis read-only.pnpm run vercel:retention:reconcileruns the full pass.- Both take no argument: anything else is refused, so a command-line allow rule for them cannot be widened by appending text.
Why
The first ordinary pass under the maintainer's retention decision needed a manual one-off command because the only executable was a temporary file under /tmp. A durable runner removes that dependency. A shell allow rule is not a safety boundary, so the safety lives in the runner.
What a pass does
- Proves the exact-main gates: every
CI / CDandCodeQL SASTrun of the SHA is a completed success, Production isREADYon it, the three canonical aliases are bound to it, canonical HTTP is 200, and the project identity matches fixed constants. - Reads every page of deployments and aliases, open PRs and remote branches; an incomplete inventory stops the run.
- Classifies each preview as
SAFE,PROTECTEDorUNKNOWNin a pure function. Production-class deployments (production target, promoted, rollback candidate) never enter the classification. reconcileonly: deletes at most 25 per pass. Before each ID it re-readsmain, the gates and the whole provider state and re-classifies; a changed verdict only defers that ID. A branch alias of a gone branch goes first, after it is shown to bind to that deployment. Writes are never retried; a failed or unconfirmed write stops the pass.- Post-proof by full re-read: protected IDs present, deleted IDs absent, canonical aliases unchanged, HTTP 200.
reconcile refuses to run unless scripts/vercel-retention/ and the two package scripts equal the reviewed origin/main and are clean, so an edited or unmerged checkout cannot delete anything.
Tests
172 cases against a simulated provider: authority (main moving at three points, push-only run gates and truncated run lists, five Production faults), pagination and read failure (including missing pagination metadata), project identity, Production-class protection, open-PR retention for 1/2/3/5 previews, unknown-metadata variants (commit SHA, creation time, state, identity), the explicit hold file, ROLLING and promoted Production-class protection, the per-preview manifest, reclassification before deletion, the post-proof Production re-check, the per-machine lock, refused/ambiguous/unconfirmed deletions, alias failure, batch limit, idempotence, redaction, source trust, argument-free entry points and ID/host validation. A live dry-run against the real project reports the same inventory as the hand-verified pass (42 Production, 2 rollback candidates, 0 unknown, 0 writes).
Not changed
No permission rule is installed and none is requested here; the exact command is documented as the one narrow rule the maintainer may choose to set. No change to the retention policy text (the Active-mode supersession is #1031), to tag, publish or Gate 7 authority, or to GitHub Pages record handling. Orphan aliases are reported by the provider but not removed.
Summary by Sourcery
Introduce a reviewed, versioned Vercel Preview retention runner that inventories and conservatively reconciles obsolete deployments with strict safety gates and post-run proof.
New Features:
- Add fixed, argument-free dry-run and reconciliation commands for safely managing Vercel Preview deployments.
- Classify Preview deployments conservatively while protecting Production-class deployments, active review evidence, open-PR previews, aliases, and unknown cases.
Enhancements:
- Require exact-main CI, Production, alias, project, repository, and HTTP gates before reconciliation.
- Bound deletions, revalidate each candidate before removal, avoid write retries, serialize reconciliation, and verify the final provider state.
- Provide reviewed hold-file support and redacted manifests, summaries, and stop reasons for operational auditing.
Build:
- Add package scripts for Vercel retention dry runs and reconciliation.
Documentation:
- Document the retention runner commands, safety model, operational constraints, holds, permissions, and stop conditions.
Tests:
- Add comprehensive simulated-provider coverage for authority checks, pagination, classification, deletion races, post-proof validation, locking, source trust, argument validation, and redaction.
Chores:
- Add the initial reviewed Vercel retention hold file and update changelog and test-count documentation.
Summary by cubic
Replaces the temporary scratchpad script behind the Vercel Preview post-merge retention pass with a versioned runner under scripts/vercel-retention/ (issue #627). pnpm run vercel:retention:dry-run is read-only; pnpm run vercel:retention:reconcile runs the full pass, and both refuse any argument so an allow rule cannot be widened by appending text.
Safeguards
- A pass proves the exact-main gates (every required push-triggered run on the SHA green, Production
READY, canonical aliases bound, canonical HTTP 200) and the fixed project and origin identity, reads every page of deployments and aliases, validates every record before classification, and classifies each preview asSAFE,PROTECTEDorUNKNOWNin a pure function. A manual or tag run never stands in, a run list at the request limit counts as truncated, a page without pagination metadata as malformed, and each page cursor is checked against its own page. PROTECTEDcovers canonical or live-branch aliases, an open PR's newest three plain previews (ranked only from recognized plain previews with plausible creation times),main/open heads, and previews named in.github/vercel-retention-holds.json; missing, contradictory or foreign metadata (commit SHA, creation time, state, rollback flag, identity, PR identifier, environment id, promotion substate) makes a previewUNKNOWN.reconciledeletes at most 25 previews per pass, re-readsmain, the gates and the whole provider state before each ID, removes the branch alias of a gone branch first (settling a removal that landed despite a reported failure), never retries a write, and ends with a post-proof that re-asserts the gates, Production, the full Production predicate, kept branch aliases and deferred IDs together with their alias bindings, settled states and the Production-class set; one pass runs per machine.reconcileruns only when the runner, hold file and package scripts equal the reviewedorigin/main; the entry file loads only built-ins and the lock before that check.
Covered by the node:test suites against a simulated provider, plus a live dry-run matching the hand-verified inventory.
Summary by CodeRabbit
- New Features
- Added read-only dry-run and reconciliation commands for reviewing Vercel preview deployments and removing eligible previews.
- Reconciliation requires the reviewed source to match the latest main branch, protects uncertain or held deployments, limits each run to 25 removals, and stops if safety checks fail.
- Documentation
- Added guidance on running the commands, safety checks, and exclusions.
- Chores
- Updated the reported test count in the README.
CodeAnt-AI Description
Add a reviewed Vercel Preview cleanup runner
What Changed
- Add a read-only preview that lists which deployments would be kept or removed and why, before cleanup begins.
- Add a cleanup command that removes up to 25 safe previews per run, while preserving Production, rollback candidates, active pull request previews, and previews explicitly held for review.
- Recheck each preview before removal and verify the results afterward. Cleanup stops when required checks, Production health, inventory, or reviewed-source checks cannot be confirmed; extra command arguments are refused.
Impact
✅ Preview deletions reviewed before cleanup
✅ Production and active pull request previews retained
✅ Uncertain cleanup stops before further deletions
💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
@codeant-ai ask: Your question here
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
@codeant-ai ask: Can you suggest a safer alternative to storing this secret?
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
@codeant-ai: Your feedback here
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
@codeant-ai: Do not flag unused imports.
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
@codeant-ai: review
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.