Guardrails
Guardrails are deterministic preconditions — bash scripts and file-existence checks that exit non-zero to block a command. They are not LLM-judgment-based. If a check needs reasoning to evaluate, it's a suggestion, not a guardrail.
The full list lives in novaspec/guardrails/checklist.md. Execution order is 0 → 1 → 2 → 7 → 3 → 4 → 5 → 6.
The 8 guardrails
0. nova-installed
Script: novaspec/guardrails/nova-installed.shChecks: novaspec/config.yml exists and context/ directory exists. Blocks: every command that requires nova-spec to be installed. Fix: npx nova-spec init.
1. branch-pattern
Checks: current git branch matches <type>/<TICKET>-<slug> where <type> is one of the values in config.yml → branch.types. Blocks: every flow command except /nova-start and /nova-status. Fix: run /nova-start <TICKET> first.
If ticket_system: none, the <TICKET> part can be any identifier.
2. proposal-exists
Checks: context/changes/active/<ticket-id>/proposal.md exists. Blocks: /nova-plan, /nova-build (non-quick-fix), /nova-review, /nova-wrap. Fix: run /nova-spec first.
3. tasks-exist
Checks: context/changes/active/<ticket-id>/tasks.md exists, with a quick-fix exception: tickets on fix/ branches can proceed without a tasks file. Blocks: /nova-build (non-quick-fix), /nova-review (non-quick-fix), /nova-wrap (non-quick-fix). Fix: run /nova-plan first.
4. all-tasks-done
Checks: if tasks.md exists, it has no remaining - [ ] lines. Blocks: /nova-review, /nova-wrap. Fix: run /nova-build until all tasks are checked.
5. review-approved
Checks: context/changes/active/<ticket-id>/review.md contains the literal string ✓ Ready for /nova-wrap. Blocks: /nova-wrap. Fix: run /nova-review until verdict is ✓.
6. old-decision-archived
Checks: every context/decisions/*.md containing > Supersedes: <X>.md implies <X>.md lives in context/decisions/archived/, not at root. Blocks: /nova-wrap. Fix: git mv context/decisions/<X>.md context/decisions/archived/<X>.md.
7. proposal-closed
Script: novaspec/guardrails/proposal-closed.shChecks: proposal.md contains none of: TBD, TODO, FIXME, ???, <placeholder>, [ ] decision. Blocks: /nova-plan. Fix: re-run /nova-spec and close the open requirements.
The bash scripts
Three guardrails are bash scripts (deterministic by construction):
nova-installed.sh
bash novaspec/guardrails/nova-installed.sh
# Exit 0 — installed
# Exit 1 — config.yml or context/ missingproposal-closed.sh
bash novaspec/guardrails/proposal-closed.sh <ticket-id>
# Exit 0 — proposal is closed
# Exit 1 — found unresolved markers (prints the lines)
# Exit 2 — proposal.md not foundMarkers it greps for: TBD, TODO, FIXME, ???, <placeholder>, [ ] decision. Add or remove patterns by editing the script.
review-checks.sh
bash novaspec/guardrails/review-checks.sh <ticket-id> [base-branch]
# Exit 0 — every applicable check passes
# Exit 1 — at least one blocking check failed
# Exit 2 — usage errorRuns in order:
- Diff non-empty —
git diff <base>...HEADandgit diff HEADnot both empty - Files-to-touch present — every path under
## Files to touchintasks.mdshows up in the diff - Lint clean —
npm/pnpm/yarn run lintexits 0 (skipped if nolintscript) - Tests pass —
npm/pnpm/yarn testexits 0 (skipped if notestscript)
Skipped checks don't fail; they print ℹ︎ and continue.
This script is invoked by nova-review-agent before the LLM review. If it fails, the verdict is ✗ Needs fixes immediately and the LLM review is skipped.
Customizing guardrails
The bash scripts are plain bash. Add sections, exit non-zero on violation:
# In review-checks.sh — block console.log in src/
if grep -rn "console\.log" src/ 2>/dev/null; then
echo "✗ console.log found in src/"
fail=1
fiFor checklist-based guardrails (1, 2, 3, 4, 5, 6) the rule lives in novaspec/guardrails/checklist.md as instructions the agent follows. To add a new one:
- Add
## 8. your-new-checksection tochecklist.md - (If deterministic) write
your-new-check.sh - Update the execution order line at the top
- Reference it in any command's
## Guardrailsection that should respect it
What guardrails are NOT
- They are not for quality. "The code is well-structured" is not a guardrail — it requires judgment.
- They are not LLM prompts disguised as checks. If the test is "does the model think this is OK", it's not a guardrail.
- They are not blockers you can't override. The developer can always escape — touch the file the guardrail expects, mark a task done by hand, etc. See PHILOSOPHY.md principle 5.
Where they're invoked
| Guardrail | Command(s) |
|---|---|
| 0 | All flow commands (any agent that calls nova-installed.sh) |
| 1 | /nova-spec, /nova-plan, /nova-build, /nova-review, /nova-wrap |
| 2 | /nova-plan, /nova-build (non-quick-fix), /nova-review, /nova-wrap |
| 3 | /nova-build, /nova-review, /nova-wrap (non-quick-fix) |
| 4 | /nova-review, /nova-wrap |
| 5 | /nova-wrap |
| 6 | /nova-wrap |
| 7 | /nova-plan |
Plus review-checks.sh is invoked by nova-review-agent before the LLM review.