@hanzlaa/rcode 3.4.32 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/AGENTS.md +6 -6
  2. package/CONTRIBUTING.md +2 -0
  3. package/LICENSE +21 -0
  4. package/README.md +66 -403
  5. package/cli/agent.js +3 -2
  6. package/cli/doctor.js +87 -1
  7. package/cli/install.js +122 -31
  8. package/cli/lib/schemas.cjs +318 -0
  9. package/cli/postinstall.js +19 -3
  10. package/dist/rcode.js +318 -24
  11. package/package.json +8 -4
  12. package/rihal/agents/rihal-cross-platform-auditor.md +15 -0
  13. package/rihal/agents/rihal-dep-auditor.md +15 -0
  14. package/rihal/agents/rihal-docs-auditor.md +3 -145
  15. package/rihal/agents/rihal-i18n-auditor.md +16 -0
  16. package/rihal/agents/rihal-nyquist-auditor.md +4 -156
  17. package/rihal/agents/rihal-observability-auditor.md +16 -0
  18. package/rihal/agents/rihal-phase-researcher.md +1 -1
  19. package/rihal/agents/rihal-planner.md +1 -1
  20. package/rihal/bin/rihal-hooks.cjs +394 -4
  21. package/rihal/bin/rihal-tools.cjs +891 -24
  22. package/rihal/commands/create-prd.md +18 -0
  23. package/rihal/commands/execute-milestone.md +18 -0
  24. package/rihal/commands/plan-milestone.md +18 -0
  25. package/rihal/commands/scaffold-milestone.md +18 -0
  26. package/rihal/commands/scaffold-skill.md +18 -0
  27. package/rihal/references/REFERENCES_INDEX.md +49 -7
  28. package/rihal/references/agent-contracts.md +10 -0
  29. package/rihal/references/design-tokens.md +98 -0
  30. package/rihal/references/docs-auditor-playbook.md +148 -0
  31. package/rihal/references/git-preflight.md +117 -0
  32. package/rihal/references/iterative-retrieval.md +85 -0
  33. package/rihal/references/nyquist-auditor-playbook.md +157 -0
  34. package/rihal/references/workstream-flag.md +2 -2
  35. package/rihal/skills/actions/1-analysis/rihal-prfaq/SKILL.md +9 -0
  36. package/rihal/skills/actions/4-implementation/rihal-checkpoint-preview/SKILL.md +9 -0
  37. package/rihal/skills/actions/4-implementation/rihal-ci/SKILL.md +4 -0
  38. package/rihal/skills/actions/4-implementation/rihal-code-review/steps/step-02-review.md +7 -3
  39. package/rihal/skills/actions/4-implementation/rihal-harden/SKILL.md +4 -0
  40. package/rihal/skills/actions/4-implementation/rihal-migrate/SKILL.md +4 -0
  41. package/rihal/skills/agents/haitham-frontend/SKILL.md +2 -0
  42. package/rihal/skills/agents/majlis-council/SKILL.md +1 -1
  43. package/rihal/team.yaml +32 -0
  44. package/rihal/templates/settings-hooks.json +39 -0
  45. package/rihal/workflows/check-todos.md +4 -0
  46. package/rihal/workflows/code-review-fix.md +4 -3
  47. package/rihal/workflows/code-review.md +1 -1
  48. package/rihal/workflows/debug.md +1 -1
  49. package/rihal/workflows/dev-story.md +4 -0
  50. package/rihal/workflows/diff.md +2 -2
  51. package/rihal/workflows/do.md +16 -8
  52. package/rihal/workflows/docs-update.md +2 -2
  53. package/rihal/workflows/enable-hooks.md +6 -1
  54. package/rihal/workflows/execute-milestone.md +139 -0
  55. package/rihal/workflows/execute-regression-gates.md +1 -1
  56. package/rihal/workflows/execute-sprint.md +54 -2
  57. package/rihal/workflows/execute-verify-phase-goal.md +31 -4
  58. package/rihal/workflows/execute-waves.md +33 -5
  59. package/rihal/workflows/execute.md +40 -6
  60. package/rihal/workflows/help.md +1 -1
  61. package/rihal/workflows/import.md +1 -1
  62. package/rihal/workflows/lens-audit.md +39 -23
  63. package/rihal/workflows/list-workspaces.md +1 -1
  64. package/rihal/workflows/map-codebase.md +4 -4
  65. package/rihal/workflows/new-milestone.md +18 -1
  66. package/rihal/workflows/new-project-research.md +53 -1
  67. package/rihal/workflows/new-workspace.md +1 -1
  68. package/rihal/workflows/plan-milestone.md +105 -0
  69. package/rihal/workflows/plan-research-validation.md +1 -1
  70. package/rihal/workflows/plan-spawn-planner.md +1 -1
  71. package/rihal/workflows/plan.md +31 -3
  72. package/rihal/workflows/plant-seed.md +6 -0
  73. package/rihal/workflows/quick.md +11 -5
  74. package/rihal/workflows/research-phase.md +24 -0
  75. package/rihal/workflows/scaffold-milestone.md +60 -0
  76. package/rihal/workflows/scaffold-skill.md +137 -0
  77. package/rihal/workflows/scan.md +1 -1
  78. package/rihal/workflows/session-report.md +43 -3
  79. package/rihal/workflows/verify-work.md +3 -3
  80. package/server/dashboard.js +53 -6
  81. package/server/lib/api.js +7 -0
  82. package/server/lib/html/client.js +725 -13
  83. package/server/lib/html/css.js +2046 -466
  84. package/server/lib/html/shell.js +227 -134
  85. package/server/lib/scanner.js +33 -0
  86. package/server/orchestrator.js +438 -0
