88# all Run all validators (default)
99# agents Validate agent files
1010# skills Validate skill files
11+ # closeout Check closeout-routing contract on completion skills
1112# runtime Check for stale references
1213# config Validate opencode.json
1314# hooks Test hook scripts
@@ -24,7 +25,7 @@ command="${1:-all}"
2425while [ " $# " -gt 0 ]; do
2526 case " $1 " in
2627 --root) root=" $( cd " $2 " && pwd -P) " ; shift 2 ;;
27- all|agents|skills|runtime|config|hooks|smoke|release) command=" $1 " ; shift ;;
28+ all|agents|skills|runtime|config|hooks|smoke|release|closeout ) command=" $1 " ; shift ;;
2829 * ) shift ;;
2930 esac
3031done
@@ -97,6 +98,67 @@ sys.exit(0)
9798 printf ' %d skills checked\n' " $count "
9899}
99100
101+ run_closeout () {
102+ printf ' \n── Closeout Routing ────────────────────────────────────────\n'
103+ local -a skills=(
104+ architecture-decision architecture-review art-bible asset-spec brainstorm
105+ code-review consistency-check design-system dev-story gate-check help
106+ map-systems project-stage-detect quick-design smoke-check story-done
107+ story-readiness team-qa ux-design
108+ )
109+ local -a markers=(
110+ " Verdict: **COMPLETE**"
111+ " Verdict: COMPLETE"
112+ " **Verdict: COMPLETE**"
113+ " ## Recommended Next Steps"
114+ )
115+ local -a required=(
116+ " Session Worklist"
117+ " production/session-state/active.md"
118+ " completed work"
119+ " owed verification"
120+ " numbered next-action prompt"
121+ " Next action:"
122+ " 1. (Recommended)"
123+ " (Recommended)"
124+ )
125+ local -a forbidden=(
126+ " one recommended next action"
127+ " numbered choice set"
128+ )
129+ local checked=0
130+ for s in " ${skills[@]} " ; do
131+ local f=" $root /.opencode/skills/$s /SKILL.md"
132+ [ -f " $f " ] || { fail " $s (no SKILL.md)" ; continue ; }
133+ checked=$(( checked + 1 ))
134+ # Trigger only when the skill declares a closeout marker.
135+ local has_marker=0 m
136+ for m in " ${markers[@]} " ; do
137+ if rg -q -F " $m " " $f " 2> /dev/null; then has_marker=1; break ; fi
138+ done
139+ if [ " $has_marker " -eq 0 ]; then
140+ pass " $s (no closeout marker; skipped)"
141+ continue
142+ fi
143+ local missing=() bad=()
144+ for m in " ${required[@]} " ; do
145+ if ! rg -q -F " $m " " $f " 2> /dev/null; then missing+=(" $m " ); fi
146+ done
147+ for m in " ${forbidden[@]} " ; do
148+ if rg -q -F " $m " " $f " 2> /dev/null; then bad+=(" $m " ); fi
149+ done
150+ if [ " ${# missing[@]} " -eq 0 ] && [ " ${# bad[@]} " -eq 0 ]; then
151+ pass " $s (closeout routing complete)"
152+ else
153+ local detail=" "
154+ [ " ${# missing[@]} " -gt 0 ] && detail+=" missing: ${missing[*]} "
155+ [ " ${# bad[@]} " -gt 0 ] && detail+=" forbidden-present: ${bad[*]} "
156+ fail " $s (closeout contract)$detail "
157+ fi
158+ done
159+ printf ' %d closeout skills checked\n' " $checked "
160+ }
161+
100162run_runtime () {
101163 printf ' \n── Runtime References ──────────────────────────────────────\n'
102164 # Check for stale Claude references
@@ -241,19 +303,21 @@ case "$command" in
241303 all)
242304 run_agents
243305 run_skills
306+ run_closeout
244307 run_runtime
245308 run_config
246309 run_hooks
247310 run_smoke
248311 ;;
249- agents) run_agents ;;
250- skills) run_skills ;;
251- runtime) run_runtime ;;
252- config) run_config ;;
253- hooks) run_hooks ;;
254- smoke) run_smoke ;;
255- release) run_release ;;
256- * ) printf ' Unknown command: %s\nAvailable: all, agents, skills, runtime, config, hooks, smoke, release\n' " $command " >&2 ; exit 2 ;;
312+ agents) run_agents ;;
313+ skills) run_skills ;;
314+ closeout) run_closeout ;;
315+ runtime) run_runtime ;;
316+ config) run_config ;;
317+ hooks) run_hooks ;;
318+ smoke) run_smoke ;;
319+ release) run_release ;;
320+ * ) printf ' Unknown command: %s\nAvailable: all, agents, skills, closeout, runtime, config, hooks, smoke, release\n' " $command " >&2 ; exit 2 ;;
257321esac
258322
259323printf ' \n── Result: %d error(s) ──\n' " $errors "
0 commit comments