specrails-core 4.12.0 → 5.0.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 (76) hide show
  1. package/README.md +49 -78
  2. package/bin/specrails-core.mjs +18 -98
  3. package/bin/tui-installer.mjs +22 -105
  4. package/commands/doctor.md +1 -1
  5. package/dist/installer/cli.js +12 -2
  6. package/dist/installer/cli.js.map +1 -1
  7. package/dist/installer/commands/doctor.js +3 -5
  8. package/dist/installer/commands/doctor.js.map +1 -1
  9. package/dist/installer/commands/init.js +23 -19
  10. package/dist/installer/commands/init.js.map +1 -1
  11. package/dist/installer/commands/update.js +17 -16
  12. package/dist/installer/commands/update.js.map +1 -1
  13. package/dist/installer/commands/v5-migration.js +119 -0
  14. package/dist/installer/commands/v5-migration.js.map +1 -0
  15. package/dist/installer/phases/install-config.js +3 -6
  16. package/dist/installer/phases/install-config.js.map +1 -1
  17. package/dist/installer/phases/manifest.js +2 -6
  18. package/dist/installer/phases/manifest.js.map +1 -1
  19. package/dist/installer/phases/prereqs.js +0 -1
  20. package/dist/installer/phases/prereqs.js.map +1 -1
  21. package/dist/installer/phases/scaffold.js +38 -148
  22. package/dist/installer/phases/scaffold.js.map +1 -1
  23. package/package.json +1 -1
  24. package/schemas/profile.v1.json +1 -1
  25. package/templates/agents/sr-architect.md +30 -0
  26. package/templates/agents/sr-developer.md +21 -8
  27. package/templates/agents/sr-reviewer.md +44 -31
  28. package/templates/codex-skills/batch-implement/SKILL.md +9 -32
  29. package/templates/codex-skills/implement/SKILL.md +61 -143
  30. package/templates/codex-skills/rails/sr-architect/SKILL.md +38 -20
  31. package/templates/codex-skills/rails/sr-developer/SKILL.md +29 -10
  32. package/templates/codex-skills/rails/sr-reviewer/SKILL.md +21 -10
  33. package/templates/commands/specrails/doctor.md +1 -1
  34. package/templates/commands/specrails/implement.md +117 -288
  35. package/templates/commands/specrails/memory-inspect.md +6 -4
  36. package/templates/commands/specrails/propose-spec.md +1 -1
  37. package/templates/commands/specrails/refactor-recommender.md +8 -51
  38. package/templates/commands/specrails/retry.md +12 -48
  39. package/templates/commands/specrails/telemetry.md +1 -1
  40. package/templates/gemini-commands/implement.toml +9 -0
  41. package/templates/profiles/default.json +5 -18
  42. package/commands/enrich.md +0 -1456
  43. package/templates/agents/sr-backend-developer.md +0 -91
  44. package/templates/agents/sr-backend-reviewer.md +0 -152
  45. package/templates/agents/sr-doc-sync.md +0 -247
  46. package/templates/agents/sr-frontend-developer.md +0 -85
  47. package/templates/agents/sr-frontend-reviewer.md +0 -145
  48. package/templates/agents/sr-merge-resolver.md +0 -195
  49. package/templates/agents/sr-performance-reviewer.md +0 -186
  50. package/templates/agents/sr-product-analyst.md +0 -36
  51. package/templates/agents/sr-product-manager.md +0 -148
  52. package/templates/agents/sr-security-reviewer.md +0 -191
  53. package/templates/agents/sr-test-writer.md +0 -176
  54. package/templates/codex-skills/enrich/SKILL.md +0 -191
  55. package/templates/codex-skills/merge-resolve/SKILL.md +0 -88
  56. package/templates/codex-skills/rails/sr-backend-developer/SKILL.md +0 -93
  57. package/templates/codex-skills/rails/sr-backend-reviewer/SKILL.md +0 -120
  58. package/templates/codex-skills/rails/sr-doc-sync/SKILL.md +0 -124
  59. package/templates/codex-skills/rails/sr-frontend-developer/SKILL.md +0 -106
  60. package/templates/codex-skills/rails/sr-frontend-reviewer/SKILL.md +0 -111
  61. package/templates/codex-skills/rails/sr-merge-resolver/SKILL.md +0 -156
  62. package/templates/codex-skills/rails/sr-performance-reviewer/SKILL.md +0 -109
  63. package/templates/codex-skills/rails/sr-product-analyst/SKILL.md +0 -85
  64. package/templates/codex-skills/rails/sr-product-manager/SKILL.md +0 -131
  65. package/templates/codex-skills/rails/sr-security-reviewer/SKILL.md +0 -121
  66. package/templates/codex-skills/rails/sr-test-writer/SKILL.md +0 -115
  67. package/templates/commands/specrails/auto-propose-backlog-specs.md +0 -312
  68. package/templates/commands/specrails/enrich.md +0 -1456
  69. package/templates/commands/specrails/get-backlog-specs.md +0 -226
  70. package/templates/commands/specrails/merge-resolve.md +0 -172
  71. package/templates/commands/specrails/reconfig.md +0 -80
  72. package/templates/commands/specrails/vpc-drift.md +0 -405
  73. package/templates/commands/test.md +0 -58
  74. package/templates/personas/persona.md +0 -43
  75. package/templates/personas/the-maintainer.md +0 -98
  76. package/templates/settings/perf-thresholds.yml +0 -25
@@ -66,28 +66,14 @@ which openspec && openspec --version
66
66
 
67
67
  #### 5. Agent discovery
68
68
 
69
- Agent discovery runs in one of two modes: **profile mode** (a profile JSON is active) or **legacy mode** (no profileidentical to pre-4.1.0 behavior).
69
+ `AVAILABLE_AGENTS` resolves through a single path: **a profile if one is active, otherwise the baseline trio**. There are no modesthe baseline is just the default value the resolution falls back to when no profile is present.
70
70
 
71
- ##### Profile detection
71
+ ##### Resolve the profile path
72
72
 
73
- A profile is active when either condition holds:
73
+ A profile is active when either condition holds (highest precedence first):
74
74
 
75
- 1. The environment variable `SPECRAILS_PROFILE_PATH` is set AND points to a readable file. Tools like `specrails-desktop` set this to a job-scoped snapshot.
76
- 2. The file `.specrails/profiles/project-default.json` exists and is readable.
77
-
78
- If condition 1 holds, use `$SPECRAILS_PROFILE_PATH` as the profile path. Otherwise, if condition 2 holds, use `.specrails/profiles/project-default.json`. Otherwise, fall through to **legacy mode**.
79
-
80
- ##### Preflight: `jq` availability (profile mode only)
81
-
82
- When running in profile mode, `jq` is required to read the profile JSON. Run:
83
-
84
- ```bash
85
- command -v jq >/dev/null 2>&1 || { echo "[error] 'jq' is required for profile-aware mode. Install with: brew install jq / apt install jq / https://stedolan.github.io/jq/"; exit 1; }
86
- ```
87
-
88
- ##### Profile mode — load, validate, populate
89
-
90
- Resolve the profile path into `PROFILE_PATH` (highest precedence wins), then read it:
75
+ 1. `SPECRAILS_PROFILE_PATH` is set AND points to a readable file. Tools like `specrails-desktop` set this to a job-scoped snapshot.
76
+ 2. `.specrails/profiles/project-default.json` exists and is readable.
91
77
 
92
78
  ```bash
93
79
  if [[ -n "${SPECRAILS_PROFILE_PATH:-}" && -r "${SPECRAILS_PROFILE_PATH:-}" ]]; then
@@ -95,13 +81,27 @@ if [[ -n "${SPECRAILS_PROFILE_PATH:-}" && -r "${SPECRAILS_PROFILE_PATH:-}" ]]; t
95
81
  elif [[ -r ".specrails/profiles/project-default.json" ]]; then
96
82
  PROFILE_PATH=".specrails/profiles/project-default.json"
97
83
  else
98
- PROFILE_MODE="legacy"
84
+ PROFILE_PATH=""
85
+ fi
86
+ ```
87
+
88
+ ##### No profile → baseline default
89
+
90
+ When `PROFILE_PATH` is empty, `AVAILABLE_AGENTS` is the baseline trio and there are no per-agent model overrides (each agent uses the `model:` in its own `.md` frontmatter). No profile file is written — the baseline is an in-memory default, honoring the reserved-paths contract (`.specrails/profiles/**` is never created by the pipeline):
91
+
92
+ ```bash
93
+ if [[ -z "$PROFILE_PATH" ]]; then
94
+ AVAILABLE_AGENTS="$(printf '%s\n' sr-architect sr-developer sr-reviewer)"
95
+ PROFILE_NAME=""
99
96
  fi
100
97
  ```
101
98
 
102
- When `PROFILE_PATH` is set (profile mode), read the profile:
99
+ ##### Profile present load, validate, populate
100
+
101
+ `jq` is required to read a profile JSON:
103
102
 
104
103
  ```bash
104
+ command -v jq >/dev/null 2>&1 || { echo "[error] 'jq' is required to read a profile. Install with: brew install jq / apt install jq / https://stedolan.github.io/jq/"; exit 1; }
105
105
  PROFILE="$(cat "$PROFILE_PATH")"
106
106
  ```
107
107
 
@@ -149,14 +149,20 @@ if [[ "$IS_LAST" != "true" ]]; then
149
149
  fi
150
150
  ```
151
151
 
152
- Populate `AVAILABLE_AGENTS` from the profile and verify each referenced agent file exists on disk:
152
+ Populate `AVAILABLE_AGENTS` from the profile. The three baseline agents are **hard-required**: if a baseline agent's file is missing, STOP. A **non-baseline** agent whose file is missing is **warned and skipped** — this is how a pre-v5 profile that still references a removed agent (e.g. `sr-frontend-developer`) degrades gracefully:
153
153
 
154
154
  ```bash
155
- AVAILABLE_AGENTS="$(jq -r '.agents[].id' <<<"$PROFILE" | sort)"
156
- for id in $AVAILABLE_AGENTS; do
157
- [[ -f ".claude/agents/$id.md" ]] \
158
- || { echo "[error] profile references agent '$id' but .claude/agents/$id.md does not exist"; exit 1; }
155
+ AVAILABLE_AGENTS=""
156
+ for id in $(jq -r '.agents[].id' <<<"$PROFILE" | sort); do
157
+ if [[ -f ".claude/agents/$id.md" ]]; then
158
+ AVAILABLE_AGENTS="$AVAILABLE_AGENTS$id"$'\n'
159
+ elif [[ "$id" == "sr-architect" || "$id" == "sr-developer" || "$id" == "sr-reviewer" ]]; then
160
+ echo "[error] Core agent $id not found. Run npx specrails-core update to reinstall."; exit 1
161
+ else
162
+ echo "[warn] profile references agent '$id' but no agent file exists — skipping (removed in v5; use a custom-* agent)"
163
+ fi
159
164
  done
165
+ AVAILABLE_AGENTS="$(printf '%s' "$AVAILABLE_AGENTS" | sed '/^$/d')"
160
166
  ```
161
167
 
162
168
  Also store per-agent model overrides and the orchestrator model for use in later phases:
@@ -176,13 +182,12 @@ done < <(jq -r '.agents[] | [.id, (.model // "null")] | @tsv' <<<"$PROFILE")
176
182
  # Routing rules (array), consumed by Phase 3b.
177
183
  ROUTING="$(jq '.routing' <<<"$PROFILE")"
178
184
 
179
- PROFILE_MODE="profile"
180
185
  PROFILE_NAME="$(jq -r '.name' <<<"$PROFILE")"
181
186
  ```
182
187
 
183
- ##### Apply per-agent model overrides (profile mode only)
188
+ ##### Apply per-agent model overrides (only when a profile declares them)
184
189
 
185
- Claude Code's Agent tool determines a subagent's model from the `model:` line in the agent's `.md` frontmatter at invocation time — there is no per-call model parameter. When a profile is active, rewrite each agent's frontmatter `model:` value in-place to match `AGENT_MODEL[$id]`.
190
+ Claude Code's Agent tool determines a subagent's model from the `model:` line in the agent's `.md` frontmatter at invocation time — there is no per-call model parameter. When a profile declares model overrides, rewrite each agent's frontmatter `model:` value in-place to match `AGENT_MODEL[$id]`.
186
191
 
187
192
  This rewrite is safe because:
188
193
  - Multi-feature runs execute in **isolated git worktrees** (`isolation: worktree`), so each rail mutates its own copy of `.claude/agents/` without cross-rail contention.
@@ -195,7 +200,7 @@ for id in "${!AGENT_MODEL[@]}"; do
195
200
  file=".claude/agents/$id.md"
196
201
  [[ -f "$file" ]] || continue
197
202
  # Rewrite the first `model:` line within the frontmatter block (lines between the
198
- # first two `---` separators). Use sed with portable syntax (macOS + Linux).
203
+ # first two `---` separators). Use awk with portable syntax (macOS + Linux).
199
204
  awk -v new="$model" '
200
205
  BEGIN { in_fm=0; done=0 }
201
206
  /^---$/ { in_fm = !in_fm; print; next }
@@ -207,42 +212,21 @@ done
207
212
 
208
213
  If a profile does not declare `model` for a given agent (the field is optional), that agent's frontmatter is left untouched.
209
214
 
210
- ##### Legacy mode — preserve current behavior
211
-
212
- If no profile is active, scan the agents directory exactly as before:
213
-
214
- ```bash
215
- AVAILABLE_AGENTS="$(ls .claude/agents/sr-*.md 2>/dev/null | sed 's|.*/||;s|\.md$||' | sort)"
216
- PROFILE_MODE="legacy"
217
- PROFILE_NAME=""
218
- ```
219
-
220
- Per-agent model overrides are empty in legacy mode — subagent invocations inherit the `model:` value from each agent's `.md` frontmatter. Routing in Phase 3b uses the hardcoded legacy rules.
221
-
222
- ##### Agent roles (both modes)
215
+ ##### Agent roles
223
216
 
224
- The pipeline adapts dynamically to the installed agents:
217
+ The pipeline ships exactly three first-party agents. Any additional agent comes from an active profile that declares a user-owned `custom-*` agent (with routing) — the installer never ships or manages non-core agents.
225
218
 
226
219
  | Agent | Role | Required? | Phase(s) affected |
227
220
  |-------|------|-----------|-------------------|
228
221
  | sr-architect | Architecture & design | **Core** (always present) | 3a |
229
222
  | sr-developer | Full-stack implementation | **Core** (always present) | 3b |
230
- | sr-reviewer | Generalist quality gate | **Core** (always present) | 4b |
231
- | sr-merge-resolver | Merge conflict resolution | Optional — required for multi-feature merge conflict resolution | 4a |
232
- | sr-product-manager | Product exploration | Optional | 1 |
233
- | sr-test-writer | Test generation | Optional | 3c |
234
- | sr-doc-sync | Documentation sync | Optional | 3d |
235
- | sr-frontend-developer | Frontend implementation | Optional | 3b (routing) |
236
- | sr-backend-developer | Backend implementation | Optional | 3b (routing) |
237
- | sr-frontend-reviewer | Frontend review | Optional | 4b |
238
- | sr-backend-reviewer | Backend review | Optional | 4b |
239
- | sr-security-reviewer | Security analysis | Optional | 4b |
240
- | sr-performance-reviewer | Performance analysis | Optional | 4b |
223
+ | sr-reviewer | Quality gate (correctness, tests, security, performance) | **Core** (always present) | 4b |
224
+ | custom-* | Profile-declared specialist | Optional — only when an active profile lists it | per profile routing |
241
225
 
242
226
  **Gate rules** (applied throughout the pipeline):
243
- - If an optional agent is NOT in `AVAILABLE_AGENTS`, **skip** that phase/sub-step silently and note `"<agent> not installed skipping"`.
244
- - Core agents are guaranteed to exist. If a core agent is missing, **STOP** and print: `[error] Core agent <name> not found. Run /specrails:enrich or reinstall.`
245
- - In **profile mode**, the profile's `agents[]` IS the source of truth for `AVAILABLE_AGENTS`. Agents not listed are considered unavailable regardless of what is on disk.
227
+ - The three baseline agents are the source of truth for the non-profile default; a profile's `agents[]` (baseline + any `custom-*`) is the source of truth when a profile is active. Agents not in `AVAILABLE_AGENTS` are unavailable regardless of what is on disk.
228
+ - If a `custom-*` agent routed by the profile is not in `AVAILABLE_AGENTS` (e.g. skipped above), that routing target is dropped the task falls through to the profile's `default: true` rule.
229
+ - If a core agent is missing, **STOP** and print: `[error] Core agent <name> not found. Run npx specrails-core update to reinstall.`
246
230
 
247
231
  ### Summary
248
232
 
@@ -256,7 +240,7 @@ Print a setup report:
256
240
  | OpenSpec | ok | ... |
257
241
  | Dependencies | ok | ... |
258
242
  | Test runner | ok | ... |
259
- | Agents | N installed | core: 3/3, optional: M |
243
+ | Agents | N available | baseline: 3/3, custom (profile): M |
260
244
  ```
261
245
 
262
246
  **Pass `TEST_CMD`, `BACKLOG_AVAILABLE`, and `AVAILABLE_AGENTS` forward** — all later phases must use these.
@@ -287,6 +271,7 @@ Before parsing input, scan `$ARGUMENTS` for control flags:
287
271
  - If `--confidence-override "<reason>"` is present in `$ARGUMENTS`:
288
272
  - Set `CONFIDENCE_OVERRIDE_REASON=<reason>` (the quoted string immediately following `--confidence-override`)
289
273
  - Strip `--confidence-override` and the reason before further parsing.
274
+ - The one flag bypasses BOTH confidence gates: the architect's design-confidence gate (Phase 3a.3) and the reviewer's score gate (Phase 4b-conf).
290
275
 
291
276
  If none of these flags is present: `DRY_RUN=false`, `APPLY_MODE=false`, `CONFIDENCE_OVERRIDE_REASON=""`. Pipeline runs as normal.
292
277
 
@@ -301,13 +286,13 @@ Initialize conflict-tracking variables:
301
286
  **If the user passed a text description** (e.g. `"add feature X"`):
302
287
  - **Single-feature mode**. Derive a kebab-case change name.
303
288
  - Set `SINGLE_MODE = true`. No worktrees, no parallelism.
304
- - **Skip Phase 1 and Phase 2** — go directly to Phase 3a.
289
+ - Go directly to Phase 3a.
305
290
 
306
291
  **If the user passed ticket references** (e.g. `#85, #71` or `#1, #2`):
307
292
  - Fetch each ticket from `.specrails/local-tickets.json` at `tickets["{id}"]`.
308
293
  - Extract area, value, effort, and feature details from each ticket body.
309
294
  - If only 1 ticket: set `SINGLE_MODE = true`.
310
- - **Skip Phase 1 and Phase 2** — go directly to confirmation table.
295
+ - Go directly to the confirmation table.
311
296
 
312
297
  #### Phase 0 snapshot capture
313
298
 
@@ -392,8 +377,6 @@ Write the initial state file:
392
377
  "phases": {
393
378
  "architect": "pending",
394
379
  "developer": "pending",
395
- "test-writer": "<'pending' if sr-test-writer ∈ AVAILABLE_AGENTS, else 'skipped'>",
396
- "doc-sync": "<'pending' if sr-doc-sync ∈ AVAILABLE_AGENTS, else 'skipped'>",
397
380
  "reviewer": "pending",
398
381
  "ship": "pending",
399
382
  "ci": "pending"
@@ -430,27 +413,15 @@ When a phase completes or fails, update `PIPELINE_STATE_PATH`:
430
413
 
431
414
  If `PIPELINE_STATE_AVAILABLE=false`: skip all state updates silently.
432
415
 
433
- **If the user passed area names**:
416
+ **If the user passed area names** (no concrete issue/spec):
434
417
  - Check for open backlog issues. If found, filter and pick top 3.
435
- - If none, proceed to Phase 1.
418
+ - If none, STOP with: `[input] No backlog issue or spec resolved from the given area(s). Product exploration was removed in v5 — pass a backlog issue number or a feature description/spec.`
436
419
 
437
420
  ---
438
421
 
439
- ## Phase 1: Explore (parallel)
422
+ ## Phase 1 & 2: (removed in v5)
440
423
 
441
- **Only runs if Phase 0 found no backlog issues AND user passed area names AND `sr-product-manager` `AVAILABLE_AGENTS`.**
442
-
443
- If `sr-product-manager` is not installed, skip Phase 1 and Phase 2 entirely. Print: `[phase-1] sr-product-manager not installed — skipping exploration. Proceeding with provided input.`
444
-
445
- For each area, launch a **sr-product-manager** agent (`subagent_type: sr-product-manager`, `run_in_background: true`).
446
-
447
- Wait for all to complete. Read their output.
448
-
449
- ## Phase 2: Select
450
-
451
- **Only runs if Phase 1 ran.**
452
-
453
- Pick the single idea with the best impact/effort ratio from each exploration. Present to user and wait for confirmation.
424
+ Product exploration and idea selection were driven by `sr-product-manager`, which is not shipped in v5. The pipeline works from concrete inputs — backlog issues or a feature description/spec passed to the command — and proceeds directly from Phase 0 to Phase 3a. If you want product discovery, run it as a separate step and feed the resulting spec/issue into `/specrails:implement`.
454
425
 
455
426
  ## Phase 3a.0: Pre-architect conflict check
456
427
 
@@ -575,6 +546,42 @@ Quick-check each architect's artifacts:
575
546
 
576
547
  **Pipeline state:** update `architect` → `done`. If any architect agent failed (skipped area): update `architect` → `failed` with error context `"sr-architect failed for: <area-names>"`.
577
548
 
549
+ ### 3a.3 Design confidence gate
550
+
551
+ Implementation is the expensive phase — it only runs on designs the architect actually trusts. For each feature, read:
552
+
553
+ ```
554
+ ${SPECRAILS_REPO_DIR:-.}/openspec/changes/<name>/design-confidence.json
555
+ ```
556
+
557
+ - **File missing** (pre-gate architect output): print `[design-confidence] Warning: design-confidence.json not found for <name>. Proceeding without gate.` and continue — backward compatible.
558
+ - **`confidence: "high"` or `"medium"`**: print `[design-confidence] <name>: <level>. Proceeding.` and continue to Phase 3b.
559
+ - **`confidence: "low"`**:
560
+ - If `CONFIDENCE_OVERRIDE_REASON` is non-empty: print `[design-confidence] Override accepted for <name>. Reason: <CONFIDENCE_OVERRIDE_REASON>. Proceeding.` and continue.
561
+ - Otherwise **HALT this feature before any implementation cost is paid.** Print:
562
+
563
+ ```
564
+ ## Design Confidence Gate: BLOCKED — <name>
565
+
566
+ The architect could not form a confident design.
567
+
568
+ **Reason:** <reason>
569
+
570
+ **Blocking question:** <blocking_question>
571
+
572
+ ### Next Steps
573
+ 1. Answer the question above (edit the ticket description or refine the spec), then re-run `/specrails:implement`.
574
+ 2. Or, to accept the risk and implement the architect's best interpretation:
575
+ `/specrails:implement #N --confidence-override "reason"`
576
+
577
+ No implementation, git, or backlog operations were performed for this feature.
578
+ ```
579
+
580
+ - **Pipeline state:** `developer` → `skipped`, `failed_phase` = `architect`, `error_context` = `"design confidence low: <blocking_question>"`.
581
+ - In multi-feature mode, a `low` on one feature halts only that feature — the others continue normally. Halted features appear in the Phase 4e report with Status `BLOCKED (confidence)`.
582
+
583
+ The OpenSpec artifacts the architect produced are intentionally left in place — the answered question turns them into a resumable starting point, not waste.
584
+
578
585
  ## Phase 3b: Implement
579
586
 
580
587
  ### Pre-flight: Verify Bash permission
@@ -585,6 +592,8 @@ Before launching any developer agent, run a trivial Bash command to confirm Bash
585
592
 
586
593
  **Read reviewer learnings:** Check `.claude/agent-memory/sr-reviewer/common-fixes.md` and include in developer prompts.
587
594
 
595
+ **Test-execution economy:** every developer prompt MUST include this reminder: *"Per-task test runs are SCOPED to the test file(s) the task touches. The full suite runs exactly once, at your validation gate. The reviewer owns the pipeline's authoritative full CI run."*
596
+
588
597
  #### Dry-Run: Redirect developer writes
589
598
 
590
599
  **If `DRY_RUN=true`**, include the following in every developer agent prompt:
@@ -619,11 +628,11 @@ Produce three sets: `FRONTEND_TASKS`, `BACKEND_TASKS`, `OTHER_TASKS`.
619
628
 
620
629
  **Step 2 — Route tasks to developer agents:**
621
630
 
622
- Routing operates in one of two modes depending on the value of `PROFILE_MODE` set in Phase -1.
631
+ Routing has a single path. When **no profile** is active, every task goes to `sr-developer` `DEVELOPER_ROUTING = { sr-developer: <all tasks> }`. When a **profile** is active, apply its `ROUTING` rules (below), which may direct some tasks to profile-declared `custom-*` agents.
623
632
 
624
- ##### Profile mode (`PROFILE_MODE=profile`)
633
+ ##### Profile routing
625
634
 
626
- When a profile is active, apply `ROUTING` rules in their array order. For each task, collect its tag set (layer tags plus any explicit `[tag]` markers in tasks.md). The first rule whose `tags` array intersects the task's tag set wins. The terminal `default: true` rule catches tasks matched by no earlier rule.
635
+ Apply `ROUTING` rules in their array order. For each task, collect its tag set (the layer tags from Step 1 plus any explicit `[tag]` markers in tasks.md). The first rule whose `tags` array intersects the task's tag set wins. The terminal `default: true` rule catches tasks matched by no earlier rule.
627
636
 
628
637
  Example (pseudocode):
629
638
 
@@ -655,29 +664,14 @@ assigned_agent_for_task() {
655
664
  }
656
665
  ```
657
666
 
658
- Produce `DEVELOPER_ROUTING` from the per-task decisions, grouping by assigned agent. An agent assigned by the profile SHALL only be used if it also appears in `AVAILABLE_AGENTS` (the profile's own `agents[]`). If the profile routes a task to an agent not present in `agents[]`, that is a profile configuration bug **STOP** and print: `[error] profile routing references agent '<id>' which is not declared in profile.agents[]`.
659
-
660
- ##### Legacy mode (`PROFILE_MODE=legacy`)
661
-
662
- When no profile is active, evaluate available developer agents in `AVAILABLE_AGENTS` and apply these hardcoded rules in priority order:
667
+ Produce `DEVELOPER_ROUTING` from the per-task decisions, grouping by assigned agent. If a rule routes a task to an agent that is **not** in `AVAILABLE_AGENTS` (e.g. a `custom-*` agent that was warned-and-skipped in Phase -1 because its file is missing), that routing target is dropped and the task falls through to the terminal `default: true` rule (which resolves to a baseline agent). Do not STOP for a missing non-baseline target — graceful degradation is intentional.
663
668
 
664
- | Condition | Agent(s) selected | Mode |
665
- |-----------|-------------------|------|
666
- | ALL tasks are frontend-only AND `sr-frontend-developer` ∈ `AVAILABLE_AGENTS` | **sr-frontend-developer** | Single agent for all tasks |
667
- | ALL tasks are backend-only AND `sr-backend-developer` ∈ `AVAILABLE_AGENTS` | **sr-backend-developer** | Single agent for all tasks |
668
- | Mix of frontend + backend tasks AND both layer-specific developers available | **sr-frontend-developer** for `FRONTEND_TASKS`, **sr-backend-developer** for `BACKEND_TASKS`, **sr-developer** for `OTHER_TASKS` | Parallel agents per layer |
669
- | Frontend tasks exist AND `sr-frontend-developer` available, but no backend-specific developer | **sr-frontend-developer** for `FRONTEND_TASKS`, **sr-developer** for remaining | Parallel agents |
670
- | Backend tasks exist AND `sr-backend-developer` available, but no frontend-specific developer | **sr-backend-developer** for `BACKEND_TASKS`, **sr-developer** for remaining | Parallel agents |
671
- | No layer-specific developers available (fallback) | **sr-developer** | Single agent for all tasks |
672
-
673
- Store the result as `DEVELOPER_ROUTING`: a map of `{agent_id: [task_list]}`.
674
-
675
- ##### Routing trace (both modes)
669
+ ##### Routing trace
676
670
 
677
671
  After computing `DEVELOPER_ROUTING`, optionally emit a trace line to aid debugging:
678
672
 
679
673
  ```
680
- [phase-3b] routing decision: mode=$PROFILE_MODE profile=${PROFILE_NAME:-none} agents=[list]
674
+ [phase-3b] routing decision: profile=${PROFILE_NAME:-none} agents=[list]
681
675
  ```
682
676
 
683
677
  **Step 3 — Print routing decision:**
@@ -687,16 +681,15 @@ After computing `DEVELOPER_ROUTING`, optionally emit a trace line to aid debuggi
687
681
 
688
682
  | Agent | Tasks | Reason |
689
683
  |-------|-------|--------|
690
- | sr-frontend-developer | Task 1, Task 3 | Frontend-only tasks (React components) |
691
- | sr-backend-developer | Task 2, Task 4 | Backend-only tasks (API endpoints) |
692
- | sr-developer | Task 5 | Mixed layer (config + infra) |
684
+ | sr-developer | Task 1, Task 2 | Default (no profile / default rule) |
685
+ | custom-api-dev | Task 3 | Profile routed [backend] tasks here |
693
686
  ```
694
687
 
695
- Also store `DEVELOPER_AGENTS_USED` (the set of developer agent IDs actually launched) — Phase 4b will use this for reviewer selection.
688
+ Also store `DEVELOPER_AGENTS_USED` (the set of developer agent IDs actually launched) — reported in the Phase 4e summary.
696
689
 
697
690
  #### Launch modes
698
691
 
699
- For each entry in `DEVELOPER_ROUTING`, launch the assigned developer agent using its `subagent_type` (`sr-developer`, `sr-frontend-developer`, or `sr-backend-developer`) with its task subset.
692
+ For each entry in `DEVELOPER_ROUTING`, launch the assigned developer agent using its `subagent_type` (`sr-developer` or a profile-declared `custom-*` developer) with its task subset.
700
693
 
701
694
  **If `SINGLE_MODE` and only one agent in routing**: Launch in the main repo, foreground.
702
695
  **If `SINGLE_MODE` but multiple agents in routing**: Launch agents sequentially in the main repo (one at a time, foreground), passing only their assigned tasks.
@@ -717,84 +710,14 @@ Only after the LAST background agent sends its completion notification, emit the
717
710
  | <feature-a> | sr-developer | 64 | 8m 02s |
718
711
  | <feature-b> | sr-developer | 50 | 7m 35s |
719
712
 
720
- All N developers complete. Proceeding to Phase 3c.
713
+ All N developers complete. Proceeding to Phase 4.
721
714
  ```
722
715
 
723
716
  This prevents stale "still waiting" text from appearing as the terminal result when the job completes.
724
717
 
725
718
  **Pipeline state:** update `developer` → `done`. Also update `implemented_files` in the state file with the complete list of files created or modified by the developer agent(s). If developer failed: update `developer` → `failed` with error context `"<agent-id> failed: <exit code or error description>"`.
726
719
 
727
- ## Phase 3c: Write Tests
728
-
729
- **Guard:** If `sr-test-writer` ∉ `AVAILABLE_AGENTS`, skip this phase. Print: `[phase-3c] sr-test-writer not installed — skipping test generation.` Update pipeline state: `test-writer` → `skipped`. Proceed to Phase 3d.
730
-
731
- Launch a **sr-test-writer** agent (`subagent_type: sr-test-writer`) for each feature immediately after its developer completes.
732
-
733
- Construct the agent invocation prompt to include:
734
- - **IMPLEMENTED_FILES_LIST**: the complete list of files the developer created or modified for this feature
735
- - **TASK_DESCRIPTION**: the original task or feature description that drove the implementation
736
-
737
- ### Launch modes
738
-
739
- **If `SINGLE_MODE`**: Launch a single sr-test-writer agent in the foreground (`run_in_background: false`). Wait for it to complete before proceeding to Phase 4.
740
-
741
- **If multiple features (worktrees)**: Launch one sr-test-writer agent per feature, each in its corresponding worktree (`isolation: worktree`, `run_in_background: true`). Wait for all sr-test-writer agents to complete before proceeding to Phase 4.
742
-
743
- ### Dry-run behavior
744
-
745
- **If `DRY_RUN=true`**, include in every test-writer agent prompt:
746
-
747
- > IMPORTANT: This is a dry-run. Write all new or modified test files under:
748
- > .claude/.dry-run/\<feature-name\>/
749
- >
750
- > Mirror the real destination path within this directory. After writing each file, append an entry
751
- > to .claude/.dry-run/\<feature-name\>/.cache-manifest.json using:
752
- > {"cached_path": "...", "real_path": "...", "operation": "create"}
753
-
754
- ### Failure handling
755
-
756
- If a test-writer agent fails or times out:
757
- - Record `Tests: FAILED` for that feature in the Phase 4e report
758
- - Continue to Phase 4 — the sr-test-writer failure is non-blocking
759
- - Include in the reviewer agent prompt: "Note: the sr-test-writer failed for this feature. Check for coverage gaps."
760
-
761
- **Pipeline state:** update `test-writer` → `done` (or `failed` with error context `"sr-test-writer timed out or errored"`). Failure does not block the pipeline.
762
-
763
- ## Phase 3d: Doc Sync
764
-
765
- **Guard:** If `sr-doc-sync` ∉ `AVAILABLE_AGENTS`, skip this phase. Print: `[phase-3d] sr-doc-sync not installed — skipping doc sync.` Update pipeline state: `doc-sync` → `skipped`. Proceed to Phase 4.
766
-
767
- Launch a **sr-doc-sync** agent (`subagent_type: sr-doc-sync`) for each feature after its tests are written.
768
-
769
- Construct the agent invocation prompt to include:
770
- - **IMPLEMENTED_FILES_LIST**: the complete list of files the developer created or modified for this feature
771
- - **TASK_DESCRIPTION**: the original task or feature description that drove the implementation
772
-
773
- ### Launch modes
774
-
775
- **If `SINGLE_MODE`**: Launch a single sr-doc-sync agent in the foreground (`run_in_background: false`). Wait for it to complete before proceeding to Phase 4.
776
-
777
- **If multiple features (worktrees)**: Launch one sr-doc-sync agent per feature, each in its corresponding worktree (`isolation: worktree`, `run_in_background: true`). Wait for all sr-doc-sync agents to complete before proceeding to Phase 4.
778
-
779
- ### Dry-run behavior
780
-
781
- **If `DRY_RUN=true`**, include in every doc-sync agent prompt:
782
-
783
- > IMPORTANT: This is a dry-run. Write all new or modified doc files under:
784
- > .claude/.dry-run/\<feature-name\>/
785
- >
786
- > Mirror the real destination path within this directory. After writing each file, append an entry
787
- > to .claude/.dry-run/\<feature-name\>/.cache-manifest.json using:
788
- > {"cached_path": "...", "real_path": "...", "operation": "create|modify"}
789
-
790
- ### Failure handling
791
-
792
- If a doc-sync agent fails or times out:
793
- - Record `Docs: FAILED` for that feature in the Phase 4e report
794
- - Continue to Phase 4 — the sr-doc-sync failure is non-blocking
795
- - Include in the reviewer agent prompt: "Note: the sr-doc-sync agent failed for this feature."
796
-
797
- **Pipeline state:** update `doc-sync` → `done` (or `failed` with error context `"sr-doc-sync timed out or errored"`). Failure does not block the pipeline.
720
+ > **Note (v5):** dedicated test-writing (`sr-test-writer`) and doc-sync (`sr-doc-sync`) phases were removed. Tests and documentation are part of each OpenSpec task and are produced by `sr-developer`; the reviewer's TDD and spec-completeness checklist enforces them. A profile may reintroduce equivalent stages via `custom-*` agents with routing.
798
721
 
799
722
  ## Phase 4: Merge & Review
800
723
 
@@ -890,40 +813,15 @@ After all features are processed, print the preliminary report:
890
813
  - <file> (features: <a>, <b> — conflicting section: "<heading>")
891
814
  ```
892
815
 
893
- **Step 5a: Smart conflict resolution** (skip if `SINGLE_MODE=true` or `DRY_RUN=true` or `sr-merge-resolver` ∉ `AVAILABLE_AGENTS`)
894
-
895
- If `sr-merge-resolver` is not installed, print: `[smart-merge] sr-merge-resolver not installed — skipping merge conflict resolution agent. Fix conflicts manually.` and skip to Step 5b.
816
+ **Step 5a: Conflict handling** (orchestrator-owned; skip if `SINGLE_MODE=true` or `DRY_RUN=true`)
896
817
 
897
- If `MERGE_REPORT.requires_resolution` is non-empty:
818
+ There is no dedicated resolver agent. The built-in section-aware / `patch --forward` merge above IS the only resolution path. If `MERGE_REPORT.requires_resolution` is non-empty after the merge, the orchestrator does not guess:
898
819
 
899
- ```
900
- [smart-merge] N file(s) have conflict markers. Launching sr-merge-resolver…
901
- ```
902
-
903
- Build `CONTEXT_BUNDLES` from the features in `MERGE_ORDER`:
904
- ```
905
- { "<feature-a>": "${SPECRAILS_REPO_DIR:-.}/openspec/changes/<feature-a>/context-bundle.md", "<feature-b>": "${SPECRAILS_REPO_DIR:-.}/openspec/changes/<feature-b>/context-bundle.md" }
906
- ```
820
+ - Leave the conflict markers in place in the affected files.
821
+ - **Halt the affected features** — do not proceed to Phase 4c (git/PR) for any feature whose files still carry conflict markers. Independent features with no unresolved conflicts continue normally.
822
+ - Print: `[merge] N file(s) have unresolved conflict markers — halting the affected feature(s). Resolve manually, then re-run.`
907
823
 
908
- Load merge resolver config from `.claude/merge-resolver-config.json` if it exists. Extract `confidence_threshold` (default: 70) and `mode` (default: `auto`).
909
-
910
- Construct the `sr-merge-resolver` agent prompt with:
911
- - `CONFLICTED_FILES`: all file paths in `MERGE_REPORT.requires_resolution`
912
- - `CONTEXT_BUNDLES`: the map above
913
- - `CONFIDENCE_THRESHOLD`: from config or default (70)
914
- - `RESOLUTION_MODE`: from config or default (`auto`)
915
- - `REPORT_PATH`: `${SPECRAILS_REPO_DIR:-.}/openspec/changes/<first-feature-in-MERGE_ORDER>/merge-resolution-report.md`
916
-
917
- Launch the **sr-merge-resolver** agent (`subagent_type: sr-merge-resolver`, foreground, `run_in_background: false`). Wait for it to complete.
918
-
919
- Read `MERGE_RESOLUTION_STATUS` from the agent's output (`CLEAN`, `PARTIAL`, or `UNRESOLVED`).
920
-
921
- **Post-resolution: update MERGE_REPORT**
922
-
923
- For each file in `MERGE_REPORT.requires_resolution`:
924
- - Re-scan the file for remaining `<<<<<<<` markers.
925
- - If none remain: move the file from `requires_resolution` → `auto_resolved` (with note: `smart-resolver`).
926
- - If markers remain: keep in `requires_resolution`.
824
+ Files in `requires_resolution` MUST appear in the Phase 4e final report so the user sees exactly what to fix.
927
825
 
928
826
  **Step 5b: Emit final merge report**
929
827
 
@@ -945,7 +843,7 @@ Pipeline will continue. Fix remaining conflicts before the reviewer runs CI.
945
843
  Resolution report: openspec/changes/<feature>/merge-resolution-report.md
946
844
  ```
947
845
 
948
- If `MERGE_REPORT.requires_resolution` is now empty: print `All conflicts resolved by smart resolver.` and omit the "Requires Manual Resolution" section.
846
+ If `MERGE_REPORT.requires_resolution` is now empty: print `All conflicts resolved.` and omit the "Requires Manual Resolution" section.
949
847
 
950
848
  **Step 6: Clean up worktrees** (skip if `DRY_RUN=true`)
951
849
 
@@ -953,91 +851,23 @@ If `MERGE_REPORT.requires_resolution` is now empty: print `All conflicts resolve
953
851
  git worktree remove <worktree-path> --force
954
852
  ```
955
853
 
956
- Pass `MERGE_REPORT` to the Phase 4b reviewer agent prompt, listing any files in `requires_resolution`. If the smart resolver ran, also pass the path to `merge-resolution-report.md` so the reviewer can inspect resolution decisions for correctness.
957
-
958
- ### 4b. Layer Dispatch and Review
959
-
960
- #### Step 1: Layer Classification
961
-
962
- Before launching any reviewer, classify `MODIFIED_FILES_LIST` into layer-specific file sets.
963
-
964
- **Frontend files** — a file is frontend if any of these conditions match:
965
- - Extension is one of: `.jsx`, `.tsx`, `.vue`, `.svelte`, `.css`, `.scss`, `.sass`, `.less`, `.html`, `.htm`
966
- - Extension is `.js` or `.ts` AND path contains one of: `components/`, `pages/`, `views/`, `ui/`, `client/`, `frontend/`, `app/`
967
- - Path starts with: `public/`, `static/`, `assets/`
968
-
969
- Set `FRONTEND_FILES` = files matching frontend rules.
970
-
971
- **Backend files** — a file is backend if any of these conditions match:
972
- - Extension is one of: `.py`, `.go`, `.java`, `.rb`, `.php`, `.rs`, `.cs`, `.sql`
973
- - Extension is `.js` or `.ts` AND path contains one of: `server/`, `api/`, `routes/`, `controllers/`, `services/`, `models/`, `db/`, `backend/`
974
- - Path is under: `migrations/`, `alembic/`, `db/migrate/`
975
-
976
- Set `BACKEND_FILES` = files matching backend rules.
977
-
978
- **Overlap rule:** a file may appear in both `FRONTEND_FILES` and `BACKEND_FILES` (e.g., a Next.js API route at `pages/api/`). Both reviewers will scan it independently.
979
-
980
- #### Step 2: Determine which reviewers to launch
981
-
982
- A layer reviewer is launched when **both** conditions are met:
983
- 1. There are files to review for that layer (file classification) **OR** the corresponding layer developer was used in Phase 3b (`DEVELOPER_AGENTS_USED`)
984
- 2. The reviewer agent is installed (`∈ AVAILABLE_AGENTS`)
985
-
986
- | Reviewer | Launch condition | Skip reason |
987
- |----------|-----------------|-------------|
988
- | sr-frontend-reviewer | (`FRONTEND_FILES` non-empty OR `sr-frontend-developer` ∈ `DEVELOPER_AGENTS_USED`) AND installed | No frontend files + no frontend developer used, or not installed |
989
- | sr-backend-reviewer | (`BACKEND_FILES` non-empty OR `sr-backend-developer` ∈ `DEVELOPER_AGENTS_USED`) AND installed | No backend files + no backend developer used, or not installed |
990
- | sr-security-reviewer | Installed | Not installed |
991
- | sr-performance-reviewer | Installed | Not installed |
992
-
993
- If a reviewer is skipped, set its report variable to `"SKIPPED"` and note the reason.
994
-
995
- #### Step 3: Launch Layer Reviewers in Parallel
996
-
997
- Launch all applicable layer reviewers in parallel (`run_in_background: true`), using the corresponding `subagent_type` for each (`sr-frontend-reviewer`, `sr-backend-reviewer`, `sr-security-reviewer`, `sr-performance-reviewer`):
998
-
999
- **sr-frontend-reviewer** (`subagent_type: sr-frontend-reviewer`, if applicable per Step 2):
1000
- - Pass `FRONTEND_FILES_LIST`: the list of files in `FRONTEND_FILES`
1001
- - Pass `PIPELINE_CONTEXT`: brief description of what was implemented
1002
-
1003
- **sr-backend-reviewer** (`subagent_type: sr-backend-reviewer`, if applicable per Step 2):
1004
- - Pass `BACKEND_FILES_LIST`: the list of files in `BACKEND_FILES`
1005
- - Pass `PIPELINE_CONTEXT`: brief description of what was implemented
1006
-
1007
- **sr-security-reviewer** (`subagent_type: sr-security-reviewer`, if applicable per Step 2):
1008
- - Pass `MODIFIED_FILES_LIST`: the complete list of all files created or modified during this run
1009
- - Pass `PIPELINE_CONTEXT`: brief description of what was implemented
1010
- - Pass the exemptions config path: `.claude/security-exemptions.yaml`
1011
-
1012
- **sr-performance-reviewer** (`subagent_type: sr-performance-reviewer`, if applicable per Step 2):
1013
- - Pass `MODIFIED_FILES_LIST`: the complete list of all files created or modified during this run
1014
- - Pass `PIPELINE_CONTEXT`: brief description of what was implemented
1015
-
1016
- Wait for all launched layer reviewers to complete before proceeding to Step 4.
1017
-
1018
- Parse status lines from each completed reviewer:
1019
- - `FRONTEND_REVIEW_STATUS: ISSUES_FOUND` or `CLEAN` → set `FRONTEND_STATUS`
1020
- - `BACKEND_REVIEW_STATUS: ISSUES_FOUND` or `CLEAN` → set `BACKEND_STATUS`
1021
- - `SECURITY_STATUS: BLOCKED | WARNINGS | CLEAN` → set `SECURITY_BLOCKED=true` if `BLOCKED`, otherwise `false`
1022
-
1023
- If a layer reviewer fails or times out: set the relevant report variable to `"ERROR: reviewer did not complete"` and continue.
1024
-
1025
- #### Step 4: Launch Generalist Reviewer
854
+ Pass `MERGE_REPORT` to the Phase 4b reviewer agent prompt, listing any files in `requires_resolution`.
1026
855
 
1027
- Construct the generalist reviewer's invocation prompt with layer reports injected. Set each variable to the full output of the corresponding reviewer, or to the string `"SKIPPED"` if that reviewer was not launched:
856
+ ### 4b. Review
1028
857
 
1029
- - `FRONTEND_REVIEW_REPORT`: full output of frontend-reviewer (or `"SKIPPED"`)
1030
- - `BACKEND_REVIEW_REPORT`: full output of backend-reviewer (or `"SKIPPED"`)
1031
- - `SECURITY_REVIEW_REPORT`: full output of security-reviewer
858
+ There is a single reviewer, `sr-reviewer`. It owns every review dimension — correctness, TDD/spec completeness, code quality, **security**, and **performance** — scaled to what the change actually touches (its checklist covers all of them). There are no separate layer-reviewer passes.
1032
859
 
1033
- Include in the reviewer prompt:
1034
- - Full CI commands
860
+ Construct the reviewer's invocation prompt with:
861
+ - `MODIFIED_FILES_LIST`: the complete list of all files created or modified during this run
862
+ - `PIPELINE_CONTEXT`: a brief description of what was implemented
863
+ - `MERGE_REPORT`: any files still in `requires_resolution` (multi-feature runs)
864
+ - The security-exemptions config path: `.claude/security-exemptions.yaml` (if present)
865
+ - Full CI commands (reference material for its scoped-first verification policy — the reviewer runs diff-scoped tests plus fast static checks, escalating to one full pass only when its own fixes warrant it; the developer's Phase 4 full pass is the baseline)
1035
866
  - Cross-feature merge issue checks
1036
867
  - Instruction to record learnings to `common-fixes.md`
1037
868
  - Instruction to archive completed changes via OpenSpec
1038
- - The three layer report variables substituted into the `[injected]` slots in the reviewer agent template
1039
869
 
1040
- Note: if total layer report length is very large, truncate each layer report to its findings tables only (omit skipped-file logs) to stay within prompt limits.
870
+ The reviewer emits `SECURITY_STATUS: BLOCKED | WARNINGS | CLEAN`. Set `SECURITY_BLOCKED=true` if `BLOCKED`, otherwise `false`.
1041
871
 
1042
872
  **The security gate (blocking ship on `SECURITY_STATUS: BLOCKED`) is enforced in Phase 4c.** Do not apply it here.
1043
873
 
@@ -1407,7 +1237,6 @@ Include the shipping mode in the report:
1407
1237
 
1408
1238
  ## Error Handling
1409
1239
 
1410
- - If a sr-product-manager fails: skip that area, continue with others
1411
1240
  - If a sr-architect fails: skip that area, report the failure
1412
1241
  - If a sr-developer fails: report which phase it failed at
1413
1242
  - If the sr-reviewer finds unfixable issues: report them, push what works
@@ -122,12 +122,14 @@ If `AGENT_FILTER` is set (single-agent mode), show the full content of each file
122
122
 
123
123
  ## Phase 4: Orphan Detection
124
124
 
125
- An **orphaned** memory directory is one whose agent name does not correspond to a known sr-agent persona.
125
+ An **orphaned** memory directory is one whose agent name does not correspond to a known agent.
126
126
 
127
- Known sr-agent names (check for exact match):
128
- `sr-architect`, `sr-developer`, `sr-test-writer`, `sr-reviewer`, `sr-frontend-reviewer`, `sr-backend-reviewer`, `sr-security-reviewer`, `sr-doc-sync`, `sr-product-manager`
127
+ Known agent names (check for exact match):
128
+ `sr-architect`, `sr-developer`, `sr-reviewer`
129
129
 
130
- For each directory in `AGENT_DIRS`, check whether its `AGENT_NAME` is in the known list. Collect non-matching directories as `ORPHANED_DIRS`.
130
+ Any directory whose name begins with `custom-` is a user- or profile-owned agent and is **never** orphaned treat the `custom-` prefix as known.
131
+
132
+ For each directory in `AGENT_DIRS`, check whether its `AGENT_NAME` is in the known list (or carries the `custom-` prefix). Collect non-matching directories as `ORPHANED_DIRS`.
131
133
 
132
134
  If `ORPHANED_DIRS` is non-empty, print:
133
135