Post-Commit Quality Gate
This page describes a proposed/retained post-commit automation design. Its named template, prompt, hook, and Ralph engine are not currently tracked in this repository. Do not assume it runs after commits.
Overview
The post-commit quality gate is a closed-loop automation that runs after every commit. It analyzes exactly what changed, determines what tests and docs are affected, implements the missing coverage, runs the new tests until they pass, and produces a clean summary.
The goal: developer commits code; the loop handles test coverage and documentation.
Template: prd-post-commit.template.json
Prompt: PROMPT_post-commit.md
Stories: 12 (PC00-PC11)
Slash command: /post-commit-loop-bootstrap [commit-ref]
Story Breakdown
| ID | Title | Depends On | Output |
|---|---|---|---|
| PC00 | Commit analysis: classify every changed file | -- | .ralph/pc-analysis.md, .ralph/post-commit-state.json |
| PC01 | Test assessment: identify coverage gaps | PC00 | .ralph/pc-test-plan.md |
| PC02 | Implement vitest unit/integration tests | PC01 | New/updated test files |
| PC03 | Implement playwright e2e tests | PC01 | New playwright test files |
| PC04 | Run all tests, iterate on failures | PC02, PC03 | .ralph/pc-test-results.md |
| PC05 | Bruno: assess and update API test collections | PC01 | .ralph/pc-bruno-results.md |
| PC06 | Docs assessment: identify documentation gaps | PC00 | .ralph/pc-docs-plan.md |
| PC07 | Extract source facts for documentation | PC06 | .ralph/extractions/<topic>.md |
| PC08 | Write or update documentation | PC07 | Updated doc files, .ralph/pc-docs-results.md |
| PC09 | Docs verification: lint, links, accuracy | PC08 | .ralph/pc-docs-verification.md |
| PC10 | Final cross-check and commit-ready summary | PC04, PC05, PC09 | .ralph/pc-summary.md |
| PC11 | Self-improvement: update guardrails from failures | PC10 | .ralph/pc-prompt-improvements.md, updated .ralph/guardrails.md |
Flow
PC01, PC02, PC03, PC04, and PC05 are the testing track. PC06 through PC09 are the documentation track. Both tracks run in parallel and converge at PC10. PC11 always runs last and feeds improvements back into the loop.
Change Classification (PC00)
PC00 reads the commit diff and classifies every changed file:
| Type | Meaning | Test Impact | Doc Impact |
|---|---|---|---|
NEW_FEATURE | New exported function, class, route, or component | Unit + integration + e2e gaps | New doc needed |
CHANGED_BEHAVIOR | Existing API signature or behavior changed | Stale tests to fix | Stale doc to update |
BUGFIX | Targeted fix, no interface change | Regression test gap | Possibly none |
REFACTOR | Internal restructure, no observable change | Likely none | Likely none |
CONFIG_CHANGE | package.json, tsconfig, env, docker | Likely none | README may need update |
TEST_CHANGE | Test files only | n/a | n/a |
DOC_CHANGE | Documentation files only | n/a | n/a |
CHORE | Build scripts, lock files, formatting | Likely none | Likely none |
Test Gap Categories (PC01)
| Gap Type | Priority | What It Means |
|---|---|---|
STALE_TEST | Critical | Existing test asserts wrong behavior after a change |
REGRESSION_GAP | High | Bug was fixed but no test would catch a recurrence |
INTEGRATION_GAP | High | New route/endpoint with no API test |
E2E_GAP | Medium | User flow affected with no playwright coverage |
UNIT_GAP | Medium | New function with no unit test |
API_COLLECTION_GAP | Medium | Changed API endpoint not covered in the Bruno collection |
STALE_TEST items are addressed first -- they actively give false confidence.
Test Execution (PC04)
PC04 runs all new and modified tests and iterates on failures:
- Runs
npm run test:run(vitest) for unit/integration tests - If browser E2E tests were added: runs the owning Playwright command such as
npm run test:admin:e2eornpm run test:widget:e2e; roottest:e2eis a Vitest integration-pattern command, not Playwright - For each failure: diagnoses root cause -- is the test wrong or did the code introduce a regression?
- Fixes test assertions (if test is wrong) or fixes source code (if code introduced a regression)
- Re-runs until all newly added tests pass
PC04 is not complete until all newly added tests pass. Pre-existing failures are documented but do not block completion.
Docs Gap Categories (PC06)
| Gap Type | Priority | What It Means |
|---|---|---|
BROKEN_LINK | Critical | Doc references a file/function/route that was renamed or removed |
STALE_DOC | High | Existing doc describes old behavior |
NEW_DOC_NEEDED | High | New feature/route with no documentation |
README_NEEDED | High | Significant new feature not mentioned in README |
INCOMPLETE_DOC | Medium | Doc exists but missing required sections |
Documentation uses the extract-then-write pattern: source facts are extracted to .ralph/extractions/<topic>.md in PC07 before any doc is written in PC08. This prevents fabricated content.
Grace Modes
The loop degrades gracefully when components are absent:
| Absent Component | Behavior |
|---|---|
No bruno/ collection | PC05 skips with a note -- no error |
No tests/e2e/ directory | PC03 skips -- no playwright tests created |
| No docs directory | Doc stories skip gracefully |
| No running server for E2E | Playwright test files are written but execution is skipped |
Final Summary (PC10)
PC10 cross-checks every item in pc-analysis.md is accounted for and produces .ralph/pc-summary.md with an explicit status:
| Status | Meaning |
|---|---|
CLEAN | All items addressed, all new tests pass, no broken links |
NEEDS_ATTENTION | Open blocking issues remain (listed explicitly) |
Deferred items (non-blocking) are also listed with reasons for the next iteration.
Quality Gates
- PC00 drives scope for ALL stories -- never work outside what the commit touched
- Every test assertion must reflect actual current behavior, not aspirational behavior
- Never break existing passing tests
- All newly added tests must pass before PC04 is marked complete
- Use extract-then-write for all doc changes: read source, extract to
.ralph/extractions/, then write doc - Dry-run mode is mandatory for any endpoint with real external side effects
- Never fabricate documentation content -- defer if source cannot be found
Artifacts
| File | Written By | Purpose |
|---|---|---|
.ralph/pc-analysis.md | PC00 | Itemized commit change log |
.ralph/post-commit-state.json | PC00, PC10 | Loop state, items, completion status |
.ralph/pc-test-plan.md | PC01 | Prioritized test gap plan |
.ralph/pc-test-results.md | PC04 | Test run before/after metrics |
.ralph/pc-bruno-results.md | PC05 | Bruno changes or skip reason |
.ralph/pc-docs-plan.md | PC06 | Prioritized doc gap plan |
.ralph/extractions/<topic>.md | PC07 | Source extraction artifacts |
.ralph/pc-docs-results.md | PC08 | Per-file doc change summary |
.ralph/pc-docs-verification.md | PC09 | Lint and accuracy verification |
.ralph/pc-summary.md | PC10 | Final commit-ready summary |
Self-Improvement (PC11)
After every run, PC11 reads the run's errors.log and progress.md and feeds failures back into the loop:
- Adds new Signs to
.ralph/guardrails.mdfor any repeated failure patterns (e.g. "this API always returns 201 not 200", "this file is always renamed to X") - Proposes PROMPT fixes to
.ralph/pc-prompt-improvements.mdwhen the prompt gave bad guidance -- does NOT edit the PROMPT directly, as that requires human review - Notes loop design gaps in
.ralph/pc-loop-design-notes.mdwhen a commit type isn't handled well
Over many commits, the guardrails file accumulates project-specific knowledge that gets injected into every subsequent run, making the loop smarter and reducing retries.
PRD Isolation
Each run generates a run-specific PRD (prd-post-commit-<hash>.json) so multiple runs don't overwrite each other. The template (prd-post-commit.template.json) is never modified. prd-active.json is a convenience copy of the most recent run.
Clean up old run PRDs with:
git clean -f .agents/tasks/prd-post-commit-*.json
Git Hook
Install once to run the loop automatically after every commit:
# Submodule layout (Ralph_Loops repo at .agents/ralph/):
cp .agents/ralph/.agents/ralph/post-commit.hook.sh .git/hooks/post-commit
chmod +x .git/hooks/post-commit
# Standalone layout (loop engine directly at .agents/ralph/):
cp .agents/ralph/post-commit.hook.sh .git/hooks/post-commit
chmod +x .git/hooks/post-commit
The hook auto-detects which layout you have at runtime.
The hook runs Ralph in the background by default (RALPH_POST_COMMIT_ASYNC=1) so it never blocks your terminal. After committing you can monitor progress:
# Watch the log:
tail -f ../<repo>--ralph-<hash>/.ralph/post-commit-<hash>.log
# Check the summary when done:
cat ../<repo>--ralph-<hash>/.ralph/pc-summary.md
Disable temporarily without uninstalling:
RALPH_POST_COMMIT=0 git commit -m "..."
Worktree Isolation
By default the hook runs inside an isolated git worktree (../<repo>--ralph-<hash>/) rather than your main working tree. This means:
- Generated tests and documentation never appear as uncommitted changes in your repo
- Your in-progress work is untouched while Ralph runs
- You review the output as a normal PR before it merges
What the hook does automatically
- Creates
../<repo>--ralph-<hash>/viagit worktree addfrom HEAD - Copies
.env*files into the worktree so the environment matches - Runs
npm installin the worktree - Points
RALPH_ROOTat the worktree so all loop artifacts and generated files land there - Runs the loop asynchronously in the background
Reviewing and merging
When the loop finishes, the hook prints the exact PR command for your git host. Review the worktree and push:
cd ../<repo>--ralph-<hash>
git add -A
git commit -m "test/docs: post-commit quality gate for <hash>"
git push -u origin ralph/post-commit-<hash>
Then open a PR — the hook auto-detects your remote and prints the right command:
# GitHub:
gh pr create --base <your-branch> --title "test/docs: post-commit quality gate for <hash>"
Disabling worktree mode
To run the loop directly in the main repo (no worktree created):
RALPH_POST_COMMIT_WORKTREE=0 git commit -m "..."
Or set it permanently in your shell profile:
export RALPH_POST_COMMIT_WORKTREE=0
Configuration reference
| Variable | Default | Meaning |
|---|---|---|
RALPH_POST_COMMIT | 1 | Set to 0 to disable the hook entirely |
RALPH_POST_COMMIT_ASYNC | 1 | Set to 0 to block the terminal until complete |
RALPH_POST_COMMIT_WORKTREE | 1 | Set to 0 to run in-place (no worktree) |
RALPH_CLAUDE_MODEL | sonnet | Claude model to use |
Run Command (Manual)
# Bootstrap (defaults to HEAD):
/post-commit-loop-bootstrap
# Bootstrap for a specific commit:
/post-commit-loop-bootstrap abc1234
# Then run Ralph autonomously:
PROMPT_BUILD=.agents/ralph/PROMPT_post-commit.md \
PRD_PATH=.agents/tasks/prd-active.json \
bash .agents/ralph/loop.sh
# One story at a time (for debugging):
MAX_ITERATIONS=1 \
PROMPT_BUILD=.agents/ralph/PROMPT_post-commit.md \
PRD_PATH=.agents/tasks/prd-active.json \
bash .agents/ralph/loop.sh