@hanzlaa/rcode 4.8.0 → 4.9.1

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 (41) hide show
  1. package/AGENTS.md +1 -1
  2. package/CONTRIBUTING.md +3 -0
  3. package/README.md +7 -5
  4. package/cli/doctor.js +4 -2
  5. package/cli/github-sync.js +26 -17
  6. package/cli/install.js +59 -34
  7. package/cli/lib/namespace-migrate.cjs +62 -4
  8. package/cli/migrate-namespace.js +4 -0
  9. package/dist/rcode.js +207 -207
  10. package/package.json +1 -1
  11. package/rcode/agents/rcode-code-reviewer.md +1 -1
  12. package/rcode/agents/rcode-docs-auditor.md +1 -1
  13. package/rcode/agents/rcode-edge-case-hunter.md +1 -1
  14. package/rcode/agents/rcode-security-adversary.md +1 -1
  15. package/rcode/agents/rcode-security-auditor.md +1 -1
  16. package/rcode/agents/rcode-sprint-checker.md +1 -1
  17. package/rcode/agents/rcode-verifier.md +1 -1
  18. package/rcode/bin/lib/brain.cjs +16 -1
  19. package/rcode/bin/lib/gitignore.cjs +3 -5
  20. package/rcode/bin/rcode-hooks.cjs +22 -5
  21. package/rcode/bin/rcode-tools.cjs +166 -14
  22. package/rcode/data/intent-table.json +1 -1
  23. package/rcode/references/git-preflight.md +5 -2
  24. package/rcode/references/output-format.md +5 -5
  25. package/rcode/workflows/add-phase.md +33 -14
  26. package/rcode/workflows/do.md +33 -1
  27. package/rcode/workflows/execute-sprint.md +3 -4
  28. package/rcode/workflows/execute-waves.md +25 -32
  29. package/rcode/workflows/execute.md +80 -21
  30. package/rcode/workflows/init.md +28 -6
  31. package/rcode/workflows/plan-research-validation.md +10 -5
  32. package/rcode/workflows/plan-spawn-planner.md +9 -13
  33. package/rcode/workflows/plan.md +2 -2
  34. package/rcode/workflows/scaffold-skill.md +19 -1
  35. package/rcode/workflows/scan.md +22 -1
  36. package/rcode/workflows/secure-phase.md +7 -1
  37. package/rcode/workflows/validate-phase.md +7 -1
  38. package/server/lib/html/client/components/Sidebar.js +8 -5
  39. package/server/lib/html/client/views/PhasesView.js +1 -1
  40. package/server/lib/html/css.js +4 -0
  41. package/server/lib/scanner.js +4 -1
@@ -198,11 +198,10 @@ Deviations are normal — handle via rules below.
198
198
  - `type="auto"`: if `tdd="true"` → TDD execution. Implement with deviation rules + auth gates. Verify done criteria. Commit (see task_commit). Track hash for Summary.
199
199
  - `type="checkpoint:*"`: STOP → checkpoint_protocol → wait for user → continue only after confirmation.
200
200
  - **Task completion precedence (when signals conflict):**
201
- 1. `<verify><automated>` — machine-executable shell commands. **Highest authority.** If these pass, the task is done. If these fail, the task is NOT done — regardless of what `<acceptance_criteria>` says.
201
+ 1. `<verify><automated>` — machine-executable shell commands. **Highest authority.** If these pass, the task is done. If these fail, the task is NOT done — regardless of what `<done>` says.
202
202
  2. `<done>` — single observable sentence. Use as the human-readable confirmation once automated checks pass.
