Enforce fail-closed invariants before any WO action
Principle: Skills are not advice; they are doors with keys.
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.
| 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 |
| 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).
| 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 |
| 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 |
| 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).
| 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 |
| 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:
wo/repair Issue 7: "Reconcile Falls with WO_INVALID_SCHEMA"DO NOT use fragile string matching like git rev-parse --show-toplevel | grep ".worktrees".
USE the porcelain format:
# 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"
DO NOT use hardcoded "1 hour" rule.
The TTL is configurable:
WO_LOCK_TTL_SEC (default: 86400 = 24h)scripts/helpers.py → check_lock_age()Process:
# 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}')
"
When any guard check fails:
wo/status for snapshotwo/repairExample failure output:
⛔ 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.
scripts/ctx_wo_take.py - Take script with status checkscripts/helpers.py - Lock age validationscripts/ctx_reconcile_state.py - State reconciliation_ctx/jobs/running/*.lock - Lock files# 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
After running guard, always output:
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>]