WO Guard - Fail-Closed Invariant Gate
Principle: Skills are not advice; they are doors with keys.
Overview
This skill enforces fail-closed checks before any WO action. It is the gatekeeper that prevents agents from "doing whatever they want."
Without this skill, all other WO skills are just literature.
When to Use
| Checkpoint | When |
|---|---|
| PRE-TAKE | Before running ctx_wo_take.py |
| POST-TAKE | After ctx_wo_take.py completes |
| PRE-COMMIT | Before committing changes in worktree |
| PRE-FINISH | Before running ctx_wo_finish.py |
| POST-FINISH | After ctx_wo_finish.py completes |
Invariant Checks
PRE-TAKE Checks
| Check | Command | Expected |
|---|---|---|
| No locks from OTHER WOs | ls _ctx/jobs/running/*.lock | No locks for WOs != target WO |
| System healthy | ctx_wo_take.py --status | No errors |
| WO exists in pending | cat _ctx/jobs/pending/WO-XXXX.yaml | Valid YAML |
Note: Locks from the SAME WO are OK if WO is already running (re-taking).
POST-TAKE Checks
| Check | Command | Expected |
|---|---|---|
| In worktree | git worktree list --porcelain | Current dir matches WO worktree |
| Correct branch | git branch --show-current | feat/wo-WO-XXXX |
| YAML moved to running | cat _ctx/jobs/running/WO-XXXX.yaml | status: running |
| Lock exists | test -f _ctx/jobs/running/WO-XXXX.lock | Exit 0 |
PRE-COMMIT Checks
| Check | Command | Expected |
|---|---|---|
| In correct worktree | git worktree list --porcelain | Worktree matches WO |
| WO is running | Check YAML status field | running |
| Lock is valid | Check lock file | PID alive, age < TTL |
| Files in scope | git diff --name-only | All in scope.allow |
PRE-FINISH Checks
| Check | Command | Expected |
|---|---|---|
| In correct worktree | git worktree list --porcelain | Worktree matches WO |
| Correct branch | git branch --show-current | feat/wo-WO-XXXX |
| Lock exists | test -f _ctx/jobs/running/WO-XXXX.lock | Exit 0 |
| Session markers (BOTH) | grep "\[WO-XXXX\]" session.md | intent: AND result: present |
| Repo clean | git status --porcelain | Empty |
| Verify commands pass | Run commands from WO YAML | All PASS |
Note: PRE-FINISH does NOT check verdict.json (that's generated by finish).
POST-FINISH Checks
| Check | Command | Expected |
|---|---|---|
| Verdict exists | test -f _ctx/handoff/WO-XXXX/verdict.json | Exit 0 |
| Verdict PASS | cat verdict.json | "result": "PASS" |
| All DoD artifacts | ls _ctx/handoff/WO-XXXX/ | 5 files present |
| WO moved to done | cat _ctx/jobs/done/WO-XXXX.yaml | status: done |
SCHEMA VALIDATION Check (PRE-COMMIT)
| Check | Command | Expected |
|---|---|---|
| DoD schemas valid | ctx_backlog_validate.py | No WO_INVALID_SCHEMA |
| WO schemas valid | ctx_backlog_validate.py | No errors |
If schema validation fails:
- •STOP - Do not commit
- •Run
wo/repairIssue 7: "Reconcile Falls with WO_INVALID_SCHEMA" - •Fix schemas before proceeding
Worktree Check (Deterministic)
DO NOT use fragile string matching like git rev-parse --show-toplevel | grep ".worktrees".
USE the porcelain format:
bash
# Get worktree info git worktree list --porcelain # Verify current worktree matches WO git worktree list --porcelain | grep -A2 "worktree $(pwd)" | grep "branch feat/wo-WO-XXXX"
Lock TTL Validation
DO NOT use hardcoded "1 hour" rule.
The TTL is configurable:
- •Environment variable:
WO_LOCK_TTL_SEC(default:86400= 24h) - •Checker function:
scripts/helpers.py→check_lock_age()
Process:
bash
# Check lock age via Python
uv run python -c "
from scripts.helpers import check_lock_age
from pathlib import Path
result = check_lock_age(Path('_ctx/jobs/running/WO-XXXX.lock'))
print(f'Lock valid: {result}')
"
Failure Response
When any guard check fails:
- •STOP immediately
- •DIAGNOSE: Run
wo/statusfor snapshot - •SUGGEST: Point user to
wo/repair - •DO NOT PROCEED until guard passes
Example failure output:
code
⛔ GUARD FAILED: PRE-COMMIT Check: Correct worktree Expected: worktree for WO-XXXX Actual: main repo ACTION: You are in the wrong directory. Run: cd .worktrees/WO-XXXX Then: Re-run guard DO NOT COMMIT until guard passes.
Resources
- •
scripts/ctx_wo_take.py- Take script with status check - •
scripts/helpers.py- Lock age validation - •
scripts/ctx_reconcile_state.py- State reconciliation - •
_ctx/jobs/running/*.lock- Lock files
Quick Reference
bash
# Pre-take guard (WO-XXXX is target) ls _ctx/jobs/running/*.lock | grep -v "WO-XXXX.lock" # Should be empty or stale # Post-take guard git worktree list --porcelain | grep -A2 "$(pwd)" | grep "branch feat/wo-WO-XXXX" # Pre-commit guard git diff --name-only | while read f; do grep -q "$f" _ctx/jobs/running/WO-XXXX.yaml || exit 1; done # Pre-finish guard git status --porcelain && \ grep "\[WO-XXXX\] intent:" _ctx/session.md && \ grep "\[WO-XXXX\] result:" _ctx/session.md
Required Output
After running guard, always output:
code
GUARD_CHECKPOINT=<PRE-TAKE|POST-TAKE|PRE-COMMIT|PRE-FINISH|POST-FINISH> GUARD_RESULT=<PASS|FAIL> ACTIVE_WO=<WO-XXXX|none> CWD=<current directory> BRANCH=<current branch|none> STATE=<pending|running|done|failed|none> NEXT_ALLOWED=[<list of allowed actions>]