203
- 3. `<acceptance_criteria>` prose checklist. **Lowest authority.** Use as a guide during implementation, but automated results override prose judgments.
204
- - If `<verify><automated>` is absent: fall back to `<done>`, then `<acceptance_criteria>`.
205
- - **MANDATORY acceptance_criteria check:** After completing each task, if it has `<acceptance_criteria>`, verify EVERY criterion before moving to the next task. Use grep, file reads, or CLI commands to confirm each criterion. If any criterion fails, fix the implementation before proceeding. Do not skip criteria or mark them as "will verify later".
203
+ - If `<verify><automated>` is absent: fall back to `<done>` alone. `<evidence>` (grep hits, line ranges, or a creates-justification recorded by the planner per issue #649) is supporting grounding, not a completion signal to re-check here — the real plan schema (planner-playbook.md, sprint.md) has no such tag.
204
+ - **MANDATORY completion check:** After completing each task, confirm `<verify><automated>` passes (or, if absent, that the task's `<done>` sentence is observably true). Use grep, file reads, or CLI commands to confirm. If any check fails, fix the implementation before proceeding. Do not skip this or mark it as "will verify later".
206
205
  3. Run `<verification>` checks
207
206
  4. Confirm `<success_criteria>` met
208
207
  5. Document deviations in Summary
@@ -73,45 +73,38 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT
73
73
 
74
74
  **Classify plan and select subagent_type (BEFORE spawning, once per plan):**
75
75
 
76
- Reuse the `files_modified` list already parsed in step 1's intra-wave overlap check for this
77
- plan do not re-read the plan file to get it again.
76
+ This used to be prose pseudocode the orchestrating LLM was expected to hand-apply
77
+ (FRONTEND_GLOBS/BACKEND_GLOBS matching + keyword fallback). A live execution run showed
78
+ that computation was never actually carried out — a plan whose `files_modified` clearly
79
+ matched the backend glob rule (a path containing `db`) still fell back to `rcode-executor`.
80
+ Classification is now a deterministic CLI call — do not hand-compute it.
78
81
 
79
- ```
80
- FRONTEND_GLOBS = ["*.tsx", "*.jsx", "*.css"] + paths containing "client" or "ui"
81
- BACKEND_GLOBS = paths containing "api", "server", "db", or "service"
82
-
83
- touches_frontend = any(file matches FRONTEND_GLOBS for file in files_modified)
84
- touches_backend = any(file matches BACKEND_GLOBS for file in files_modified)
85
-
86
- if touches_frontend and touches_backend:
87
- classification = "full-stack"
88
- elif touches_frontend:
89
- classification = "frontend"
90
- elif touches_backend:
91
- classification = "backend"
92
- else:
93
- classification = "other" # files_modified empty/absent, or no glob matched
94
- ```
95
-
96
- **If `classification` is `"other"`** (ambiguous, or `files_modified` empty/absent), fall back
97
- to keyword-matching the plan's `<objective>` text before giving up:
98
- - Frontend keywords (React, component, UI, CSS, Tailwind, frontend, client-side, accessibility, a11y) → `classification = "frontend"`
99
- - Backend keywords (API, endpoint, database, schema, service, queue, backend, server-side) → `classification = "backend"`
100
- - Neither matches (pure docs/config/infra plan) → `classification` stays `"other"`
82
+ Call `classify-plan` with the phase and this plan's id (it reads `files_modified` and the
83
+ `<objective>` directly from the plan's SPRINT.md, so no need to re-parse step 1's overlap
84
+ data yourself):
101
85
 
102
- **Route to `subagent_type`:**
86
+ ```bash
87
+ CLASSIFY_JSON=$(node ".rcode/bin/rcode-tools.cjs" classify-plan "$PHASE" "$PLAN_ID" 2>/dev/null)
88
+ SUBAGENT_TYPE=$(echo "$CLASSIFY_JSON" | node -e "let d='';process.stdin.on('data',c=>d+=c).on('end',()=>{try{console.log(JSON.parse(d).subagent_type)}catch{console.log('rcode-executor')}})")
89
+ SUBAGENT_TYPE=${SUBAGENT_TYPE:-rcode-executor}
90
+ ```
103
91
 
104
- | classification | subagent_type |
105
- |---|---|
106
- | frontend | rcode-haitham |
107
- | backend | rcode-yousef |
108
- | full-stack | rcode-hanzla |
109
- | other | rcode-executor |
92
+ Use the literal `subagent_type` value returned — do not second-guess or override it.
110
93
 
111
94
  This decision is computed once per plan, before that plan's Task() spawn(s) below, and the
112
95
  resulting `subagent_type` value is used in the Task() call template (worktree and sequential
113
96
  modes both reuse this same value — see "Sequential mode" further below).
114
97
 
98
+ **Resolve executor model (once per wave, before spawning):**
99
+
100
+ `executor_model` from `init` is the raw `model_profile` string (e.g. `balanced`), not a
101
+ resolved model id — it must be passed through `resolve-model` first, the same way
102
+ `code_review_gate` in execute.md resolves `REVIEWER_MODEL` before its Task() spawn.
103
+ ```bash
104
+ EXECUTOR_MODEL=$(node ".rcode/bin/rcode-tools.cjs" resolve-model executor 2>/dev/null | node -e "let d='';process.stdin.on('data',c=>d+=c).on('end',()=>{try{console.log(JSON.parse(d).model)}catch{console.log('')}})" || echo "sonnet")
105
+ EXECUTOR_MODEL=${EXECUTOR_MODEL:-sonnet}
106
+ ```
107
+
115
108
  **Worktree mode** (`USE_WORKTREES` is not `false`):
116
109
 
117
110
  Before spawning, capture the current HEAD:
@@ -139,7 +132,7 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT
139
132
  Task(
140
133
  subagent_type="{subagent_type}",
141
134
  description="Execute plan {plan_number} of phase {phase_number}",
142
- model="{executor_model}",
135
+ model="${EXECUTOR_MODEL}",
143
136
  isolation="worktree",
144
137
  prompt="
145
138
  <objective>
@@ -35,11 +35,12 @@ route back to the user.
35
35
  4. **Branch check**: confirm current git branch is appropriate
36
36
  for the work. Two checks, both blocking:
37
37
 
38
- a. **Not on main/master without consent**: if `git branch --show-current`
39
- returns `main` or `master`, refuse to execute. Suggest:
40
- `git switch -c <phase>-<plan>-<slug>` (e.g. `git switch -c 8-1-aria`).
41
- User can override only by passing `--allow-main` to /rcode-execute and
42
- explicitly typing the override on this turn.
38
+ a. **Not on main/master without consent** (skip entirely when `git.branching_strategy`
39
+ config is `none` check via `node .rcode/bin/rcode-tools.cjs config-get
40
+ git.branching_strategy`): if `git branch --show-current` returns `main` or
41
+ `master`, refuse to execute. Suggest: `git switch -c <phase>-<plan>-<slug>`
42
+ (e.g. `git switch -c 8-1-aria`). User can override only by passing `--on-main`
43
+ to /rcode-execute and explicitly typing the override on this turn.
43
44
 
44
45
  b. **Working tree clean enough**: if `git status --porcelain` shows
45
46
  modified files unrelated to this phase's `files_modified` frontmatter,
@@ -49,8 +50,7 @@ route back to the user.
49
50
 
50
51
  The branch name should align with the phase/plan IDs from state — check
51
52
  `workflow.branch_pattern` config (default `<phase>-<plan>-<slug>`).
52
- 5. **Worktree config**: read `workflow.use_worktrees` — if true + parallelization
53
- is true + no file overlaps, plans in a wave run parallel via worktrees
53
+ 5. **Worktree config**: read `workflow.use_worktrees` — if true + no file overlaps, plans in a wave run parallel via worktrees. (`parallelization` is not a real field in `init execute`'s output — see the "initialize" step below; don't gate on it.)
54
54
  </pre_flight>
55
55
 
56
56
  <insight_block>
@@ -98,7 +98,41 @@ CONFIG_MODE=$(node .rcode/bin/rcode-tools.cjs config-get mode 2>/dev/null || ech
98
98
 
99
99
  **If `CONFIG_MODE == "yolo"` or `$ARGUMENTS` contains `--auto`:** Skip the menu. Auto-select **A) Autonomous run** and print one line: `▶ Auto-selecting Autonomous run (yolo mode). /rcode-settings set mode guided to change.`
100
100
 
101
- Otherwise, offer three modes via AskUserQuestion. Each option names the tradeoff explicitly:
101
+ **herdr availability check (cached).** Before offering options, determine whether a 4th
102
+ option (**D) herdr multi-agent orchestration**) should be shown. This uses the same
103
+ cached-boolean idiom as `workflow._auto_chain_active` above — read with a safe default,
104
+ write back once resolved, and skip the live check on subsequent runs:
105
+
106
+ ```bash
107
+ HERDR_NAMED_EXPLICITLY=$([[ "$ARGUMENTS" =~ herdr ]] && echo true || echo false)
108
+
109
+ HERDR_CHECKED=$(node .rcode/bin/rcode-tools.cjs config-get workflow._herdr_checked 2>/dev/null || echo "false")
110
+ HERDR_CHECKED=${HERDR_CHECKED:-false} # config-get exits 0 with empty output when key absent
111
+
112
+ if [[ "$HERDR_CHECKED" != "true" || "$HERDR_NAMED_EXPLICITLY" == "true" ]]; then
113
+ # Live re-check: first time ever, OR user named herdr explicitly this run
114
+ # (herdr may have been installed since the last negative cache hit).
115
+ if command -v herdr >/dev/null 2>&1; then
116
+ HERDR_AVAILABLE=true
117
+ else
118
+ HERDR_AVAILABLE=false
119
+ fi
120
+ node .rcode/bin/rcode-tools.cjs config-set workflow._herdr_available "$HERDR_AVAILABLE" 2>/dev/null
121
+ node .rcode/bin/rcode-tools.cjs config-set workflow._herdr_checked true 2>/dev/null
122
+ else
123
+ # Cached negative (or positive) result from a prior run — skip the check entirely.
124
+ HERDR_AVAILABLE=$(node .rcode/bin/rcode-tools.cjs config-get workflow._herdr_available 2>/dev/null || echo "false")
125
+ HERDR_AVAILABLE=${HERDR_AVAILABLE:-false}
126
+ fi
127
+ ```
128
+
129
+ Do **not** re-run `command -v herdr` on every invocation once `_herdr_checked` is `true` —
130
+ that defeats the point of caching. The only exception is `HERDR_NAMED_EXPLICITLY`: if the
131
+ user's request names herdr by name (e.g. "use herdr for this", "orchestrate via herdr"),
132
+ always re-check live regardless of the cached value, since herdr may have been installed
133
+ since the negative result was cached.
134
+
135
+ Otherwise, offer modes via AskUserQuestion. Each option names the tradeoff explicitly:
102
136
 
103
137
  **A) Autonomous run** — Spawn subagent per plan in sequence/parallel per