@@ -0,0 +1,139 @@
1
+ <purpose>
2
+ Execute all phases in the current milestone in dependency order, with verify gates between waves. Closes #738.
3
+ Reads the ROADMAP.md to determine phase ordering, executes each phase via /rihal-execute, runs /rihal-verify after each, and surfaces blockers before advancing.
4
+ </purpose>
5
+
6
+ <required_reading>
7
+ @.rihal/references/output-format.md
8
+ Read all files referenced by the invoking prompt's execution_context before starting.
9
+ </required_reading>
10
+
11
+ ## 1. Parse arguments and load context
12
+
13
+ Parse `$ARGUMENTS`:
14
+ - `--milestone <name>` — filter to a named milestone (optional; default: current milestone from state)
15
+ - `--dry-run` — show execution plan without spawning agents
16
+ - `--skip-verify` — skip the verify gate between phases (fast mode, not recommended)
17
+ - `--wave <N>` — start from wave N (resume after partial failure)
18
+ - `--phase <N>` — execute only this phase (single-phase mode)
19
+
20
+ ```bash
21
+ INIT_JSON=$(node ".rihal/bin/rihal-tools.cjs" state read 2>/dev/null || echo '{}')
22
+ CURRENT_MILESTONE=$(echo "$INIT_JSON" | grep -o '"current_milestone":"[^"]*"' | cut -d'"' -f4 || echo '')
23
+ MILESTONE_TARGET="${MILESTONE_ARG:-$CURRENT_MILESTONE}"
24
+ ```
25
+
26
+ ## 2. Load phase dependency order
27
+
28
+ Read `.planning/ROADMAP.md` and extract phases for the target milestone in order. For each phase, note its status in `state.json`.
29
+
30
+ ```bash
31
+ ROADMAP_PHASES=$(node ".rihal/bin/rihal-tools.cjs" roadmap list-phases 2>/dev/null || echo '[]')
32
+ ```
33
+
34
+ Filter to phases with status `planned` or `in_progress`. Already `complete` phases are skipped unless `--force` is passed.
35
+
36
+ **Display execution plan:**
37
+ ```
38
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
39
+ RIHAL ► EXECUTE MILESTONE: {MILESTONE_TARGET}
40
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
41
+
42
+ Phases to execute:
43
+ Phase {N1}: {name} [planned]
44
+ Phase {N2}: {name} [planned]
45
+ ...
46
+
47
+ Verify gates: {enabled|disabled}
48
+ ```
49
+
50
+ If `--dry-run`: print the plan and exit without executing.
51
+
52
+ ## 3. Execute phases in dependency wave order
53
+
54
+ For each pending phase (in ROADMAP order):
55
+
56
+ **3a. Pre-phase gate:**
57
+ ```bash
58
+ READINESS=$(node ".rihal/bin/rihal-tools.cjs" check-implementation-readiness --phase "${PHASE_NUM}" 2>/dev/null)
59
+ READY=$(echo "$READINESS" | grep -o '"ready":true' | grep -c . || echo 0)
60
+ ```
61
+
62
+ If `READY == 0` and `--skip-verify` is NOT set, print blockers and pause:
63
+ ```
64
+ ⛔ Phase {N} readiness check failed:
65
+ {blockers}
66
+
67
+ Options:
68
+ 1. Fix blockers and re-run /rihal-execute-milestone
69
+ 2. Skip this phase (--skip N) and continue
70
+ 3. Abort milestone execution
71
+ ```
72
+
73
+ **3b. Execute the phase:**
74
+ ```
75
+ ◆ Executing Phase {N}: {name}...
76
+ ```
77
+
78
+ Invoke `/rihal-execute {PHASE_NUM}` via Skill or Agent dispatch.
79
+
80
+ **3c. Verify gate (unless --skip-verify):**
81
+
82
+ After each phase execution completes:
83
+ ```bash
84
+ VERIFY_RESULT=$(node ".rihal/bin/rihal-tools.cjs" state read | grep -o '"status":"[^"]*"' | head -1 || echo '"status":"unknown"')
85
+ ```
86
+
87
+ Spawn `rihal-verifier` for the phase. On `FAIL` or `PARTIAL`:
88
+ ```
89
+ ⚠ Verify gate: Phase {N} — {FAIL|PARTIAL}
90
+
91
+ Gap count: {N}
92
+ Recommendation: Run /rihal-sprint-plan {N} --gaps to close before advancing.
93
+
94
+ Options:
95
+ 1. Close gaps now (spawn /rihal-sprint-plan --gaps) [Recommended]
96
+ 2. Advance anyway (log gap, continue to next phase)
97
+ 3. Abort milestone execution
98
+ ```
99
+
100
+ **3d. Record completion:**
101
+ ```bash
102
+ node ".rihal/bin/rihal-tools.cjs" state set-phase "${PHASE_NAME}" 2>/dev/null || true
103
+ ```
104
+
105
+ ## 4. Milestone completion summary
106
+
107
+ After all phases complete:
108
+ ```
109
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
110
+ RIHAL ► MILESTONE COMPLETE ✓ {MILESTONE_TARGET}
111
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
112
+
113
+ Phases executed: {N}
114
+ Phases skipped: {M}
115
+ Verify gaps found: {K}
116
+
117
+ Next step: /rihal-complete-milestone
118
+ ```
119
+
120
+ ## Output Format
121
+
122
+ Open with banner (see step 2). Closure banner (see step 4).
123
+
124
+ Per-phase lines:
125
+ ```
126
+ ✓ Phase {N}: {name} — complete
127
+ ⚠ Phase {N}: {name} — partial (gaps logged)
128
+ ✗ Phase {N}: {name} — failed (blocked)
129
+ ```
130
+
131
+ ## Examples
132
+
133
+ **Happy path:** `/rihal-execute-milestone` → executes all planned phases in order with verify gates.
134
+
135
+ **Single phase:** `/rihal-execute-milestone --phase 7` → equivalent to `/rihal-execute 7` but with the milestone verify gate wired in.
136
+
137
+ **Resume:** `/rihal-execute-milestone --wave 3` → skip waves 1-2 (already complete), start from wave 3.
138
+
139
+ **Dry run:** `/rihal-execute-milestone --dry-run` → print execution plan, exit.
@@ -70,7 +70,7 @@ build/types pass because TypeScript types come from config, not the live databas
70
70
  **Run after execution completes but BEFORE verification marks success.**
