# prview gate rollout playbook
`prview gate` is the automation entry point for local hooks and CI. It runs the
measured fast gate profile, reads the generated `MERGE_GATE.json`, and returns a
stable process exit code. Hook recipes must decide from that exit code only;
they must not parse stdout.
## Exit-code contract
| `0` | `PASS`, or `CONDITIONAL` without `--strict` | Allow |
| `1` | `BLOCK` | Block in Warn and Required |
| `2` | `CONDITIONAL` with `--strict` | Block in Required |
| `3` | Gate execution failed before a trustworthy verdict was available | Block in Warn and Required |
Use `prview gate --json` when CI needs a machine-readable summary, artifact
paths, or SARIF path discovery. Pass/fail still comes from the process exit code.
## Rollout ladder: Shadow -> Warn -> Block
Start advisory. A gate that blocks too early trains people to bypass it.
| Shadow / advisory | `prview gate` | Report only; hook exits `0` even when the gate exits non-zero | At least 7 days of runs, no false `BLOCK`, no repeated exit `3`, and runtime fits the team budget |
| Warn | `prview gate` | Block only `BLOCK` (`1`) and execution errors (`3`); `CONDITIONAL` remains exit `0` | At least 14 days of runs, false-block rate under 2%, flaky tools fixed, and caveats are triaged quickly |
| Block / required | `prview gate --strict` | Required CI check; blocks `BLOCK`, `CONDITIONAL`, and execution errors | Keep only after owners agree that `CONDITIONAL` is actionable enough to block merges |
Measured baselines from the initial gate profile:
| `prview-rs` | about 8s | Semgrep plus cached Cargo check |
| `pensieve` | about 47s | Semgrep on a larger repo |
Treat those as starting budgets. If a repo is consistently slower, tune the
policy or Semgrep scope before moving from Shadow to Warn.
## Repository-local hook
This repo ships a raw pre-push hook installer:
```bash
make git-hooks
```
The target is idempotent. It symlinks:
- `tools/githooks/pre-commit` -> `.git/hooks/pre-commit`
- `tools/githooks/pre-push` -> `.git/hooks/pre-push`
The pre-push hook calls `prview gate` and supports rollout modes:
```bash
# Shadow/advisory: never blocks the push
git push
# Warn: blocks BLOCK and execution errors
PRVIEW_GATE_HOOK_MODE=warn git push
# Strict local dry-run of the required CI behavior
PRVIEW_GATE_HOOK_MODE=strict git push
```
Set `PRVIEW_GATE_HOOK_MODE=warn` in your shell environment once the repo has
cleared the Shadow criteria.
## Hook recipes
### Lefthook
`lefthook.yml`:
```yaml
pre-push:
commands:
prview-gate:
run: prview gate
```
Install with:
```bash
lefthook install
```
Advisory Shadow variant:
```yaml
pre-push:
commands:
prview-gate:
run: prview gate || status=$?; echo "prview gate advisory exit: ${status:-0}"; exit 0
```
Required strict variant:
```yaml
pre-push:
commands:
prview-gate:
run: prview gate --strict
```
### pre-commit
`.pre-commit-config.yaml`:
```yaml
default_install_hook_types: [pre-push]
repos:
- repo: local
hooks:
- id: prview-gate
name: prview gate
entry: prview gate
language: system
pass_filenames: false
stages: [pre-push]
```
Install with:
```bash
pre-commit install --hook-type pre-push
```
Advisory Shadow variant:
```yaml
repos:
- repo: local
hooks:
- id: prview-gate-advisory
name: prview gate advisory
entry: sh -c 'prview gate || status=$?; echo "prview gate advisory exit: ${status:-0}"; exit 0'
language: system
pass_filenames: false
stages: [pre-push]
```
Required strict variant:
```yaml
repos:
- repo: local
hooks:
- id: prview-gate-strict
name: prview gate strict
entry: prview gate --strict
language: system
pass_filenames: false
stages: [pre-push]
```
### Husky
Install Husky, then create `.husky/pre-push`:
```sh
prview gate
```
Advisory Shadow variant:
```sh
prview gate
status=$?
echo "prview gate advisory exit: $status"
exit 0
```
Required strict variant:
```sh
prview gate --strict
```
### Raw Git hook
`.git/hooks/pre-push`:
```sh
#!/bin/sh
set -u
if ! command -v prview >/dev/null 2>&1; then
echo "prview is required: cargo install prview --locked --force" >&2
exit 3
fi
prview gate
```
Install with:
```bash
chmod +x .git/hooks/pre-push
```
Advisory Shadow variant:
```sh
#!/bin/sh
set -u
prview gate
status=$?
echo "prview gate advisory exit: $status"
exit 0
```
Required strict variant:
```sh
#!/bin/sh
set -u
prview gate --strict
```
## CI required check
Use the composite Action for the final required stage:
```yaml
- uses: vetcoders/prview-rs@main # pin to a released tag once one ships `prview gate`
id: prview
with:
strict: "true"
version: "latest"
```
Use `strict: "false"` during advisory CI rollout. `CONDITIONAL` remains exit
`0`, while `BLOCK` remains exit `1`.
## Troubleshooting
| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| Hook blocks with exit `3` | `prview` is missing, the repo is not a valid git checkout, or a required tool failed before a verdict was produced | Install `prview`, run `prview gate` manually, and inspect the printed error |
| Hook blocks with exit `1` | Merge gate verdict is `BLOCK` | Open the generated run directory and read `00_summary/MERGE_GATE.json` and `PR_REVIEW.md` |
| Required CI blocks with exit `2` | Verdict is `CONDITIONAL` and CI uses `--strict` | Either fix the caveat or move the repo back to Warn until conditionals are actionable |
| Hook is too slow | Semgrep or language checks dominate the measured budget | Stay in Shadow, tune policy/check scope, then re-measure before Warn |
| SARIF is missing | No inline findings/advisories were produced | This is normal; upload only when `30_context/INLINE_FINDINGS.sarif` exists |
External hook-manager references:
- [Lefthook configuration](https://lefthook.dev/configuration/)
- [pre-commit stages and pre-push installation](https://pre-commit.com/#confining-hooks-to-run-at-certain-stages)
- [Husky get started](https://typicode.github.io/husky/get-started.html)
- [Git pre-push hook contract](https://git-scm.com/docs/githooks#_pre_push)