104
138
  wave rules. Checkpoints still pause for user. Fastest wall-clock.
@@ -112,9 +146,31 @@ Otherwise, offer three modes via AskUserQuestion. Each option names the tradeoff
112
146
  later waves in a separate session. Good for staged rollout / review
113
147
  gates.
114
148
 
149
+ **D) herdr multi-agent orchestration** (only shown if `HERDR_AVAILABLE == "true"`) —
150
+ Fan this phase's plans out to parallel `herdr` panes/tabs, each running its own
151
+ Claude agent in an isolated git worktree, then merge their work back. Be specific
152
+ about the tradeoff, not a one-liner: separate terminal panes you can watch
153
+ independently, genuinely parallel wall-clock (not wave-sequenced), noticeably
154
+ higher token cost than A/B/C since each pane runs a full agent session, and a
155
+ merge step at the end. Best fit is plans that are truly independent (no shared
156
+ `files_modified`, no cross-plan sequencing) — for plans with overlaps or a single
157
+ linear wave, herdr adds coordination overhead for no benefit; prefer A or C instead.
158
+ Selecting this option invokes the `rcode-herdr-orchestration` skill — it does not
159
+ replace this workflow's execution, it's a different way to run the same plans.
160
+
161
+ **Never auto-select D, even in yolo/`--auto` mode.** Orchestrating via herdr requires
162
+ explicit user confirmation on this turn — if `CONFIG_MODE == "yolo"` skipped the menu
163
+ above, herdr is simply not offered this run; the autonomous-run auto-selection must
164
+ never silently switch into herdr mode.
165
+
115
166
  Include a recommendation line: "My recommendation: {letter} because {reason
116
167
  in one clause}." Then ask which option to proceed with — do NOT silently
117
- pick one.
168
+ pick one. If the user selects D, confirm once more in plain language what
169
+ will happen (parallel panes, worktrees, higher cost) before invoking
170
+ `Skill(skill="rcode-herdr-orchestration", ...)` — do not invoke it on the same
171
+ turn as the AskUserQuestion answer without that confirmation being part of
172
+ the answer itself (i.e. selecting the option IS the confirmation only if its
173
+ label made the tradeoff explicit, which it does above).
118
174
  </three_options>
119
175
 
120
176
  <output_format>
@@ -238,14 +294,16 @@ If `INIT` is empty or `INIT.ok` is false, print error and exit:
238
294
  Error: rcode-tools init failed. Verify .rcode/ is installed and state.json is valid.
239
295
  ```
240
296
 
241
- Parse JSON for: `executor_model`, `verifier_model`, `commit_docs`, `parallelization`, `branching_strategy`, `branch_name`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `plans`, `incomplete_plans`, `plan_count`, `incomplete_count`, `state_exists`, `roadmap_exists`, `phase_req_ids`, `response_language`.
297
+ Parse JSON for these real, top-level fields: `executor_model`, `verifier_model`, `phase_dir`, `plans`, `state_exists`, `response_language`. Fields commonly assumed to exist but that are NOT top-level (verified live this session against `init execute`'s real output, `cmdInitExecute` in rcode-tools.cjs): `branching_strategy` is nested under `config.branching_strategy`; `commit_docs` doesn't exist (closest real value is `config.commit_planning`, a `"true"`/`"false"` string); `parallelization` has no source anywhere (not top-level, not under `config`, not in `phase-plan-index`'s output either); `branch_name` isn't returned (the `handle_branching` step below now computes it from config instead); `phase_name` isn't derivable either (`phase_dir`'s basename is a slug, not the human-readable name — read ROADMAP.md if a step needs it); `incomplete_plans`/`incomplete_count` don't exist (`plans[]` items only carry `{path, depends_on, wave, plan}`, no completion field); `roadmap_exists`/`phase_req_ids` are returned only by the separate `init sprint-plan` command, not `init execute`.
298
+ Derivable, not literal: `phase_found` as `phase_dir !== null`; `phase_number` as the `target` field (the raw phase argument as passed, e.g. `"45"`); `phase_slug` from `phase_dir`'s basename (the part after the first `-`); `plan_count` as `plans.length`. Downstream `${PHASE_NUMBER}`/`${PLAN_COUNT}` references later in this workflow (snapshot tag, review prompts, `phase complete`, etc.) resolve from `target`/`plans.length` per these derivations; `${PHASE_NAME}` and `${INCOMPLETE_COUNT}` have no source here — read `PHASE_NAME` from ROADMAP.md if a later step needs it, and treat `INCOMPLETE_COUNT` as unknown until `phase-plan-index` runs in `discover_and_group_plans` (which does return a real per-plan `has_summary` completion signal).
242
299
 
243
300
  **If `response_language` is set:** Include `response_language: {value}` in all spawned subagent prompts so any user-facing output stays in the configured language.
244
301
 
245
302
  Read worktree config:
246
303
 
247
304
  ```bash