71
71
 
72
72
  ```bash
73
- SCHEMA_DRIFT=$(node ".rihal/bin/rihal-tools.cjs" verify schema-drift "${PHASE_NUMBER}" 2>/dev/null)
73
+ SCHEMA_DRIFT=$(node ".rihal/bin/rihal-tools.cjs" verify schema-drift "${PHASE_NUMBER}" 2>/dev/null || echo '{"ok":false,"drift_detected":true,"blocking":true,"message":"schema-drift command failed — treating as drift to fail safe"}')
74
74
  ```
75
75
 
76
76
  Parse JSON result for: `drift_detected`, `blocking`, `schema_files`, `orms`, `unpushed_orms`, `message`.
@@ -25,7 +25,9 @@ INIT=$(node ".rihal/bin/rihal-tools.cjs" init execute "${PHASE}")
25
25
  if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
26
26
  ```
27
27
 
28
- Extract from init JSON: `executor_model`, `commit_docs`, `sub_repos`, `phase_dir`, `phase_number`, `plans`, `summaries`, `incomplete_plans`, `state_path`, `config_path`.
28
+ Extract from init JSON: `executor_model`, `commit_docs`, `sub_repos`, `phase_dir`, `phase_number`, `plans`, `summaries`, `incomplete_plans`, `state_path`, `config_path`, `response_language`.
29
+
30
+ **If `response_language` is set:** include `Respond in {response_language}.` in every spawned subagent prompt (executor, debugger, gap-closer). VERIFICATION.md, SUMMARY.md, and any human-facing prose must be written in that language. Code, identifiers, commit messages, and file paths stay English.
29
31
 
30
32
  If `.planning/` missing: error.
31
33
  </step>
@@ -41,7 +43,12 @@ Find first PLAN without matching SUMMARY. Decimal phases supported (`01.1-hotfix
41
43
 
42
44
  ```bash
43
45
  PHASE=$(echo "$PLAN_PATH" | grep -oE '[0-9]+(\.[0-9]+)?-[0-9]+')
44
- # config settings can be fetched via rihal-tools config-get if needed
46
+
47
+ # Scoped yolo check — closes #739. Honours yolo_scope and yolo_ttl from config.
48
+ PHASE_NUM=$(echo "$PHASE" | grep -oE '^[0-9]+(\.[0-9]+)?')
49
+ YOLO_STATUS=$(node ".rihal/bin/rihal-tools.cjs" config-check-yolo --phase "${PHASE_NUM}" --workflow execute-sprint 2>/dev/null || echo '{"active":false}')
50
+ YOLO_ACTIVE=$(echo "$YOLO_STATUS" | grep -o '"active":true' | grep -c . || echo 0)
51
+ [ "$YOLO_ACTIVE" -gt 0 ] && CONFIG_MODE="yolo" || CONFIG_MODE=$(node ".rihal/bin/rihal-tools.cjs" config-get mode 2>/dev/null || echo "guided")
45
52
  ```
46
53
 
47
54
  <if mode="yolo">
@@ -335,6 +342,51 @@ If new untracked files appeared after running scripts or tools, decide for each:
335
342
 
336
343
  </task_commit>
337
344
 
345
+ <post_step_revert_gate>
346
+ ## Post-Step Revert Detection Gate (closes #737)
347
+
348
+ After committing each task, run a diff check to detect accidental reverts. This catches the class of bug where a task's implementation unknowingly undoes work from a previous task or wave.
349
+
350
+ **When to run:** After every `git commit` that records a task completion (not after TDD RED commits or style-only commits).
351
+
352
+ **Detection:**
353
+ ```bash
354
+ # Compare current HEAD against the commit that started this plan execution
355
+ # PLAN_START_SHA is captured at plan execution start (before first task commit)
356
+ PLAN_START_SHA="${PLAN_START_SHA:-$(git rev-parse HEAD~${COMPLETED_TASK_COUNT:-1} 2>/dev/null || echo '')}"
357
+
358
+ if [[ -n "$PLAN_START_SHA" ]]; then
359
+ REVERTED_FILES=$(git diff --name-only "${PLAN_START_SHA}"..HEAD | xargs -I{} sh -c \
360
+ 'git show '"$PLAN_START_SHA"':{} 2>/dev/null | diff - <(git show HEAD:{} 2>/dev/null) >/dev/null 2>&1 && echo {}' 2>/dev/null || true)
361
+ fi
362
+
363
+ # Simpler check: look for net-zero or net-negative line delta on committed source files
364
+ STAGED_SUMMARY=$(git diff --stat HEAD~1..HEAD 2>/dev/null | tail -1)
365
+ ```
366
+
367
+ **Revert signal heuristics — flag for review if:**
368
+ 1. A source file that was modified by a *previous* task (visible in `git log --oneline -10 -- <file>`) has FEWER lines now than at `PLAN_START_SHA`.
369
+ 2. A function or class that was added in a prior task is no longer present in the file at `HEAD`.
370
+ 3. `git diff --stat HEAD~1..HEAD` shows a large deletion count with no corresponding additions.
371
+
372
+ **On revert signal detected:**
373
+ ```
374
+ ⚠ Revert signal on task {N}: {file} appears to have fewer lines than at plan start.
375
+ Plan start SHA: {PLAN_START_SHA}
376
+ Current HEAD: {current_hash}
377
+
378
+ Review with: git diff {PLAN_START_SHA}..HEAD -- {file}
379
+
380
+ Options:
381
+ a) The reduction is intentional (refactor/simplification) — continue
382
+ b) Work was accidentally overwritten — fix before proceeding
383
+ ```
384
+
385
+ Pause and present this to the user. Do NOT auto-continue on a revert signal. If the user confirms it's intentional, continue. If it's an accidental revert, restore the lost work before committing the next task.
386
+
387
+ **Performance:** Skip this check on tasks with zero deletions (`git diff --stat HEAD~1..HEAD | grep -q ' 0 deletions'`) — deletions are the precursor to a revert; pure additions cannot revert prior work.
388
+ </post_step_revert_gate>
389
+
338
390
  <step name="checkpoint_protocol">
