commit · diff

git:20260823.6b8fa14 to git:20260823.86d504a

28 added, 0 removed. Audit A to A.

---
name: commit
description: Stage, commit, push, open a PR, and merge to main. Use ONLY on explicit commit intent — user says "commit", "ship it", "push this", "open a PR", "merge to main", "let's commit this", or prefixes with `/commit`. Do NOT auto-invoke on vague end-of-task phrases ("we're done", "wrap up") — those require explicit confirmation first. Runs the standard commit-PR-merge cycle; never force-pushes or skips hooks.
argument-hint: "[optional: commit message]"
allowed-tools: ["Bash", "Read", "Glob", "Agent", "Task"]
---
# Commit, PR, and Merge
Stage changes, verify quality gates, commit with a descriptive message, create a PR, and merge to main.
## Steps
### Step 0: Quality Gate (Pre-Commit)
**Run before branching.** For every changed `.qmd`, `.tex`, or `.R` file that has quality rubrics, run:
```bash
python3 scripts/quality_score.py <changed-file-paths>
```
- If any file scores below **80**, halt and report the findings. The user must either fix the issues or explicitly override with phrases like *"commit anyway"* or *"skip quality gate"*.
- If all files score 80+, continue.
Spawn the **verifier** agent (via the `Agent` tool with `subagent_type=verifier`) to run compilation/render checks on the changed files. Report pass/fail before committing.
### Step 0b: Consistency Gate (Pre-Commit)
**Runs unconditionally.** The full backtest suite — all ten gates (surface-sync count claims like `"18 agents, 60 skills, 37 rules, 8 hooks"` and marked tables, skill integrity, model currency, links, spec conformance, staleness, repo hygiene, derived counts, ledger coverage, hook battery):
```bash
./scripts/backtest.sh
```
- **Exit 0:** all gates green — continue.
- **Nonzero:** at least one gate is red — print its output and halt. Fix, then re-run. Do NOT proceed past this gate on a red result, even with "commit anyway" — count drift alone produced PRs #70, #76, and #78.
### Step 0c: Passport Check (Pre-Commit)
If the diff touches a manuscript (`.tex`/`.qmd`) that has a passport in `quality_reports/passports/`, any `source_file` a passport lists, or any file a claim declares as a display (its `location:`, or an `appears_in` `path:`), read that passport: a load-bearing claim with `status: FAIL` or `STALE` is a **must-fix** — halt, name the claim, and point at `/audit-reproducibility` (or the stale script) before committing. A touched display triggers the check for the same reason a touched script does: editing one artifact's copy of a number desynchronizes it from that number's other displays — the supplement or deck holding the same value was not necessarily updated in the same commit. Skip silently when no passport exists.
### Step 1: Check current state
```bash
git status
git diff --stat
git log --oneline -5
```
### Step 2: Create a branch
```bash
git checkout -b <short-descriptive-branch-name>
```
### Step 3: Stage files
Add specific files (never use `git add -A`):
```bash
git add <file1> <file2> ...
```
Do NOT stage `.claude/settings.local.json` or any files containing secrets.
### Step 4: Commit with a descriptive message
If `$ARGUMENTS` is provided, use it as the commit message. Otherwise, analyze the staged changes and write a message that explains *why*, not just *what*.
**The subject line states what is TRUE AFTER the commit** — a plain sentence about behavior that a reader could go and test. *"Fixed review feedback"* and *"Updated the checker"* narrate your afternoon and tell a reader nothing; *"The parity gate refuses a fixture whose hash is unregistered"* is a claim they can check against the code. Process narration — which review round it came from, who asked, how many attempts it took — belongs in the body if it belongs anywhere. The body still carries the *why*; the subject carries the claim.
```bash
git commit -m "$(cat <<'EOF'
<commit message here>
EOF
)"
```
### Step 5: Push and create PR
```bash
git push -u origin <branch-name>
gh pr create --title "<short title>" --body "$(cat <<'EOF'
## Summary
<1-3 bullet points>
## Test plan
<checklist>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
```
### Step 6: Merge and clean up
```bash
gh pr merge <pr-number> --merge --delete-branch
git checkout main
+ git status --porcelain # must print nothing before the next line runs
git pull
```
+
+ **Expect the tree to be dirty here — that is what Step 3 is for.** Staging specific files means
+ everything the session touched but did not stage, plus every untracked artifact, is still in the
+ tree when you arrive at this step. The `git-guardrails` hook this template ships **denies**
+ `git pull` (and `merge`, and `rebase`) whenever `git status --porcelain` is non-empty, because a
+ pull that resolves on top of uncommitted work cannot be reviewed afterwards — you can no longer
+ tell which hunk came from the remote. Chaining a stash into the same command line does not help:
+ the hook reads the tree, it does not predict what the line will do to it.
+
+ Clear the tree deliberately, then pull:
+
+ ```bash
+ git stash push -u -m "post-merge: unstaged leftovers" # -u, or untracked files stay behind
+ git pull
+ git stash pop
+ ```
+
+ `git pull --autostash` is the one-command alternative, and the hook exempts it because git — not
+ the hook — establishes the precondition. It stashes **tracked** modifications only. Untracked
+ files usually ride along harmlessly, but if the incoming commits add a file at a path you are
+ holding untracked, git aborts the merge (*"untracked working tree files would be overwritten"*)
+ after the autostash has already been taken. When porcelain shows `??` lines you care about,
+ prefer the explicit `-u` stash above.
+
+ `ALLOW_DIRTY_MERGE=1` is the hatch for a dirty state you have looked at and can justify out loud.
+ It is not the routine remedy, and reaching for it because the block is inconvenient is the
+ behavior the check exists to prevent.
### Step 7: Report
Report the PR URL and what was merged.
## Important
- **Never skip Step 0.** Quality gates catch broken compilation, bad citations, and hardcoded paths before they reach `main`. If the user insists on skipping, record their override reason in the commit message.
- Always create a NEW branch — never commit directly to main.
- Exclude `settings.local.json` and sensitive files from staging.
- Use `--merge` (not `--squash` or `--rebase`) unless asked otherwise.
- If the commit message from `$ARGUMENTS` is provided, use it exactly.