248
- USE_WORKTREES=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.use_worktrees 2>/dev/null || echo "true")
305
+ USE_WORKTREES=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.use_worktrees 2>/dev/null)
306
+ USE_WORKTREES=${USE_WORKTREES:-true} # config-get exits 0 with empty output when key absent; || fallback won't fire
249
307
  ```
250
308
 
251
309
  When `USE_WORKTREES` is `false`, all executor agents run without `isolation="worktree"` — they execute sequentially on the main working tree instead of in parallel worktrees.
@@ -265,16 +323,17 @@ When `CONTEXT_WINDOW >= 500000` (1M-class models), subagent prompts include rich
265
323
  - Verifier agents receive all SPRINT.md, SUMMARY.md, CONTEXT.md files plus REQUIREMENTS.md
266
324
  - This enables cross-phase awareness and history-aware verification
267
325
 
268
- **If `phase_found` is false:** Error — phase directory not found. Run `/rcode-status` to inspect state or `/rcode-plan {N}` to create the phase.
269
- **If `plan_count` is 0:** Error — no plans found in phase. Run `/rcode-plan {N}` to generate plans or `/rcode-help` for the command surface.
326
+ **If `phase_dir` is `null` (derived `phase_found` false):** Error — phase directory not found. Run `/rcode-status` to inspect state or `/rcode-plan {N}` to create the phase.
327
+ **If `plans.length` is 0 (derived `plan_count` 0):** Error — no plans found in phase. Run `/rcode-plan {N}` to generate plans or `/rcode-help` for the command surface.
270
328
  **If `state_exists` is false but `.planning/` exists:** Offer reconstruct or continue.
271
329
 
272
- When `parallelization` is false, plans within a wave execute sequentially.
330
+ `parallelization` is not a real field in `init execute`'s output (see the "initialize" step's field notes above) — this line currently documents behavior with no data source; don't treat it as a working toggle until a real source is wired in.
273
331
 
274
332
  **Runtime detection for Copilot:**
275
333
  Check if the current runtime is Copilot by testing for the `@rcode-executor` agent pattern
276
334
  or absence of the `Task()` subagent API. If running under Copilot, force sequential inline
277
- execution regardless of the `parallelization` settingCopilot's subagent completion
335
+ execution unconditionally (there is no real `parallelization` toggle to override see the
336
+ "initialize" step's field notes above) — Copilot's subagent completion
278
337
  signals are unreliable (see `<runtime_compatibility>`). Set `COPILOT_SEQUENTIAL=true`
279
338
  internally and skip the `execute_waves` step in favor of `check_interactive_mode`'s
280
339
  inline path for each plan.
@@ -352,7 +411,7 @@ Check `branching_strategy` from init:
352
411
 
353
412
  **"none":** Skip, continue on current branch.
354
413
 
355
- **"phase" or "milestone":** Use pre-computed `branch_name` from init:
414
+ **"phase" or "milestone":** `init execute` does not return `branch_name` (see the "initialize" step's field notes above) — compute `BRANCH_NAME` from `workflow.branch_pattern` config (default `<phase>-<plan>-<slug>`) before running:
356
415
  ```bash
357
416
  git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME"
358
417
  ```
@@ -361,7 +420,7 @@ All subsequent commits go to this branch. User handles merging.
361
420
  </step>
362
421
 
363
422
  <step name="validate_phase">
364
- From init JSON: `phase_dir`, `plan_count`, `incomplete_count`.
423
+ From init JSON: `phase_dir` (real); `plan_count` derives as `plans.length`; `incomplete_count` has no source at this point (see the "initialize" step's field notes) — treat as unknown until `phase-plan-index` runs.
365
424
 
366
425
  Report: "Found {plan_count} plans in {phase_dir} ({incomplete_count} incomplete)"
367
426
 
@@ -527,7 +586,7 @@ Selected wave finished successfully. This phase still has incomplete plans, so p
527
586
  <step name="run_verify_commands">
528
587
  **Run per-task `<verify>` shell commands from all completed SPRINT.md plans.**
529
588
 
530
- After all executor agents finish, extract and run any `<verify>` blocks defined in plan tasks. These are the machine-executable counterpart to `<acceptance_criteria>` prose.
589
+ After all executor agents finish, extract and run any `<verify>` blocks defined in plan tasks. These are the machine-executable proof that a task's `<done>` criteria are met — the plan schema has no such tag; `<verify><automated>` plus `<evidence>` grounding are what the planner and executor actually emit and enforce.
531
590
 
532
591
  ```bash
533
592
  # Extract all <verify> blocks from all SPRINT.md files for this phase
@@ -745,10 +804,10 @@ fi
745
804
  ```
746
805
  ⚠ Phase {X} EXECUTED but not yet verified.
747
806
 
748
- The following acceptance criteria require human verification before
807
+ The following task completion criteria require human verification before
749
808
  the phase can advance to `status: complete`:
750
809
 
751
- {list AC items from SPRINT.md}
810
+ {list each task's <done> sentence from SPRINT.md}
752
811
 
753
812
  Recommended next steps:
754
813
  /rcode-add-tests {X} — generate unit + E2E tests before UAT
@@ -761,7 +820,7 @@ fi
761
820
  **If `VERIFICATION_STATUS` is `fail`:**
762
821
 
763
822
  1. Mark the phase as `status: executed` (so /rcode-plan --gaps can run a closure cycle).
764
- 2. Surface the failed AC items.
823
+ 2. Surface the tasks whose `<done>` criteria failed human verification.
765
824
  3. STOP. Don't mark complete on a failing verification.
766
825
 
767
826
  **Only when `VERIFICATION_STATUS` is `pass`** — proceed to `update_roadmap` below.
@@ -42,6 +42,12 @@ Run detection in parallel:
42
42
  test -f .rcode/config.yaml && echo "rcode-configured: yes" || echo "rcode-configured: no"
43
43
  test -f .rcode/state.json && echo "state-present: yes" || echo "state-present: no"
44
44
  test -f .rcode/JOURNEY.md && echo "rihla-present: yes" || echo "rihla-present: no"
45
+ # #1028: config.yaml is written by the installer itself, so it exists on the
46
+ # very first /rcode-init run after a fresh install — not just on a genuine
47
+ # "returning" session. state.json's installer-seeded _seeded_stub marker
48
+ # (cleared once a real init/new-project run completes) is what actually
49
+ # distinguishes "normal first brownfield run" from "prior init was interrupted".
50
+ grep -q '"_seeded_stub"[[:space:]]*:[[:space:]]*true' .rcode/state.json 2>/dev/null && echo "seeded-stub: yes" || echo "seeded-stub: no"
45
51
 
46
52
  # Project presence
47
53
  # Use git rev-parse instead of test -d .git — in git worktrees .git is a FILE not a dir
@@ -83,12 +89,20 @@ If `state === "returning"` and `--reset` not passed:
83
89
  Or run with --reset to reconfigure.
84
90
  ```
85
91
 
86
- - If `rihla-present: no` — JOURNEY.md is missing from a partial prior init. Do NOT stop. Print a recovery notice and continue to Steps 4 and 4b to write the missing baseline:
92
+ - If `rihla-present: no` and `seeded-stub: yes` this is the normal, expected first `/rcode-init` run on a brownfield project: `config.yaml` was seeded by the installer, but no real init has happened yet. Nothing was broken; do NOT use "recovery" language. Print a plain first-run notice and continue to Steps 4 and 4b to write the baseline:
93
+
94
+ ```
95
+ ✓ rcode is configured. Writing the JOURNEY.md baseline now...
96
+ ```
97
+
98
+ Skip Steps 2 and 3 (config already exists). Jump directly to Step 4.
99
+
100
+ - If `rihla-present: no` and `seeded-stub: no` — a genuine prior init/new-project run completed (the stub marker was cleared) but JOURNEY.md is missing, meaning that prior run was interrupted before writing it. This is an actual recovery case. Print a recovery notice and continue to Steps 4 and 4b to write the missing baseline:
87
101
 
88
102
  ```
89
103
  ✓ rcode is already configured here.
90
104
 
91
- JOURNEY.md baseline is missing — completing the scan step now...
105
+ JOURNEY.md baseline is missing from a prior interrupted init — completing it now...
92
106
  ```
93
107
 
94
108
  Skip Steps 2 and 3 (config already exists). Jump directly to Step 4.
@@ -279,10 +293,10 @@ if [ -s .rcode/context/active.md ] && ! grep -q "Run \`/rcode" .rcode/context/ac
279
293
  fi
280
294
  ```
281
295
 
282
- After writing both files, refresh the memory bank fingerprint so staleness checks see the project as fresh:
296
+ After writing both files, refresh the memory bank fingerprint so staleness checks see the project as fresh. `context refresh` reads `.rcode/sources.yaml`, but nothing in rcode ever creates that file (the installer only scaffolds the unrelated `.rcode/brain/sources.yaml`, used by `brain pull`) — so on every fresh project this call is a guaranteed no-op. Skip it when the file is known not to exist rather than spending a call to learn that (#1018):
283
297
 
284
298
  ```bash
285
- node .rcode/bin/rcode-tools.cjs context refresh >/dev/null 2>&1 || true
299
+ test -f .rcode/sources.yaml && node .rcode/bin/rcode-tools.cjs context refresh >/dev/null 2>&1 || true
286
300
  ```
287
301
 
288
302
  ## Step 4c — Scaffold CLAUDE.md / AGENTS.md if missing
@@ -290,12 +304,12 @@ node .rcode/bin/rcode-tools.cjs context refresh >/dev/null 2>&1 || true
290
304
  `generate-claude-md` (the command routing rule + project rules block every agent needs at session start) previously only ran via the `/rcode-new-project` roadmap flow — a project set up with `/rcode-init` alone (the common "add rcode to an existing codebase" path) never got it, so agents had no ambient instruction to check `do.md` before acting ad-hoc. Close that gap here, unconditionally (not just on `fresh`):
291
305
 
292
306
  ```bash
293
- if [ ! -f CLAUDE.md ]; then
307
+ if [ ! -f CLAUDE.md ] || [ ! -f AGENTS.md ]; then
294
308
  node .rcode/bin/rcode-tools.cjs generate-claude-md
295
309
  fi
296
310
  ```
297
311
 
298
- Never pass `--force` here — an existing `CLAUDE.md` is the user's own file (or was already generated by a prior init/new-project run) and must not be overwritten. Silent no-op when `CLAUDE.md` already exists.
312
+ Never pass `--force` here — an existing `CLAUDE.md` or `AGENTS.md` is the user's own file (or was already generated by a prior init/new-project run) and must not be overwritten. `cmdGenerateClaudeMd` gates each file's write on that file's own existence, so this backfills whichever one is missing (#1025). Silent no-op when both files already exist.
299
313
 
300
314
  ## Step 5 — Suggest the next step
301
315
 
@@ -327,6 +341,14 @@ Or strategic question about the codebase:
327
341
  /rcode-council {your question}
328
342
  ```
329
343
 
344
+ **In all three cases, append this tip:**
345
+ ```
346
+ Tip: run /rcode-enable-hooks to turn on a one-line project status primer at
347
+ the start of every session, plus 9 other opt-in guardrails (read-before-edit
348
+ checks, dangerous-command blocking, auto-formatting). Off by default so a
349
+ fresh install never surprises you.
350
+ ```
351
+
330
352
  **If `returning` with `--reset`:**
331
353
  ```
332
354
  ✓ rcode reconfigured. Prior state preserved in state.json.
@@ -157,9 +157,12 @@ test -f "${PHASE_DIR}/${PADDED_PHASE}-VALIDATION.md" && echo "VALIDATION_CREATED
157
157
  > Skip if `workflow.security_enforcement` is explicitly `false`. Absent = enabled.
158
158
 
159
159
  ```bash
160
- SECURITY_CFG=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.security_enforcement --raw 2>/dev/null || echo "true")
161
- SECURITY_ASVS=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.security_asvs_level --raw 2>/dev/null || echo "1")
162
- SECURITY_BLOCK=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.security_block_on --raw 2>/dev/null || echo "high")
160
+ SECURITY_CFG=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.security_enforcement --raw 2>/dev/null)
161
+ SECURITY_CFG=${SECURITY_CFG:-true} # config-get exits 0 with empty output when key absent; || fallback won't fire
162
+ SECURITY_ASVS=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.security_asvs_level --raw 2>/dev/null)
163
+ SECURITY_ASVS=${SECURITY_ASVS:-1} # config-get exits 0 with empty output when key absent; || fallback won't fire
164
+ SECURITY_BLOCK=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.security_block_on --raw 2>/dev/null)
165
+ SECURITY_BLOCK=${SECURITY_BLOCK:-high} # config-get exits 0 with empty output when key absent; || fallback won't fire
163
166
  ```
164
167
 
165
168
  **If `SECURITY_CFG` is `false`:** Skip to step 5.6.
@@ -183,8 +186,10 @@ Continue to step 5.6. Security config is passed to the planner in step 8.
183
186
  > Skip if `workflow.ui_phase` is explicitly `false` AND `workflow.ui_safety_gate` is explicitly `false` in `.rcode/config.yaml`. If keys are absent, treat as enabled.
184
187
 
185
188
  ```bash
186
- UI_PHASE_CFG=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.ui_phase 2>/dev/null || echo "true")
187
- UI_GATE_CFG=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.ui_safety_gate 2>/dev/null || echo "true")
189
+ UI_PHASE_CFG=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.ui_phase 2>/dev/null)
190
+ UI_PHASE_CFG=${UI_PHASE_CFG:-true} # config-get exits 0 with empty output when key absent; || fallback won't fire
191
+ UI_GATE_CFG=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.ui_safety_gate 2>/dev/null)
192
+ UI_GATE_CFG=${UI_GATE_CFG:-true} # config-get exits 0 with empty output when key absent; || fallback won't fire
188
193
  ```
189
194
 
190
195
  **If both are `false`:** Skip to step 6.
@@ -114,7 +114,7 @@ ${AGENT_SKILLS_PLANNER}
114
114
  <downstream_consumer>
115
115
  Output consumed by /rcode-execute. Plans need:
116
116
  - Frontmatter (wave, depends_on, autonomous, **files_modified** — aggregated list of all file paths from `<files>` blocks across every task; used by executor for intra-wave parallel-safety overlap detection)
117
- - Tasks in XML format with read_first, files, acceptance_criteria, verify (with `<automated>` child), and done fields (MANDATORY on every task)
117
+ - Tasks in XML format with read_first, files, evidence, verify (with `<automated>` child), and done fields (MANDATORY on every task)
118
118
  - Verification criteria
119
119
  - must_haves for goal-backward verification
120
120
  - **`## Files Touched`** section (see below) — required on every SPRINT.md
@@ -257,17 +257,13 @@ Every task MUST include these fields — they are NOT optional:
257
257
  - Executor checkpoint (knows what to stage after each task)
258
258
  - Example: `src/auth/auth.service.ts`, `tests/auth/auth.service.test.ts`
259
259
 
260
- 3. **`<acceptance_criteria>`** — Verifiable conditions that prove the task was done correctly. Rules:
261
- - Every criterion must be checkable with grep, file read, test command, or CLI output
262
- - NEVER use subjective language ("looks correct", "properly configured", "consistent with")
263
- - ALWAYS include exact strings, patterns, values, or command outputs that must be present
264
- - Examples:
265
- - Code: `auth.py contains def verify_token(` / `test_auth.py exits 0`
266
- - Config: `.env.example contains DATABASE_URL=` / `Dockerfile contains HEALTHCHECK`
267
- - Docs: `README.md contains '## Installation'` / `API.md lists all endpoints`
268
- - Infra: `deploy.yml has rollback step` / `docker-compose.yml has healthcheck for db`
269
-
270
- 4. **`<verify>`** — Shell commands that PROVE the acceptance criteria are met. Run by executor after task completes and by verifier during post-execution check. The block MUST contain an `<automated>` child with the exact commands to run (Dimension 8 hard-blocks without it). Rules:
260
+ 3. **`<evidence>`** — REQUIRED (issue #649). Must show codebase grounding proving the task is real, not theoretical. At minimum one of:
261
+ - `grep:` a literal grep/Glob pattern + count of matches that justified this task (e.g. `` `rg '\.alert' apps/web/src` → 13 hits across 9 files ``)
262
+ - `lines:` exact `path:line-line` ranges of code being modified
263
+ - `creates:` the file paths being created from scratch (with one-line justification why no existing file fits)
264
+ A task without `<evidence>` is theoretical and MUST NOT be written. (Matches `rcode/references/planner-playbook.md`'s "Task Anatomy" section — single source of truth for this rule.)
265
+
266
+ 4. **`<verify>`** — Shell commands that PROVE the `<done>` criteria are met. Run by executor after task completes and by verifier during post-execution check. The block MUST contain an `<automated>` child with the exact commands to run (Dimension 8 hard-blocks without it). Rules:
271
267
  - `<automated>` commands must exit 0 on success, non-zero on failure
272
268
  - Prefer `grep -q` for presence checks, `test -f` for file existence, project test runner for behavior
273
269
  - Keep commands short and composable — one check per line
@@ -325,7 +321,7 @@ Every task MUST include these fields — they are NOT optional:
325
321
  - [ ] Tasks are specific and actionable
326
322
  - [ ] Every task has `<read_first>` with at least the file being modified
327
323
  - [ ] Every task has `<files>` listing exact files this task will modify or create
328
- - [ ] Every task has `<acceptance_criteria>` with grep-verifiable conditions
324
+ - [ ] Every task has `<evidence>` with grep/lines/creates codebase grounding per issue #649 — not a prose checklist tag (none exists in the real plan schema)
329
325
  - [ ] Every task has `<verify>` with an `<automated>` child containing at least one shell command (Dimension 8 blocker)
330
326
  - [ ] Every task has `<done>` with a single observable acceptance sentence (Dimension 2 requirement)
331
327
  - [ ] Every `<action>` contains concrete values (no "align X with Y" without specifying what)
@@ -49,8 +49,8 @@ Read all files referenced by the invoking prompt's execution_context before star
49
49
  ${PHASE_GOAL_HAS_UI ? '@.rcode/references/ui-brand.md' : ''}
50
50
  @.rcode/references/karpathy-guidelines.md
51
51
  <!-- Read .rcode/references/agent-contracts.md only if defining or debugging agent contracts -->
52
- <!-- Read .rcode/references/gates.md only if implementing or troubleshooting gate logic -->
53
- @.rcode/references/thinking-models-planning.md
52
+ <!-- Read .rcode/references/gates.md only if implementing or troubleshooting gate logic; thinking-models-planning.md (127 lines) only if features.thinking_partner is enabled -->
53
+ ${THINKING_PARTNER_ENABLED === 'true' ? '@.rcode/references/thinking-models-planning.md' : ''}
54
54
  </required_reading>
55
55
 
56
56
  <available_agent_types>
@@ -117,6 +117,21 @@ if [ -n "$fails" ]; then
117
117
  fi
118
118
  ```
119
119
 
120
+ ## Step 3.5 — Prove It Moved the Needle
121
+
122
+ Passing the 5-component check in Step 3 only proves the file has the right shape — a triggers block, an Overview, a Workflow, an Output Format, and Examples. It says nothing about whether loading the skill actually changes what an agent does. A skill can be structurally perfect and still be a no-op in practice: the model reads it, nods along, and then falls back to its untrained default the moment a real scenario hits.
123
+
124
+ Do not mark a new or edited skill as ready to ship until you've run this gate:
125
+
126
+ 1. **Write the pressure scenario.** Take the exact situation the skill's triggers describe — the moment the skill is supposed to change the agent's behavior — and phrase it as a concrete task an agent could be handed cold, with no mention of the skill.
127
+ 2. **Run the control.** Spawn a fresh subagent with no memory of this conversation and the skill NOT loaded (do not mention it, do not point at its path). Give it the pressure scenario. Record what it does by default — this is almost always the suboptimal or wrong behavior the skill exists to correct.
128
+ 3. **Run the treatment.** Spawn a second fresh subagent, same scenario, this time with the skill loaded (either by having it available for the model to invoke, or by including its content directly in the prompt). Record what it does.
129
+ 4. **Compare.** The treatment run must diverge from the control run in the specific direction the skill claims to produce. "Both agents produced fine-looking output" is not a pass — the question is whether the skill was the reason for the difference. If the two runs land on the same behavior, the skill has no measurable effect yet, no matter how clean its markdown is.
130
+
131
+ If the comparison shows no behavioral difference, the skill is not done. Go back and sharpen the Workflow section, the triggers, or the examples — whichever part failed to actually steer the model — and rerun this gate. Do not ship on structural compliance alone.
132
+
133
+ Skip this gate only when scaffolding a brand-new, still-empty skill (Step 2's placeholders haven't been filled in yet — there's no behavior to test). Once real content replaces the placeholders, this gate becomes mandatory before the skill is considered ready.
134
+
120
135
  ## Step 4 — Confirm and Next Up
121
136
 
122
137
  Print:
@@ -127,13 +142,16 @@ Print:
127
142
 
128
143
  All 5 required components are present (triggers, Overview, Workflow,
129
144
  Output Format, Examples). The placeholders need real content before
130
- the skill becomes useful.
145
+ the skill becomes useful — and once they're filled in, run Step 3.5
146
+ (Prove It Moved the Needle) before calling the skill done.
131
147
 
132
148
  ▶ Next Up
133
149
  $EDITOR rcode/skills/actions/<group>/rcode-<NAME>/SKILL.md
134
150
  # fill in placeholders
135
151
  npx @hanzlaa/rcode install --force # install to .claude/skills/
136
152
  node --test test/compliance.test.cjs # verify compliance
153
+ # then: control vs. treatment
154
+ # subagent run (Step 3.5)
137
155
  ```
138
156
 
139
157
  ## Next Up
@@ -14,10 +14,12 @@ Valid rcode subagent types (use exact names — do not fall back to 'general-pur
14
14
 
15
15
  ## Step 0 — Usage check
16
16
 
17
- If `$ARGUMENTS` is empty or contains only `--help` or `-h`:
17
+ If `$ARGUMENTS` contains `--help` or `-h`:
18
18
  - Print the usage block below
19
19
  - STOP — do not proceed
20
20
 
21
+ If `$ARGUMENTS` is empty, do NOT stop — proceed to Step 1, which defaults focus to `tech+arch`.
22
+
21
23
  **Usage:**
22
24
  ```
23
25
  /rcode-scan [--focus tech|arch|quality|concerns|tech+arch] [--refresh] [--reset]
@@ -339,6 +341,25 @@ This file tracks structural changes between scans. Each entry is auto-written by
339
341
 
340
342
  This file is **read by future `/rcode-scan --refresh` runs** as additional anchor context — the memory bank is self-improving across scans.
341
343
 
344
+ ## Step 6.6: Propagate scan findings into PROJECT.md / STATE.md stubs
345
+
346
+ After the RETURNED banner, check whether `.planning/PROJECT.md` and `.planning/STATE.md` are still install stubs (contain the `<!-- INSTALL STUB` banner). If real, non-stub content already exists, skip this step entirely — never overwrite user-authored planning docs.
347
+
348
+ ```bash
349
+ grep -q '<!-- INSTALL STUB' .planning/PROJECT.md 2>/dev/null && echo "PROJECT_IS_STUB"
350
+ grep -q '<!-- INSTALL STUB' .planning/STATE.md 2>/dev/null && echo "STATE_IS_STUB"
351
+ ```
352
+
353
+ If `PROJECT.md` is a stub: replace its `**One-line:**` sentence with a brief, grounded one-line summary derived from the scan output just written (e.g. from STACK.md/ARCHITECTURE.md's opening lines — language, framework, primary purpose). Keep the rest of the stub structure (Vision/Stack sections) intact; do not attempt a full rewrite. Use Read then Edit — never blind-overwrite.
354
+
355
+ If `STATE.md` is a stub: update `**Current phase:**` line to read:
356
+ ```
357
+ **Current phase:** none — existing project scanned, see .planning/codebase/ — no phases planned yet
358
+ ```
359
+ and add one line under `## Next Action` noting the scan already ran: `Codebase scanned via /rcode-scan — run /rcode-new-project or /rcode-add-phase to plan phases.`
360
+
361
+ This is a minimal, targeted edit — not a full PROJECT.md content-generation system. The goal is closing the "user opens STATE.md and sees zero acknowledgment a scan already ran" gap.
362
+
342
363
  ## Step 7: Final cue (orchestrator-level, after RETURNED banner)
343
364
 
344
365
  The RETURNED banner above is Dalil's voice. After it, the orchestrator may add ONE neutral cue line if the user might want a deeper scan:
@@ -3,7 +3,8 @@ Verify threat mitigations for a completed phase. Confirm SPRINT.md threat regist
3
3
  </purpose>
4
4
 
5
5
  <required_reading>
6
- @.rcode/references/ui-brand.md
6
+ <!-- ui-brand.md (254 lines): only load when the phase goal/CONTEXT.md contains UI signals — mirrors plan.md:49's PHASE_GOAL_HAS_UI pattern -->
7
+ ${PHASE_GOAL_HAS_UI ? '@.rcode/references/ui-brand.md' : ''}
7
8
  </required_reading>
8
9
 
9
10
  <available_agent_types>
@@ -50,6 +51,11 @@ Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`,
50
51
  ```bash
51
52
  AUDITOR_MODEL=$(node ".rcode/bin/rcode-tools.cjs" resolve-model rcode-security-auditor --raw)
52
53
  SECURITY_CFG=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.security_enforcement --raw 2>/dev/null || echo "true")
54
+
55
+ # Detect UI signals in phase goal + CONTEXT.md to decide whether to load ui-brand.md (254 lines)
56
+ PHASE_GOAL_HAS_UI=$(grep -iEl "frontend|ui|component|design|style|brand" \
57
+ .planning/phases/*${phase_number}*/*-CONTEXT.md \
58
+ .planning/ROADMAP.md 2>/dev/null | head -1)
53
59
  ```
54
60
 
55
61
  If `SECURITY_CFG` is `false`: exit with "Security enforcement disabled. Enable via /rcode-settings."
@@ -3,7 +3,8 @@ Audit Nyquist validation gaps for a completed phase. Generate missing tests. Upd
3
3
  </purpose>
4
4
 
5
5
  <required_reading>
6
- @.rcode/references/ui-brand.md
6
+ <!-- ui-brand.md (254 lines): only load when the phase goal/CONTEXT.md contains UI signals — mirrors plan.md:49's PHASE_GOAL_HAS_UI pattern -->
7
+ ${PHASE_GOAL_HAS_UI ? '@.rcode/references/ui-brand.md' : ''}
7
8
  @.rcode/references/karpathy-guidelines.md
8
9
  </required_reading>
9
10
 
@@ -32,6 +33,11 @@ Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`.
32
33
  ```bash
33
34
  AUDITOR_MODEL=$(node ".rcode/bin/rcode-tools.cjs" resolve-model rcode-nyquist-auditor --raw)
34
35
  NYQUIST_CFG=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.nyquist_validation --raw)
36
+
37
+ # Detect UI signals in phase goal + CONTEXT.md to decide whether to load ui-brand.md (254 lines)
38
+ PHASE_GOAL_HAS_UI=$(grep -iEl "frontend|ui|component|design|style|brand" \
39
+ .planning/phases/*${phase_number}*/*-CONTEXT.md \
40
+ .planning/ROADMAP.md 2>/dev/null | head -1)
35
41
  ```
36
42
 
37
43
  If `NYQUIST_CFG` is `false`: exit with "Nyquist validation is disabled. Enable via /rcode-settings."
@@ -63,11 +63,14 @@ export function Sidebar({ activeView, projectName }) {
63
63
  const name = project.name || projectName || 'No project';
64
64
  const user = (project.user && project.user.name) ? project.user : null;
65
65
 
66
- // Full store subscription for live health badge counts.
66
+ // Full store subscription for live health badge counts. Derived from
67
+ // activeSessions (the /api/sessions poll) — same source of truth as the
68
+ // OrchPanel footer and NotifyCenter — not the static store.blockers list
69
+ // (PROJECT.md-backed, unrelated to live orchestrator state; see #965).
67
70
  // Re-renders on every setState (sessions poll every 4 s, state refresh every 30 s).
68
- const { activeSessions, blockers } = useStore();
69
- const sessionCount = (activeSessions || []).filter(s => s.status === 'running').length;
70
- const blockerCount = (blockers || []).length;
71
+ const { activeSessions } = useStore();
72
+ const sessionCount = (activeSessions || []).filter(s => s.status === 'running' && !s.waiting).length;
73
+ const blockerCount = (activeSessions || []).filter(s => s.status === 'blocked' || s.waiting).length;
71
74
 
72
75
  return html`
73
76
  <aside class="sidebar" id="sidebar">
@@ -93,7 +96,7 @@ export function Sidebar({ activeView, projectName }) {
93
96
  </span>
94
97
  <span
95
98
  class=${'health-badge' + (blockerCount > 0 ? ' health-badge--alert' : ' health-badge--zero')}
96
- title=${blockerCount + ' blocker' + (blockerCount === 1 ? '' : 's')}
99
+ title=${blockerCount + ' blocked orchestration session' + (blockerCount === 1 ? '' : 's')}
97
100
  >
98
101
  <${Icon} name="alert-triangle" size=${12} />
99
102
  ${blockerCount} blocked