339
391
  On `type="checkpoint:*"`: automate everything possible first. Checkpoints are for verification/decisions only.
340
392
 
@@ -9,10 +9,12 @@ Verify phase achieved its GOAL, not just completed tasks.
9
9
  VERIFIER_SKILLS=$(node ".rihal/bin/rihal-tools.cjs" agent-skills rihal-verifier 2>/dev/null)
10
10
  ```
11
11
 
12
+ If init JSON has `response_language` set, prepend `Respond in {response_language}.` to the prompt and require VERIFICATION.md prose (rationale, gap descriptions, human_verification items) to be written in that language. Status keys, file paths, and requirement IDs stay English.
13
+
12
14
  ```
13
15
  Task(
14
16
  description="Verify phase {phase_number} goal achievement",
15
- prompt="Verify phase {phase_number} goal achievement.
17
+ prompt="${response_language ? `Respond in ${response_language}. Write VERIFICATION.md prose in ${response_language}; keep status keys, file paths, and requirement IDs in English.\n\n` : ''}Verify phase {phase_number} goal achievement.
16
18
  Phase directory: {phase_dir}
17
19
  Phase goal: {goal from ROADMAP.md}
18
20
  Phase requirement IDs: {phase_req_ids}
@@ -33,14 +35,19 @@ ${CONTEXT_WINDOW >= 500000 ? `- {phase_dir}/*-CONTEXT.md (User decisions — ver
33
35
 
34
36
  ${VERIFIER_SKILLS}",
35
37
  subagent_type="rihal-verifier",
36
- model="sonnet",
37
38
  model="{verifier_model}"
38
39
  )
39
40
  ```
40
41
 
41
- Read status:
42
+ Read status (fail-safe: missing or empty file means verifier never produced a result):
42
43
  ```bash
43
- grep "^status:" "$PHASE_DIR"/*-VERIFICATION.md | cut -d: -f2 | tr -d ' '
44
+ VERIFICATION_FILE=$(ls "$PHASE_DIR"/*-VERIFICATION.md 2>/dev/null | head -1)
45
+ if [ -z "$VERIFICATION_FILE" ] || [ ! -s "$VERIFICATION_FILE" ]; then
46
+ VERIFY_STATUS="verifier_failed"
47
+ else
48
+ VERIFY_STATUS=$(grep "^status:" "$VERIFICATION_FILE" | head -1 | cut -d: -f2 | tr -d ' ')
49
+ [ -z "$VERIFY_STATUS" ] && VERIFY_STATUS="verifier_failed"
50
+ fi
44
51
  ```
45
52
 
46
53
  | Status | Action |
@@ -48,6 +55,26 @@ grep "^status:" "$PHASE_DIR"/*-VERIFICATION.md | cut -d: -f2 | tr -d ' '
48
55
  | `passed` | → update_roadmap |
49
56
  | `human_needed` | Present items for human testing, get approval or feedback |
50
57
  | `gaps_found` | Present gap summary, offer `/rihal-plan {phase} --gaps ${Rihal_WS}` |
58
+ | `verifier_failed` | Abort: VERIFICATION.md missing/empty/unparseable. Do NOT mark phase complete. Print the verifier-failure message below and exit 1. |
59
+
60
+ **If verifier_failed:**
61
+
62
+ ```
63
+ ✖ Phase {X}: verifier agent did not produce a usable VERIFICATION.md.
64
+
65
+ This means the rihal-verifier subagent crashed, returned no output, or wrote
66
+ an invalid status header. The phase will NOT be marked complete.
67
+
68
+ Next steps:
69
+ 1. Re-run: /rihal-verify-phase {X}
70
+ 2. If it fails again, check rihal-verifier prompt/skills:
71
+ node .rihal/bin/rihal-tools.cjs agent-skills rihal-verifier
72
+ 3. As a last resort, manually create VERIFICATION.md and run /rihal-next.
73
+
74
+ Do NOT run /rihal-next until VERIFICATION.md exists with a valid status.
75
+ ```
76
+
77
+ Exit the workflow with non-zero status. Do not fall through to update_roadmap.
51
78
 
52
79
  **If human_needed:**
53
80
 
@@ -91,7 +91,6 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT
91
91
  ```
92
92
  Task(
93
93
  subagent_type="rihal-executor",
94
- model="sonnet",
95
94
  description="Execute plan {plan_number} of phase {phase_number}",
96
95
  model="{executor_model}",
97
96
  isolation="worktree",
@@ -102,6 +101,13 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT
102
101
  Do NOT update STATE.md or ROADMAP.md — the orchestrator owns those writes after all worktree agents in the wave complete.
103
102
  </objective>
104
103
 
104
+ <!--
105
+ #721 i18n: when init JSON's response_language is set, prepend this line
106
+ verbatim to the prompt before the objective. Human-facing prose in
107
+ SUMMARY.md must be in {response_language}; code/identifiers stay English.
108
+ -->
109
+ ${response_language ? `Respond in ${response_language}. Write SUMMARY.md prose in ${response_language}; keep code, file paths, identifiers, and commit messages in English.` : ''}
110
+
105
111
  <worktree_branch_check>
106
112
  FIRST ACTION before any other work: verify this worktree's branch is based on the correct commit.
107
113
 
@@ -128,11 +134,33 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT
128
134
  <parallel_execution>
129
135
  You are running as a PARALLEL executor agent. To avoid pre-commit hook
130
136
  contention with other agents, acquire a file-based lock before each
131
- commit and release it immediately after:
132
-
133
- while ! mkdir .rihal/.commit-lock 2>/dev/null; do sleep 0.5; done
137
+ commit and release it immediately after. The spinlock has a 60-second
138
+ timeout and a stale-lock recovery (older than 5 minutes is broken):
139
+
140
+ LOCK_DIR=".rihal/.commit-lock"
141
+ LOCK_WAIT=0
142
+ LOCK_MAX=60 # seconds — abort if we never acquire
143
+ LOCK_STALE=300 # seconds — assume holder is dead and break
144
+ while ! mkdir "$LOCK_DIR" 2>/dev/null; do
145
+ if [ -d "$LOCK_DIR" ]; then
146
+ AGE=$(( $(date +%s) - $(stat -c %Y "$LOCK_DIR" 2>/dev/null || stat -f %m "$LOCK_DIR" 2>/dev/null || echo 0) ))
147
+ if [ "$AGE" -gt "$LOCK_STALE" ]; then
148
+ echo "⚠ Breaking stale commit lock (age ${AGE}s > ${LOCK_STALE}s)"
149
+ rmdir "$LOCK_DIR" 2>/dev/null
150
+ continue
151
+ fi
152
+ fi
153
+ if [ "$LOCK_WAIT" -ge "$LOCK_MAX" ]; then
154
+ echo "✖ Failed to acquire commit lock within ${LOCK_MAX}s — aborting wave" >&2
155
+ exit 1
156
+ fi
157
+ sleep 0.5
158
+ LOCK_WAIT=$(( LOCK_WAIT + 1 ))
159
+ done
160
+ trap 'rmdir "$LOCK_DIR" 2>/dev/null' EXIT
134
161
  git commit -m "..." # hooks run normally
135
- rmdir .rihal/.commit-lock
162
+ rmdir "$LOCK_DIR"
163
+ trap - EXIT
136
164
 
137
165
  Hooks run as designed for every commit. AGENTS.md forbids --no-verify;
138
166
  hook failures must be fixed at the source, not bypassed. The orchestrator
@@ -142,7 +142,7 @@ Orchestrator coordinates, not executes. Each subagent loads the full execute-spr
142
142
  <runtime_compatibility>
143
143
  **Subagent spawning is runtime-specific:**
144
144
  - **Claude Code:** Uses `Task(subagent_type="rihal-executor",
145
- model="sonnet", ...)` — blocks until complete, returns result
145
+ model="{executor_model}", ...)` — blocks until complete, returns result
146
146
  - **Copilot:** Subagent spawning does not reliably return completion signals. **Default to
147
147
  sequential inline execution**: read and follow execute-sprint.md directly for each plan
148
148
  instead of spawning parallel agents. Only attempt parallel spawning if the user
@@ -160,6 +160,7 @@ via filesystem and git state.
160
160
  <required_reading>
161
161
  @.rihal/references/auto-init-guard.md
162
162
  @.rihal/references/output-format.md
163
+ @.rihal/references/git-preflight.md
163
164
  Read STATE.md before any operation to load project context.
164
165
 
165
166
  @.rihal/references/agent-contracts.md
@@ -628,12 +629,45 @@ ${REVIEWER_SKILLS}",
628
629
 
629
630
  **Parse severity counts:**
630
631
  ```bash
632
+ # Fail-safe defaults — malformed/missing frontmatter must NOT bypass the gate (#602).
633
+ # Empty string compared against an integer in bash evaluates to false, which
634
+ # would silently let critical findings through. Default missing to a sentinel
635
+ # that fails the gate so a bad REVIEW.md is treated as "block, ask the user".
636
+ REVIEW_STATUS="malformed"
637
+ CRITICAL_COUNT=0
638
+ HIGH_COUNT=0
639
+ MEDIUM_COUNT=0
640
+ LOW_COUNT=0
641
+ REVIEW_PARSE_OK=false
631
642
  if [[ -f "$REVIEW_FILE" ]]; then
632
- REVIEW_STATUS=$(sed -n '/^---$/,/^---$/p' "$REVIEW_FILE" | grep "^status:" | head -1 | cut -d: -f2 | tr -d ' ')
633
- CRITICAL_COUNT=$(sed -n '/^---$/,/^---$/p' "$REVIEW_FILE" | grep "^critical:" | head -1 | cut -d: -f2 | tr -d ' ')
634
- HIGH_COUNT=$(sed -n '/^---$/,/^---$/p' "$REVIEW_FILE" | grep "^high:" | head -1 | cut -d: -f2 | tr -d ' ')
635
- MEDIUM_COUNT=$(sed -n '/^---$/,/^---$/p' "$REVIEW_FILE" | grep "^medium:" | head -1 | cut -d: -f2 | tr -d ' ')
636
- LOW_COUNT=$(sed -n '/^---$/,/^---$/p' "$REVIEW_FILE" | grep "^low:" | head -1 | cut -d: -f2 | tr -d ' ')
643
+ FRONTMATTER=$(sed -n '/^---$/,/^---$/p' "$REVIEW_FILE")
644
+ PARSED_STATUS=$(echo "$FRONTMATTER" | grep "^status:" | head -1 | cut -d: -f2 | tr -d ' ')
645
+ PARSED_CRIT=$(echo "$FRONTMATTER" | grep "^critical:" | head -1 | cut -d: -f2 | tr -d ' ')
646
+ PARSED_HIGH=$(echo "$FRONTMATTER" | grep "^high:" | head -1 | cut -d: -f2 | tr -d ' ')
647
+ PARSED_MED=$(echo "$FRONTMATTER" | grep "^medium:" | head -1 | cut -d: -f2 | tr -d ' ')
648
+ PARSED_LOW=$(echo "$FRONTMATTER" | grep "^low:" | head -1 | cut -d: -f2 | tr -d ' ')
649
+ # Accept the parse only when status + all four counts are present AND counts
650
+ # are pure digits. Anything else = malformed → block and ask.
651
+ if [[ -n "$PARSED_STATUS" \
652
+ && "$PARSED_CRIT" =~ ^[0-9]+$ \
653
+ && "$PARSED_HIGH" =~ ^[0-9]+$ \
654
+ && "$PARSED_MED" =~ ^[0-9]+$ \
655
+ && "$PARSED_LOW" =~ ^[0-9]+$ ]]; then
656
+ REVIEW_STATUS="$PARSED_STATUS"
657
+ CRITICAL_COUNT="$PARSED_CRIT"
658
+ HIGH_COUNT="$PARSED_HIGH"
659
+ MEDIUM_COUNT="$PARSED_MED"
660
+ LOW_COUNT="$PARSED_LOW"
661
+ REVIEW_PARSE_OK=true
662
+ fi
663
+ fi
664
+
665
+ # Malformed REVIEW.md = treat as a blocking finding. The gate must NEVER
666
+ # silently pass when it can't read the report (#602).
667
+ if [[ "$REVIEW_PARSE_OK" != "true" ]]; then
668
+ echo "⛔ Code review gate: REVIEW.md missing or malformed at ${REVIEW_FILE}."
669
+ echo " Cannot determine severity counts. Treating as blocking — re-run the reviewer."
670
+ CRITICAL_COUNT=1 # force the gate to block; user can override below
637
671
  fi
638
672
  ```
639
673
 
@@ -85,7 +85,7 @@ init → new-project → plan → execute → next → status → ship
85
85
  ├── STATE.md # project memory
86
86
  └── phases/
87
87
  └── 01-foundation/
88
- ├── 01-01-SPRINT.md # the plan
88
+ ├── 01-1-SPRINT.md # the plan
89
89
  └── 01-01-SUMMARY.md # what got done
90
90
  ```
91
91
 
@@ -218,7 +218,7 @@ must_haves:
218
218
  If the imported plan references PBR plan naming (e.g., `PLAN-01.md`, `plan-01.md`), rename all references to RIHAL `{NN}-{MM}-SPRINT.md` convention during conversion.
219
219
 
220
220
  Apply RIHAL naming convention for the output filename:
221
- - Format: `{NN}-{MM}-SPRINT.md` (e.g., `04-01-SPRINT.md`)
221
+ - Format: `{NN}-{MM}-SPRINT.md` (e.g., `04-1-SPRINT.md`)
222
222
  - NEVER use `PLAN-01.md`, `plan-01.md`, or any other format
223
223
  - NN = phase number (zero-padded), MM = plan number within the phase (zero-padded)
224
224
 
@@ -48,7 +48,7 @@ Lenses and their primary skills:
48
48
  8. i18n — rihal-i18n-auditor
49
49
  9. documentation — rihal-docs-auditor
50
50
  10. cross-platform — rihal-cross-platform-auditor
51
- 11. karpathy — rihal-code-reviewer + rihal-hanzla
51
+ 11. karpathy — rihal-code-reviewer + rihal-hanzla (incl. design-token bypass)
52
52
  12. sxo — rihal-layla
53
53
  13. observability — rihal-observability-auditor
54
54
  14. naming — rihal-codebase-mapper + rihal-code-reviewer
@@ -63,7 +63,18 @@ STOP after printing help.
63
63
  TOOL="node .rihal/bin/rihal-tools.cjs"
64
64
  INIT=$($TOOL init 2>/dev/null || echo '{"ok":false}')
65
65
  MODE=$($TOOL config-get mode 2>/dev/null || echo "guided")
66
- RESPONSE_LANGUAGE=$($TOOL config-get response_language 2>/dev/null || echo "english")
66
+ RESPONSE_LANGUAGE=$($TOOL config-get response_language 2>/dev/null || echo "")
67
+ # Resolve audit model from profile (#723). Order: audit_model → default_model → sonnet.
68
+ LENS_MODEL=$($TOOL config-get audit_model 2>/dev/null \
69
+ || $TOOL config-get default_model 2>/dev/null \
70
+ || echo "sonnet")
71
+ # Build an imperative directive only when language is explicitly set (#721).
72
+ # Empty RESPONSE_LANGUAGE = English default, no directive injected.
73
+ if [ -n "$RESPONSE_LANGUAGE" ] && [ "$RESPONSE_LANGUAGE" != "english" ]; then
74
+ LANG_DIRECTIVE="Respond in $RESPONSE_LANGUAGE. Keep finding IDs, file paths, and CLI commands in English; localise only the human-facing prose."
75
+ else
76
+ LANG_DIRECTIVE=""
77
+ fi
67
78
  ```
68
79
 
69
80
  If INIT is empty or INIT.ok is false, print error and exit:
@@ -119,7 +130,7 @@ Set `LENSES` from the choice.
119
130
  SCOPE_DIRS="rihal/ .rihal/"
120
131
  [ -d src ] && SCOPE_DIRS="$SCOPE_DIRS src/"
121
132
  [ -d lib ] && SCOPE_DIRS="$SCOPE_DIRS lib/"
122
- SCOPE_SUMMARY="Scope: $SCOPE_DIRS. Response language: $RESPONSE_LANGUAGE."
133
+ SCOPE_SUMMARY="Scope: $SCOPE_DIRS.${LANG_DIRECTIVE:+ $LANG_DIRECTIVE}"
123
134
 
124
135
  # Collect project context for richer prompts
125
136
  PROJECT_NAME=$($TOOL config-get project.name 2>/dev/null || basename "$PWD")
@@ -143,7 +154,7 @@ Never halt the whole audit because one lens's skill fails.
143
154
  ```
144
155
  PRIMARY = Task(
145
156
  subagent_type="rihal-security-auditor",
146
- model="sonnet",
157
+ model="{lens_model}",
147
158
  prompt="Audit-only — do NOT fix anything. {CONTEXT}
148
159
 
149
160
  Run Lens 1 (Security) audit. Check:
@@ -161,7 +172,7 @@ PRIMARY = Task(
161
172
 
162
173
  SECONDARY = Task(
163
174
  subagent_type="rihal-security-adversary",
164
- model="sonnet",
175
+ model="{lens_model}",
165
176
  prompt="Adversarial security review. {CONTEXT}
166
177
 
167
178
  Think like an attacker. Find exploitation paths in:
@@ -183,7 +194,7 @@ FINDINGS[security] = merge(PRIMARY, SECONDARY)
183
194
  ```
184
195
  RESULT = Task(
185
196
  subagent_type="rihal-code-reviewer",
186
- model="sonnet",
197
+ model="{lens_model}",
187
198
  prompt="Audit-only — do NOT optimize anything. {CONTEXT}
188
199
 
189
200
  Run Lens 2 (Performance) audit. Check:
@@ -209,7 +220,7 @@ FINDINGS[performance] = RESULT
209
220
  ```
210
221
  PRIMARY = Task(
211
222
  subagent_type="rihal-fatima",
212
- model="sonnet",
223
+ model="{lens_model}",
213
224
  prompt="Audit-only — do NOT write tests. {CONTEXT}
214
225
 
215
226
  Run Lens 3 (Testability) audit. Check:
@@ -227,7 +238,7 @@ PRIMARY = Task(
227
238
 
228
239
  SECONDARY = Task(
229
240
  subagent_type="rihal-edge-case-hunter",
230
- model="sonnet",
241
+ model="{lens_model}",
231
242
  prompt="Enumerate edge cases and boundary conditions. {CONTEXT}
232
243
 
233
244
  Find:
@@ -249,7 +260,7 @@ FINDINGS[testability] = merge(PRIMARY, SECONDARY)
249
260
  ```
250
261
  RESULT = Task(
251
262
  subagent_type="rihal-waleed",
252
- model="sonnet",
263
+ model="{lens_model}",
253
264
  prompt="Architecture audit — do NOT redesign anything. {CONTEXT}
254
265
 
255
266
  Run Lens 4 (Extensibility) audit. Check:
@@ -275,7 +286,7 @@ FINDINGS[extensibility] = RESULT
275
286
  ```
276
287
  RESULT = Task(
277
288
  subagent_type="rihal-code-reviewer",
278
- model="sonnet",
289
+ model="{lens_model}",
279
290
  prompt="Audit-only — do NOT install or update packages. {CONTEXT}
280
291
 
281
292
  Run Lens 5 (Dep Health) audit:
@@ -301,7 +312,7 @@ FINDINGS[dep-health] = RESULT
301
312
  ```
302
313
  RESULT = Task(
303
314
  subagent_type="rihal-debugger",
304
- model="sonnet",
315
+ model="{lens_model}",
305
316
  prompt="Error recovery audit — do NOT fix anything. {CONTEXT}
306
317
 
307
318
  Run Lens 6 (Error Recovery) audit. Find missing error handling:
@@ -328,7 +339,7 @@ FINDINGS[error-recovery] = RESULT
328
339
  ```
329
340
  RESULT = Task(
330
341
  subagent_type="rihal-deviation-analyzer",
331
- model="sonnet",
342
+ model="{lens_model}",
332
343
  prompt="State machine audit — do NOT modify state. {CONTEXT}
333
344
 
334
345
  Run Lens 7 (State Machine) audit. Check:
@@ -354,7 +365,7 @@ FINDINGS[state-machine] = RESULT
354
365
  ```
355
366
  RESULT = Task(
356
367
  subagent_type="rihal-i18n-auditor",
357
- model="sonnet",
368
+ model="{lens_model}",
358
369
  prompt="i18n audit — do NOT add translations. {CONTEXT}
359
370
 
360
371
  Run Lens 8 (i18n) audit. Check:
@@ -380,7 +391,7 @@ FINDINGS[i18n] = RESULT
380
391
  ```
381
392
  RESULT = Task(
382
393
  subagent_type="rihal-docs-auditor",
383
- model="sonnet",
394
+ model="{lens_model}",
384
395
  prompt="Documentation audit — do NOT write docs. {CONTEXT}
385
396
 
386
397
  Run Lens 9 (Documentation) audit. Check:
@@ -406,7 +417,7 @@ FINDINGS[documentation] = RESULT
406
417
  ```
407
418
  RESULT = Task(
408
419
  subagent_type="rihal-code-reviewer",
409
- model="sonnet",
420
+ model="{lens_model}",
410
421
  prompt="Cross-platform audit — do NOT fix scripts. {CONTEXT}
411
422
 
412
423
  Run Lens 10 (Cross-platform) audit. Check:
@@ -433,7 +444,7 @@ FINDINGS[cross-platform] = RESULT
433
444
  ```
434
445
  PRIMARY = Task(
435
446
  subagent_type="rihal-code-reviewer",
436
- model="sonnet",
447
+ model="{lens_model}",
437
448
  prompt="Karpathy 4-principle audit — do NOT fix code. {CONTEXT}
438
449
 
439
450
  Run Lens 11 (Karpathy) audit against recent changes (HEAD~20..HEAD):
@@ -442,6 +453,9 @@ PRIMARY = Task(
442
453
  Principle 2 (Simplicity First): dead code, unused imports, speculative abstractions
443
454
  Principle 3 (Surgical Changes): whitespace-only diffs, reformatting unrelated code
444
455
  Principle 4 (Goal-Driven Execution): TODOs, stubs, not-implemented errors, mock data
456
+ Design-token bypass (#660): raw hex/rgb/hsl/named colors in CSS outside :root
457
+ or @theme blocks. Two-stage check per @.rihal/references/design-tokens.md.
458
+ Honor .rihal/design-tokens-allowlist.txt waivers.
445
459
 
446
460
  Return: file:line — principle N violation — description [critical|warn|info]
447
461
  If clean: PASS"
@@ -449,7 +463,7 @@ PRIMARY = Task(
449
463
 
450
464
  SECONDARY = Task(
451
465
  subagent_type="rihal-hanzla",
452
- model="sonnet",
466
+ model="{lens_model}",
453
467
  prompt="Implementation quality audit — do NOT refactor. {CONTEXT}
454
468
 
455
469
  Review recent code (HEAD~10..HEAD) for:
@@ -457,6 +471,8 @@ SECONDARY = Task(
457
471
  - Code that could be 3 lines but is 30
458
472
  - Unclear variable/function names
459
473
  - Missing error messages that would help debug production failures
474
+ - Design-token bypass: raw hex in CSS classes when a semantic role exists
475
+ (per @.rihal/references/design-tokens.md)
460
476
 
461
477
  Return: file:line — description [warn|info]
462
478
  If clean: PASS"
@@ -472,7 +488,7 @@ FINDINGS[karpathy] = merge(PRIMARY, SECONDARY)
472
488
  ```
473
489
  RESULT = Task(
474
490
  subagent_type="rihal-layla",
475
- model="sonnet",
491
+ model="{lens_model}",
476
492
  prompt="UX flow audit — do NOT redesign flows. {CONTEXT}
477
493
 
478
494
  Run Lens 12 (SXO/UX) audit on rihal workflows. Check:
@@ -498,7 +514,7 @@ FINDINGS[sxo] = RESULT
498
514
  ```
499
515
  RESULT = Task(
500
516
  subagent_type="rihal-code-reviewer",
501
- model="sonnet",
517
+ model="{lens_model}",
502
518
  prompt="Observability audit — do NOT add instrumentation. {CONTEXT}
503
519
 
504
520
  Run Lens 13 (Observability) audit. Check:
@@ -525,7 +541,7 @@ FINDINGS[observability] = RESULT
525
541
  ```
526
542
  PRIMARY = Task(
527
543
  subagent_type="rihal-codebase-mapper",
528
- model="sonnet",
544
+ model="{lens_model}",
529
545
  prompt="Naming consistency audit — do NOT rename anything. {CONTEXT}
530
546
 
531
547
  Run Lens 14 (Naming) audit. Produce a CONVENTIONS scan:
@@ -541,7 +557,7 @@ PRIMARY = Task(
541
557
 
542
558
  SECONDARY = Task(
543
559
  subagent_type="rihal-code-reviewer",
544
- model="sonnet",
560
+ model="{lens_model}",
545
561
  prompt="Variable naming audit in recent code changes. {CONTEXT}
546
562
 
547
563
  Review HEAD~10..HEAD for:
@@ -563,7 +579,7 @@ FINDINGS[naming] = merge(PRIMARY, SECONDARY)
563
579
  ```
564
580
  PRIMARY = Task(
565
581
  subagent_type="rihal-nyquist-auditor",
566
- model="sonnet",
582
+ model="{lens_model}",
567
583
  prompt="Coverage audit — do NOT generate tests. {CONTEXT}
568
584
 
569
585
  Run Lens 15 (Coverage) audit. Fill Nyquist gaps:
@@ -579,7 +595,7 @@ PRIMARY = Task(
579
595
 
580
596
  SECONDARY = Task(
581
597
  subagent_type="rihal-fatima",
582
- model="sonnet",
598
+ model="{lens_model}",
583
599
  prompt="Release gate — coverage quality check. {CONTEXT}
584
600
 
585
601
  Review test strategy gaps: