xtrm-tools 0.7.10 → 0.7.12

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.
@@ -9,8 +9,8 @@ description: >
9
9
  workflow, --context-depth, background jobs, MCP tool (`use_specialist`),
10
10
  or specialists doctor. Don't wait for the user to say
11
11
  "use a specialist" — proactively evaluate whether delegation makes sense.
12
- version: 4.6
13
- synced_at: zz22-docs
12
+ version: 4.8
13
+ synced_at: a58a4dda
14
14
  ---
15
15
 
16
16
  # Specialists Usage
@@ -29,13 +29,34 @@ Specialists are autonomous AI agents that run independently — fresh context, d
29
29
 
30
30
  ---
31
31
 
32
+ ## Response Style Policy
33
+
34
+ - Be direct, concise, and professional.
35
+ - Answer the user's actual question first, in the first sentence when possible.
36
+ - Do not append conversational filler like:
37
+ - "If you want, I can..."
38
+ - "I can also..."
39
+ - "Let me know if you want..."
40
+ unless the user explicitly asked for options.
41
+ - Do not restate context the user already provided unless needed to resolve ambiguity.
42
+ - Prefer short conclusions over long explanatory structures.
43
+ - Use bullets only when they improve clarity; otherwise respond in plain prose.
44
+ - Do not hedge unnecessarily. If the answer is clear, state it plainly.
45
+ - Do not give a recommendation section unless the user asked for recommendations or a decision.
46
+ - Do not propose next steps automatically after every answer.
47
+ - When reporting status, give:
48
+ 1. current state
49
+ 2. blocker or result
50
+ 3. only the next action if action is already implied or necessary
51
+ - Default to terse operational language, not coaching language.
52
+
32
53
  ## Hard Rules
33
54
 
34
55
  1. **Zero implementation by orchestrator.** When this skill is active for substantial work, you do not implement the solution yourself.
35
56
  2. **Never explore yourself.** All discovery, codebase mapping, and read-only investigation go through **explorer** (or **debugger** for root-cause analysis).
36
57
  3. **Run explorer before executor when context is lacking.** If the bead already has clear scope — files, symbols, approach — send executor directly. Only run explorer first when the issue lacks a clear track.
37
58
  4. **For tracked work, the bead is the prompt.** The bead description, notes, and parent context are the instruction surface.
38
- 5. **`--bead` and `--prompt` are mutually exclusive.** If you need to refine instructions, update the bead notes; do not add `--prompt`.
59
+ 5. **`--bead` is the only prompt.** Never use `--prompt`. If you need to refine instructions, update the bead notes first.
39
60
  6. **Chains belong to epics.** A chain is a worktree lineage (executor → reviewer → fix). An epic is the merge-gated identity that owns chains. Use `sp epic merge <epic>` to publish — never merge individual chains that belong to an unresolved epic.
40
61
  7. **Merge through epics, not manual git.** Use `sp epic merge <epic-id>` for wave-bound chains or `sp merge <chain-root-bead>` for standalone chains. Never use manual `git merge` for specialist work.
41
62
  8. **No destructive operations by specialists.** No `rm -rf`, no force pushes, no database drops, no credential rotation, no mass deletes, no history rewrites. Surface destructive requirements to the user.
@@ -72,7 +93,7 @@ specialists run <name> --bead <id> --background # background run
72
93
  specialists run <name> --bead <id> --worktree # isolated worktree (edit-capable specialists)
73
94
  specialists run <name> --bead <id> --job <job-id> # reuse another job's worktree
74
95
  specialists run <name> --bead <id> --epic <epic-id> # explicitly declare epic membership
75
- specialists run <name> --prompt "..." # ad-hoc (no bead tracking)
96
+ specialists run <name> --bead <id> --force-stale-base # bypass stale-base guard
76
97
  specialists run <name> --bead <id> --keep-alive # keep session alive after first turn
77
98
  specialists run <name> --bead <id> --context-depth 2 # inject parent bead context
78
99
 
@@ -87,7 +108,9 @@ specialists status --job <job-id> # single-job detail view (legacy
87
108
  # Epic lifecycle (canonical publication path)
88
109
  specialists epic list [--unresolved] # list epics with lifecycle state
89
110
  specialists epic status <epic-id> # show chains, blockers, readiness
111
+ specialists epic sync <epic-id> [--apply] # reconcile DB vs live state (dry-run default)
90
112
  specialists epic resolve <epic-id> # transition open -> resolving
113
+ specialists epic abandon <epic-id> --reason <text> [--force] # terminal transition for stuck epics
91
114
  specialists epic merge <epic-id> [--pr] # publish all epic-owned chains
92
115
 
93
116
  # Merge (for standalone chains only)
@@ -100,11 +123,15 @@ specialists end [--pr] # close session, publish via merge
100
123
  specialists steer <job-id> "new direction" # redirect ANY running job mid-run
101
124
  specialists resume <job-id> "next task" # resume a waiting keep-alive job
102
125
  specialists stop <job-id> # cancel a job
126
+ specialists stop <job-id> --force # 5s SIGTERM timeout, then pgroup kill + error status
103
127
 
104
128
  # Management
105
129
  specialists edit <name> # edit specialist config (dot-path, --preset)
106
130
  specialists clean # purge old job dirs + worktree GC
107
131
  specialists clean --processes # kill all running/starting specialist jobs
132
+ specialists db vacuum # compact SQLite storage (refuses if jobs running)
133
+ specialists db prune --before <iso|duration> --dry-run|--apply # prune old events/results/terminal jobs
134
+ specialists doctor orphans # integrity scan: orphan, stale-pointer, integrity-violation
108
135
  specialists init --sync-skills # re-sync skills only (no full init)
109
136
  specialists init --no-xtrm-check # skip xtrm prerequisite check (CI/testing)
110
137
  ```
@@ -177,7 +204,7 @@ via `--context-depth 2`. The bead chain IS the context chain — zero manual wir
177
204
  task-abc: "Fix auth token refresh"
178
205
  └── abc-exp: explorer (READ_ONLY — auto-appends output to abc-exp notes)
179
206
  └── abc-impl: executor (self-appends output to abc-impl notes, closes bead)
180
- └── abc-rev: reviewer (READ_ONLY — auto-appends verdict via --job <exec-job>)
207
+ └── abc-rev: reviewer (auto-appends verdict to abc-rev notes via --job <exec-job>)
181
208
  └── abc-fix: executor (if reviewer PARTIAL — fix bead, same worktree via --job)
182
209
  ```
183
210
 
@@ -187,7 +214,7 @@ task-abc: "Fix auth token refresh"
187
214
  |------|----------------|-----|
188
215
  | abc-exp | abc-exp (own) + task-abc (parent) | `--bead abc-exp --context-depth 2` |
189
216
  | abc-impl | abc-impl (own) + abc-exp (explorer findings in notes) + task-abc | `--bead abc-impl --context-depth 2` |
190
- | reviewer | abc-impl bead (with executor output + reviewer verdict in notes) | `--bead abc-impl --job <exec-job>` |
217
+ | abc-rev | abc-rev (own) + abc-impl (executor output in notes) + task-abc | `--bead abc-rev --job <exec-job> --context-depth 2` |
191
218
  | abc-fix | abc-fix (own) + abc-impl (executor output + reviewer verdict) + abc-exp | `--bead abc-fix --job <exec-job> --context-depth 2` |
192
219
 
193
220
  - No copy-paste, no manual note injection between steps
@@ -228,11 +255,15 @@ specialists run executor --worktree --bead abc-impl --context-depth 2 --backgrou
228
255
  # 6. [MERGE] Merge impl worktree branch into master
229
256
  sp merge abc-impl --rebuild
230
257
 
231
- # 7. Wave 3 — Reviewer (no separate bead uses --job + --prompt to enter executor's worktree)
232
- specialists run reviewer --job a1b2c3 --keep-alive --background --prompt "Review the token refresh fix"
258
+ # 7. Wave 3 — Reviewer (own bead, enters executor's worktree via --job)
259
+ bd create --title "Review: token refresh fix" --type task --priority 2
260
+ # -> unitAI-abc-rev
261
+ bd dep add abc-rev abc-impl
262
+
263
+ specialists run reviewer --bead abc-rev --job a1b2c3 --context-depth 2 --keep-alive --background
233
264
  # -> Job started: r4v5w6
234
- # Reviewer reads task bead from job a1b2c3's status.json automatically
235
- # Reviewer auto-appends verdict to bead notes (READ_ONLY)
265
+ # Reviewer sees: abc-rev + abc-impl (with executor output in notes) + abc via context-depth
266
+ # Reviewer auto-appends verdict to abc-rev notes
236
267
  specialists result r4v5w6
237
268
  # -> PASS: close task bead. PARTIAL/FAIL: go to step 8.
238
269
 
@@ -304,8 +335,8 @@ Reads `worktree_path` from the target job's `status.json` and uses that director
304
335
  The caller's own `--bead` remains authoritative — `--job` only selects the workspace.
305
336
 
306
337
  ```bash
307
- # Reviewer enters executor's worktree to review exactly what was written
308
- specialists run reviewer --job 49adda --keep-alive --background
338
+ # Reviewer enters executor's worktree with its own bead
339
+ specialists run reviewer --bead unitAI-rev --job 49adda --context-depth 2 --keep-alive --background
309
340
 
310
341
  # Fix executor re-enters same worktree (--bead provides new fix bead, --job provides workspace)
311
342
  specialists run executor --bead hgpu.3-fix --job 49adda --context-depth 2 --background
@@ -336,7 +367,7 @@ Use when the caller explicitly accepts concurrent write risk (e.g., target job k
336
367
  | Scenario | Flag to use |
337
368
  |----------|------------|
338
369
  | First executor run for a task | `--worktree --bead <impl-bead>` |
339
- | Reviewer on executor's output | `--job <exec-job-id>` (no `--worktree`) |
370
+ | Reviewer on executor's output | `--bead <review-bead> --job <exec-job-id> --context-depth 2` |
340
371
  | Fix executor after reviewer PARTIAL | `--bead <fix-bead> --job <exec-job-id>` |
341
372
  | Force entry to blocked worktree | `--bead <fix-bead> --job <exec-job-id> --force-job` |
342
373
  | Prep job belonging to epic (non-epic parent) | `--bead <prep-bead> --epic <epic-id>` |
@@ -367,35 +398,36 @@ Map bead dependencies to match the execution pipeline. The dep graph IS the wave
367
398
 
368
399
  ### Simple bug fix
369
400
  ```
370
- task → explore → impl
371
- └── reviewer via --job (no own bead needed)
372
- └── fix (if PARTIAL) → child of impl
401
+ task → explore → impl → review
402
+ └── fix (if PARTIAL) child of impl
373
403
  ```
374
404
  ```bash
375
405
  bd dep add explore task
376
406
  bd dep add impl explore
377
- # reviewer: specialists run reviewer --job <impl-job>
407
+ bd dep add review impl
408
+ # reviewer: specialists run reviewer --bead review --job <impl-job> --context-depth 2
378
409
  # fix: bd dep add fix impl
379
410
  ```
380
411
 
381
412
  ### Complex feature (overthinker)
382
413
  ```
383
- task → explore → design → impl → [reviewer via --job] → [fix if PARTIAL]
414
+ task → explore → design → impl → review → [fix if PARTIAL]
384
415
  ```
385
416
  ```bash
386
417
  bd dep add explore task
387
418
  bd dep add design explore
388
419
  bd dep add impl design
389
- # reviewer: specialists run reviewer --job <impl-job>
420
+ bd dep add review impl
421
+ # reviewer: specialists run reviewer --bead review --job <impl-job> --context-depth 2
390
422
  ```
391
423
 
392
424
  ### Epic with N children
393
425
  Each child gets its own explore → impl chain. Reviewer runs via `--job` per impl.
394
426
  ```
395
427
  epic
396
- ├── child-1 → explore-1 → impl-1 (reviewer via --job impl-1-job)
397
- ├── child-2 → explore-2 → impl-2 (reviewer via --job impl-2-job)
398
- └── child-N → explore-N → impl-N (reviewer via --job impl-N-job)
428
+ ├── child-1 → explore-1 → impl-1 → review-1 (reviewer --bead review-1 --job impl-1-job)
429
+ ├── child-2 → explore-2 → impl-2 → review-2 (reviewer --bead review-2 --job impl-2-job)
430
+ └── child-N → explore-N → impl-N → review-N (reviewer --bead review-N --job impl-N-job)
399
431
  ```
400
432
  Children (chains) within the same epic can run **in parallel** if they own disjoint files.
401
433
 
@@ -430,16 +462,15 @@ The review → fix loop is the mechanism for iterative quality improvement withi
430
462
  1. Executor provisions --worktree, implements, enters waiting.
431
463
  -> Job: exec-job (KEEP ALIVE — do not stop)
432
464
 
433
- 2. Reviewer enters same worktree via --job exec-job.
465
+ 2. Reviewer enters same worktree via --bead <review-bead> --job exec-job --context-depth 2.
434
466
  -> sp ps shows the chain:
435
467
  feature/unitAI-impl-executor · unitAI-impl
436
468
  ◐ exec-job executor waiting
437
469
  └ ◐ rev-job reviewer starting
438
- -> Auto-appends verdict (PASS/PARTIAL/FAIL) to bead notes.
470
+ -> Auto-appends verdict (PASS/PARTIAL/FAIL) to review bead notes.
439
471
 
440
472
  3a. PASS:
441
- -> Resume executor: "Reviewer PASS. Commit your changes."
442
- -> Verify commit landed on branch (git log)
473
+ -> Verify auto-commit landed on branch (git log)
443
474
  -> Stop reviewer, then stop executor
444
475
  -> Merge via sp merge
445
476
 
@@ -460,14 +491,17 @@ specialists run executor --worktree --bead unitAI-impl --context-depth 2 --backg
460
491
  # -> Job started: exec-job (e.g. 49adda)
461
492
  # DO NOT sp stop — executor stays alive for the entire review cycle
462
493
 
463
- # Step 2 — Reviewer enters same worktree
464
- specialists run reviewer --job 49adda --keep-alive --background --prompt "Review impl changes"
494
+ # Step 2 — Create reviewer bead and dispatch
495
+ bd create --title "Review: impl changes" --type task --priority 2
496
+ # -> unitAI-rev
497
+ bd dep add rev impl
498
+ specialists run reviewer --bead unitAI-rev --job 49adda --context-depth 2 --keep-alive --background
465
499
  # -> Job started: rev-job
466
500
  specialists result rev-job
467
501
 
468
- # Step 3a — PASS: resume executor to commit, then stop both
469
- specialists resume 49adda "Reviewer PASS. Git add and commit your changes."
470
- # Wait for commit, verify with: git log feature/unitAI-impl-executor --oneline -1
502
+ # Step 3a — PASS: verify auto-commit landed, then stop both
503
+ # Executor auto-commits substantive changes on each turn completion
504
+ # Verify with: git log feature/unitAI-impl-executor --oneline -1
471
505
  specialists stop rev-job
472
506
  specialists stop 49adda
473
507
  sp merge unitAI-impl --rebuild
@@ -475,8 +509,11 @@ sp merge unitAI-impl --rebuild
475
509
  # Step 3b — PARTIAL: resume executor with fix instructions (same session, full context)
476
510
  specialists resume 49adda "Reviewer PARTIAL. Fix: <paste specific findings here>"
477
511
  # Executor applies fixes, enters waiting again
478
- # Dispatch new reviewer:
479
- specialists run reviewer --job 49adda --keep-alive --background --prompt "Re-review after fix"
512
+ # Dispatch new reviewer (new bead for each re-review):
513
+ bd create --title "Re-review: impl after fix" --type task --priority 2
514
+ # -> unitAI-rev2
515
+ bd dep add rev2 impl
516
+ specialists run reviewer --bead unitAI-rev2 --job 49adda --context-depth 2 --keep-alive --background
480
517
  # Repeat until PASS
481
518
 
482
519
  # After final PASS + commit + stop:
@@ -496,10 +533,10 @@ Only dispatch a new fix executor when the original specialist is dead (crashed,
496
533
 
497
534
  ### Key invariants
498
535
  - **Never stop the executor/debugger before reviewer verdict.** The specialist stays in `waiting` throughout the review cycle. Stopping prematurely kills the resume path and risks uncommitted changes.
499
- - **Executors do not auto-commit.** After reviewer PASS, you must resume the executor with explicit commit instructions. Verify the commit landed before stopping.
500
- - Each fix iteration uses `resume` on the same specialist — not a new child bead or new executor.
536
+ - **Executors auto-commit substantive changes** on each turn completion (via `auto_commit: checkpoint_on_waiting`). After reviewer PASS, verify the commit landed on the branch before stopping.
537
+ - Each fix iteration uses `resume` on the same executor — not a new child bead or new executor.
501
538
  - Multiple reviewer → resume → re-review cycles are expected. The worktree and specialist session are stable across all cycles.
502
- - Only stop after: (1) reviewer PASS, (2) executor committed, (3) commit verified on branch.
539
+ - Only stop after: (1) reviewer PASS, (2) auto-commit verified on branch.
503
540
 
504
541
  ---
505
542
 
@@ -534,8 +571,7 @@ sp stop exec-job # ✗ kills resume path, risks uncommitted work
534
571
  sp stop overthinker-job # ✗ loses context if follow-up questions arise
535
572
 
536
573
  # GOOD — chain completes naturally
537
- sp resume exec-job "Reviewer PASS. Commit your changes."
538
- # verify commit...
574
+ # verify auto-commit landed on branch...
539
575
  sp merge unitAI-impl # publishes branch
540
576
  # THEN stop members (future: auto-stopped by merge)
541
577
  sp stop rev-job
@@ -671,7 +707,7 @@ The specialist reads:
671
707
 
672
708
  This prevents specialists from rediscovering known gotchas on every run.
673
709
 
674
- `--prompt` and `--bead` cannot be combined. When you need to give a specialist
710
+ **Never use `--prompt`.** For tracked work, always use `--bead`. When you need to give a specialist
675
711
  specific instructions beyond what's in the bead description, update the bead notes first:
676
712
 
677
713
  ```bash
@@ -714,9 +750,9 @@ Run `specialists list` to see what's available. Match by task type:
714
750
  ### Specialist selection notes
715
751
 
716
752
  - **executor does not run tests** — it runs `lint + tsc` only. Tests belong to the reviewer or test-runner phase.
717
- - **executor enters `waiting` after first turn** — `interactive: true` is now default. **Never stop the executor before reviewer verdict.** Keep it alive so you can: (1) resume with fix instructions if reviewer says PARTIAL, (2) resume with "commit your changes" after reviewer PASS. Executors do not auto-commit you must explicitly resume them to commit. Only `sp stop` after the commit is verified on the branch.
718
- - **explorer** is READ_ONLY — its output auto-appends to the input bead's notes. No implementation.
719
- - **reviewer** is best dispatched via `--job <exec-job> --prompt "..."` it enters the same worktree to see exactly what was written. `--job` alone is not enough; `--prompt` or `--bead` is always required.
753
+ - **executor enters `waiting` after first turn** — `interactive: true` is now default. **Never stop the executor before reviewer verdict.** Keep it alive so you can resume with fix instructions if reviewer says PARTIAL. Executors auto-commit substantive changes on each turn via `auto_commit: checkpoint_on_waiting`. Only `sp stop` after reviewer PASS and commit verified on the branch.
754
+ - **explorer** is READ_ONLY — output auto-appends to the input bead's notes. No implementation.
755
+ - **reviewer** always gets its own bead: `--bead <review-bead> --job <exec-job> --context-depth 2`. The reviewer sees the executor's output via auto-appended bead notes + context-depth. Never use `--prompt`.
720
756
  - **debugger** over **explorer** when you need root cause analysis — GitNexus call-chain tracing, ranked hypotheses, evidence-backed remediation.
721
757
  - **overthinker** before **executor** for any non-trivial task — surfaces edge cases, challenges assumptions, produces solution direction. Cheap relative to wrong implementation.
722
758
  - **researcher** is the docs specialist — never look up library docs yourself, delegate to researcher.
@@ -731,7 +767,7 @@ specialists run debugger --bead unitAI-bug --context-depth 2 --background
731
767
  specialists run planner --bead unitAI-scope --context-depth 2 --background
732
768
  specialists run overthinker --bead unitAI-design --context-depth 2 --keep-alive --background
733
769
  specialists run executor --worktree --bead unitAI-impl --context-depth 2 --background
734
- specialists run reviewer --job <exec-job-id> --keep-alive --background --prompt "Review the <feature> implementation"
770
+ specialists run reviewer --bead unitAI-rev --job <exec-job-id> --context-depth 2 --keep-alive --background
735
771
  specialists run sync-docs --bead unitAI-docs --context-depth 2 --keep-alive --background
736
772
  specialists run test-runner --bead unitAI-tests --context-depth 2 --background
737
773
  specialists run specialists-creator --bead unitAI-skill --context-depth 2 --background
@@ -852,8 +888,8 @@ specialists steer a1b2c3 "Do NOT audit. Write the actual file to disk now."
852
888
  > before killing a keep-alive job.**
853
889
 
854
890
  > **Critical:** Never stop an executor or debugger before the reviewer delivers its verdict.
855
- > Stopping prematurely: (1) kills the resume path for fix loops, (2) risks uncommitted changes
856
- > (executors don't auto-commit), and (3) forces dispatching a new specialist instead of resuming.
891
+ > Stopping prematurely: (1) kills the resume path for fix loops, and (2) forces dispatching a
892
+ > new specialist instead of resuming. Executors auto-commit substantive changes on each turn.
857
893
 
858
894
  ```bash
859
895
  # Check before stopping
@@ -917,7 +953,7 @@ bd create --title "Explore: map job run architecture" --type task --priority 2
917
953
  bd dep add exp 3f7b
918
954
  bd create --title "Implement: worktree isolation" --type task --priority 2 # -> unitAI-impl
919
955
  bd dep add impl exp
920
- # Note: reviewer runs via --job, inherits epic from impl bead.parent
956
+ # Note: reviewer gets own bead, enters via --job, inherits epic from bead.parent
921
957
 
922
958
  # Stage 1 — Explorer (prep job, declares epic explicitly)
923
959
  specialists run explorer --bead unitAI-exp --epic unitAI-3f7b --context-depth 2 --background
@@ -932,8 +968,10 @@ specialists run executor --worktree --bead unitAI-impl --context-depth 2 --backg
932
968
  # epic_id = bead.parent (unitAI-3f7b)
933
969
  specialists result job2
934
970
 
935
- # Stage 3 — Reviewer (uses --job, same worktree)
936
- specialists run reviewer --job job2 --keep-alive --background --prompt "Review implementation"
971
+ # Stage 3 — Reviewer (own bead, uses --job for same worktree)
972
+ bd create --title "Review: worktree isolation impl" --type task --priority 2 # -> unitAI-rev
973
+ bd dep add rev impl
974
+ specialists run reviewer --bead unitAI-rev --job job2 --context-depth 2 --keep-alive --background
937
975
  # -> Job started: job3
938
976
  specialists result job3
939
977
  # PASS → ready for epic merge. PARTIAL → fix loop.
@@ -942,8 +980,10 @@ specialists result job3
942
980
  bd create --title "Fix: reviewer gaps on impl" --type bug --priority 1 # -> unitAI-fix1
943
981
  bd dep add fix1 impl
944
982
  specialists run executor --bead fix1 --job job2 --context-depth 2 --background
945
- # Re-review
946
- specialists run reviewer --job job2 --keep-alive --background --prompt "Re-review after fix"
983
+ # Re-review (new reviewer bead)
984
+ bd create --title "Re-review: impl after fix" --type task --priority 2 # -> unitAI-rev2
985
+ bd dep add rev2 impl
986
+ specialists run reviewer --bead unitAI-rev2 --job job2 --context-depth 2 --keep-alive --background
947
987
 
948
988
  # [MERGE] Publish epic
949
989
  sp epic status unitAI-3f7b # verify readiness: merge_ready, all chains PASS
@@ -1018,8 +1058,8 @@ MCP is intentionally minimal. Use CLI for orchestration, monitoring, steering, r
1018
1058
 
1019
1059
  ## Known Issues
1020
1060
 
1021
- - **READ_ONLY output auto-appends** to the input bead after completion (via Supervisor). Output also available via `specialists result`.
1022
- - **`--bead` and `--prompt` conflict** by design. For tracked work, update bead notes: `bd update <id> --notes "INSTRUCTION: ..."` then `--bead` only.
1061
+ - **All specialist output auto-appends** to the input bead notes on every `run_complete` (via Supervisor). Status-aware headers: `[WAITING]` vs `[DONE]`. Output also available via `specialists result`.
1062
+ - **`--prompt` is deprecated for tracked work.** Always use `--bead`. Update bead notes for additional instructions: `bd update <id> --notes "INSTRUCTION: ..."`
1023
1063
  - **Job in `waiting` now shows magenta status** with resume hint in `status`, WAIT banner in `feed`, and resume footer in `result`. Always check before stopping a keep-alive job.
1024
1064
  - **Explorer (qwen) may produce empty output** — the model sometimes completes tool calls but fails to emit a final text summary. The bead notes will be empty. If this happens, either re-run with a different model or do the investigation yourself.
1025
1065
  - **`specialists init` requires xtrm** — `.xtrm/` directory and `xt` CLI must exist. Use `--no-xtrm-check` to bypass in CI/testing.
@@ -1031,10 +1071,60 @@ MCP is intentionally minimal. Use CLI for orchestration, monitoring, steering, r
1031
1071
 
1032
1072
  ```bash
1033
1073
  specialists doctor # health check: hooks, MCP, zombie jobs, skill drift detection
1074
+ specialists doctor orphans # integrity scan for orphan/stale-pointer/integrity-violation
1034
1075
  specialists edit <name> # edit specialist config (dot-path, --preset)
1035
1076
  specialists clean --processes # kill stale/zombie specialist processes
1036
1077
  ```
1037
1078
 
1079
+ ## Stuck-State Recovery
1080
+
1081
+ Use this flow when epic/job state disagrees with live runtime or close path loops.
1082
+
1083
+ ### 1) Reconcile epic state first (safe default)
1084
+
1085
+ ```bash
1086
+ sp epic status <epic-id>
1087
+ sp epic sync <epic-id> # dry-run default, inspect planned fixes
1088
+ sp epic sync <epic-id> --apply # apply reconciliation after review
1089
+ ```
1090
+
1091
+ `sp epic sync` is primary recovery command. It reconciles DB state against live job/worktree state.
1092
+
1093
+ ### 2) Terminally abandon unrecoverable epic
1094
+
1095
+ ```bash
1096
+ sp epic abandon <epic-id> --reason "stuck chain with unrecoverable state"
1097
+ # If guard blocks due to active pointers you intentionally want cleared:
1098
+ sp epic abandon <epic-id> --reason "manual recovery" --force
1099
+ ```
1100
+
1101
+ Use only when epic cannot be restored to valid resolving/merge path.
1102
+
1103
+ ### 3) Restore DB hygiene after recovery
1104
+
1105
+ ```bash
1106
+ sp doctor orphans
1107
+ sp db vacuum
1108
+ sp db prune --before 30d --dry-run
1109
+ sp db prune --before 30d --apply
1110
+ ```
1111
+
1112
+ - `sp db vacuum` compacts SQLite file, refuses while jobs running.
1113
+ - `sp db prune` removes old rows from events/results/terminal jobs; dry-run first.
1114
+
1115
+ ### 4) Hard-stop wedged jobs when normal stop fails
1116
+
1117
+ ```bash
1118
+ sp stop <job-id>
1119
+ sp stop <job-id> --force
1120
+ ```
1121
+
1122
+ `--force` waits 5s for SIGTERM, then kills process group and records explicit error status.
1123
+
1124
+ ### 5) `sp end` open-state loop fix
1125
+
1126
+ If `sp end` detects open-state mismatch, tool now suggests `sp epic resolve <epic-id>` as next command (no redirect loop).
1127
+
1038
1128
  - **RPC timeout on worktree job start** (30s, `command id=1`) → pi runs `npm install` in fresh
1039
1129
  worktrees if `.pi/settings.json` lists local packages. Root cause: worktree gets a stale copy
1040
1130
  of `.pi/settings.json` from the branch point. Fix: ensure `.pi/settings.json` has
@@ -1047,10 +1137,10 @@ specialists clean --processes # kill stale/zombie specialist processes
1047
1137
  - **Job hangs** → `specialists steer <id> "finish up"` or `specialists stop <id>`
1048
1138
  - **Config skipped** → stderr shows `[specialists] skipping <file>: <reason>`
1049
1139
  - **Stall timeout** → specialist hit 120s inactivity. Check `specialists feed <id>`, then retry or switch.
1050
- - **`--prompt` and `--bead` conflict** → use bead notes: `bd update <id> --notes "INSTRUCTION: ..."` then `--bead` only.
1140
+ - **Never use `--prompt`** → use bead notes: `bd update <id> --notes "INSTRUCTION: ..."` then `--bead` only.
1051
1141
  - **Worktree already exists** → it will be reused (not recreated). Safe to re-run.
1052
1142
  - **`--job` fails: worktree_path missing** → target job was not started with `--worktree`. Use `--worktree` on the next run.
1053
- - **`--job` without `--prompt` or `--bead`** → reviewer/executor requires one of these. Use `--prompt "Review the X implementation"` with `--job`.
1143
+ - **`--job` without `--bead`** → reviewer/executor requires `--bead`. Create a reviewer bead first, then use `--bead <review-bead> --job <exec-job> --context-depth 2`.
1054
1144
  - **Stale specialist processes** → SessionStart hook warns about old binary versions. Run `specialists clean --processes` to kill them all.
1055
1145
  - **`specialists init` fails with xtrm error** → xtrm must be installed first: `npm install -g xtrm-tools && xt install`. Use `--no-xtrm-check` in CI.
1056
1146
  - **Skill drift detected by doctor** → Run `specialists init --sync-skills` to re-sync canonical skills to `.xtrm/skills/default/` and refresh active symlinks.
@@ -29866,8 +29866,8 @@ function resolveOptionalTierRoot(skillsRoot) {
29866
29866
  function resolveUserPacksRoot(skillsRoot) {
29867
29867
  return import_node_path.default.join(skillsRoot, "user", "packs");
29868
29868
  }
29869
- function resolveActiveRuntimeRoot(skillsRoot, runtime) {
29870
- return import_node_path.default.join(skillsRoot, "active", runtime);
29869
+ function resolveActiveRuntimeRoot(skillsRoot, _runtime) {
29870
+ return import_node_path.default.join(skillsRoot, "active");
29871
29871
  }
29872
29872
  function resolveStateFilePath(skillsRoot) {
29873
29873
  return import_node_path.default.join(skillsRoot, STATE_FILE_NAME);
@@ -43891,13 +43891,6 @@ async function setRuntimeEnabledPacks(skillsRoot, runtime, packNames) {
43891
43891
  }
43892
43892
 
43893
43893
  // src/core/skills-materializer.ts
43894
- var PI_NPM_PROVIDED_SKILLS = /* @__PURE__ */ new Set([
43895
- "gitnexus-debugging",
43896
- "gitnexus-exploring",
43897
- "gitnexus-impact-analysis",
43898
- "gitnexus-pr-review",
43899
- "gitnexus-refactoring"
43900
- ]);
43901
43894
  function sortByName(entries) {
43902
43895
  return [...entries].sort((a, b) => a.name.localeCompare(b.name));
43903
43896
  }
@@ -43936,12 +43929,11 @@ async function selectRuntimeSkills(runtime, skillsRoot) {
43936
43929
  const defaultSkills = await discoverDefaultSkills(skillsRoot);
43937
43930
  const enabledPackSkills = await collectEnabledPackSkills(skillsRoot, enabledPacks);
43938
43931
  const allSkills = sortByName([...defaultSkills, ...enabledPackSkills]);
43939
- const filteredSkills = runtime === "pi" ? allSkills.filter((s) => !PI_NPM_PROVIDED_SKILLS.has(s.name)) : allSkills;
43940
- assertNoRuntimeCollisions(runtime, filteredSkills);
43932
+ assertNoRuntimeCollisions(runtime, allSkills);
43941
43933
  return {
43942
43934
  runtime,
43943
43935
  enabledPacks: [...enabledPacks],
43944
- skills: filteredSkills
43936
+ skills: allSkills
43945
43937
  };
43946
43938
  }
43947
43939
  async function buildRuntimeTempView(runtime, skillsRoot, selectedSkills) {
@@ -44017,11 +44009,31 @@ async function rebuildRuntimeActiveView(runtime, skillsRoot) {
44017
44009
  };
44018
44010
  }
44019
44011
  async function rebuildAllRuntimeActiveViews(skillsRoot) {
44020
- const results = [];
44021
- for (const runtime of SKILLS_RUNTIMES) {
44022
- results.push(await rebuildRuntimeActiveView(runtime, skillsRoot));
44012
+ const state = await readSkillsState(skillsRoot);
44013
+ const mergedEnabledPacks = [.../* @__PURE__ */ new Set([
44014
+ ...state.enabledPacks.claude,
44015
+ ...state.enabledPacks.pi
44016
+ ])].sort((a, b) => a.localeCompare(b));
44017
+ const defaultSkills = await discoverDefaultSkills(skillsRoot);
44018
+ const enabledPackSkills = await collectEnabledPackSkills(skillsRoot, mergedEnabledPacks);
44019
+ const mergedSkills = sortByName([...defaultSkills, ...enabledPackSkills]);
44020
+ assertNoRuntimeCollisions("claude", mergedSkills);
44021
+ const activeRuntimeRoot = resolveActiveRuntimeRoot(skillsRoot, "claude");
44022
+ await import_fs_extra6.default.ensureDir(import_node_path4.default.dirname(activeRuntimeRoot));
44023
+ const nonSymlinkEntryNames = await findNonSymlinkEntries(activeRuntimeRoot);
44024
+ if (nonSymlinkEntryNames.length > 0) {
44025
+ console.log(
44026
+ `[xtrm] Warning: ${activeRuntimeRoot} contains non-symlink entries (${nonSymlinkEntryNames.join(", ")}). These entries will be evicted during runtime view rebuild. Do not write skills to .claude/skills directly; write to .xtrm/skills/default or packs.`
44027
+ );
44023
44028
  }
44024
- return results;
44029
+ const tempRoot = await buildRuntimeTempView("claude", skillsRoot, mergedSkills);
44030
+ await atomicSwapDirectory(tempRoot, activeRuntimeRoot);
44031
+ return [{
44032
+ runtime: "claude",
44033
+ enabledPackCount: mergedEnabledPacks.length,
44034
+ discoveredSkillCount: mergedSkills.length,
44035
+ symlinkNames: mergedSkills.map((skill) => skill.name)
44036
+ }];
44025
44037
  }
44026
44038
 
44027
44039
  // src/core/skills-scaffold.ts
@@ -44062,16 +44074,16 @@ async function ensureAgentsSkillsSymlink(projectRoot) {
44062
44074
  throw new Error(`Skills invariants failed. ${summary}`);
44063
44075
  }
44064
44076
  const materializedViews = await rebuildAllRuntimeActiveViews(skillsRoot);
44065
- const activatedClaudeSkills = materializedViews.find((view) => view.runtime === "claude")?.discoveredSkillCount ?? 0;
44066
- const activatedPiSkills = materializedViews.find((view) => view.runtime === "pi")?.discoveredSkillCount ?? 0;
44077
+ const activatedClaudeSkills = materializedViews[0]?.discoveredSkillCount ?? 0;
44078
+ const activatedPiSkills = activatedClaudeSkills;
44067
44079
  await ensureSkillsSymlink(
44068
44080
  import_path3.default.join(projectRoot, ".claude", "skills"),
44069
- import_path3.default.join("..", ".xtrm", "skills", "active", "claude"),
44081
+ import_path3.default.join("..", ".xtrm", "skills", "active"),
44070
44082
  ".claude/skills"
44071
44083
  );
44072
44084
  const agentsSkillsPath = import_path3.default.join(projectRoot, ".agents", "skills");
44073
44085
  if (await import_fs_extra7.default.pathExists(agentsSkillsPath)) {
44074
- console.log(kleur_default.dim(" \u25CB .agents/skills is deprecated; runtime skills are generated under .xtrm/skills/active/*"));
44086
+ console.log(kleur_default.dim(" \u25CB .agents/skills is deprecated; runtime skills are generated under .xtrm/skills/active"));
44075
44087
  }
44076
44088
  return {
44077
44089
  activatedClaudeSkills,
@@ -44221,7 +44233,7 @@ async function launchWorktreeSession(opts) {
44221
44233
  const message = error48 instanceof Error ? error48.message : String(error48);
44222
44234
  console.log(kleur_default.dim(` warning: could not rebuild active Claude skills view (${message})`));
44223
44235
  const wtSkillsDir = import_node_path5.default.join(claudeDir, "skills");
44224
- const claudeSkillsTarget = import_node_path5.default.join("..", ".xtrm", "skills", "active", "claude");
44236
+ const claudeSkillsTarget = import_node_path5.default.join("..", ".xtrm", "skills", "active");
44225
44237
  try {
44226
44238
  const existing = (0, import_node_fs.lstatSync)(wtSkillsDir);
44227
44239
  if (!existing.isSymbolicLink() || (0, import_node_fs.readlinkSync)(wtSkillsDir) !== claudeSkillsTarget) {
@@ -44807,7 +44819,7 @@ var PI_AGENT_DIR = process.env.PI_AGENT_DIR || import_path4.default.join((0, imp
44807
44819
  var PI_MCP_ADAPTER_OVERRIDE_DIR = import_path4.default.join(PI_AGENT_DIR, "extensions", "pi-mcp-adapter");
44808
44820
  var PI_MCP_ADAPTER_REQUIRED_ENTRY = "commands.js";
44809
44821
  var PROJECT_EXTENSIONS_ENTRY = "../.xtrm/extensions";
44810
- var PROJECT_SKILLS_ENTRY = "../.xtrm/skills/active/pi";
44822
+ var PROJECT_SKILLS_ENTRY = "../.xtrm/skills/active";
44811
44823
  var PROJECT_EXTENSION_PACKAGE_ID = "npm:@jaggerxtrm/pi-extensions";
44812
44824
  var CONFLICTING_PI_PACKAGE_IDS = /* @__PURE__ */ new Set(["npm:pi-dex"]);
44813
44825
  var LEGACY_PROJECT_EXTENSION_ENTRIES = /* @__PURE__ */ new Set([
@@ -46555,8 +46567,8 @@ var import_path10 = __toESM(require("path"), 1);
46555
46567
  // src/core/skills-runtime-views.ts
46556
46568
  var import_fs_extra15 = __toESM(require_lib(), 1);
46557
46569
  var import_node_path8 = __toESM(require("path"), 1);
46558
- var CLAUDE_POINTER_TARGET = import_node_path8.default.join("..", ".xtrm", "skills", "active", "claude");
46559
- var PI_SKILLS_ENTRY = "../.xtrm/skills/active/pi";
46570
+ var CLAUDE_POINTER_TARGET = import_node_path8.default.join("..", ".xtrm", "skills", "active");
46571
+ var PI_SKILLS_ENTRY = "../.xtrm/skills/active";
46560
46572
  async function readSymlinkTarget(linkPath) {
46561
46573
  const stat = await import_fs_extra15.default.lstat(linkPath).catch(() => null);
46562
46574
  if (!stat?.isSymbolicLink()) {
@@ -46597,32 +46609,26 @@ async function hasPiSkillsPointer(projectRoot) {
46597
46609
  return Array.isArray(settings.skills) && settings.skills.includes(PI_SKILLS_ENTRY);
46598
46610
  }
46599
46611
  async function checkRuntimeSkillsViews(projectRoot) {
46600
- const activeClaudeRoot = import_node_path8.default.join(projectRoot, ".xtrm", "skills", "active", "claude");
46601
- const activePiRoot = import_node_path8.default.join(projectRoot, ".xtrm", "skills", "active", "pi");
46602
- const activeClaudeEntries = await listRuntimeEntries(activeClaudeRoot);
46603
- const activePiEntries = await listRuntimeEntries(activePiRoot);
46604
- const activeClaudeReady = activeClaudeEntries.length > 0 && await hasOnlyValidSymlinkEntries(activeClaudeRoot, activeClaudeEntries);
46605
- const activePiReady = activePiEntries.length > 0 && await hasOnlyValidSymlinkEntries(activePiRoot, activePiEntries);
46612
+ const activeRoot = import_node_path8.default.join(projectRoot, ".xtrm", "skills", "active");
46613
+ const activeEntries = await listRuntimeEntries(activeRoot);
46614
+ const activeReady = activeEntries.length > 0 && await hasOnlyValidSymlinkEntries(activeRoot, activeEntries);
46606
46615
  const claudePointerReady = await readSymlinkTarget(import_node_path8.default.join(projectRoot, ".claude", "skills")) === CLAUDE_POINTER_TARGET;
46607
46616
  const piPointerReady = await hasPiSkillsPointer(projectRoot);
46608
46617
  const hasDeprecatedAgentsSkillsPath = await import_fs_extra15.default.pathExists(import_node_path8.default.join(projectRoot, ".agents", "skills"));
46609
46618
  return {
46610
- activeClaudeReady,
46611
- activePiReady,
46619
+ activeReady,
46612
46620
  claudePointerReady,
46613
46621
  piPointerReady,
46614
- activeClaudeEntries,
46615
- activePiEntries,
46622
+ activeEntries,
46616
46623
  hasDeprecatedAgentsSkillsPath
46617
46624
  };
46618
46625
  }
46619
46626
  async function assertRuntimeSkillsViews(projectRoot) {
46620
46627
  const check2 = await checkRuntimeSkillsViews(projectRoot);
46621
46628
  const failures = [];
46622
- if (!check2.activeClaudeReady) failures.push("active claude view is missing, empty, or contains invalid links");
46623
- if (!check2.activePiReady) failures.push("active pi view is missing, empty, or contains invalid links");
46624
- if (!check2.claudePointerReady) failures.push(".claude/skills is not linked to ../.xtrm/skills/active/claude");
46625
- if (!check2.piPointerReady) failures.push(".pi/settings.json.skills does not include ../.xtrm/skills/active/pi");
46629
+ if (!check2.activeReady) failures.push("active view is missing, empty, or contains invalid links");
46630
+ if (!check2.claudePointerReady) failures.push(".claude/skills is not linked to ../.xtrm/skills/active");
46631
+ if (!check2.piPointerReady) failures.push(".pi/settings.json.skills does not include ../.xtrm/skills/active");
46626
46632
  if (failures.length > 0) {
46627
46633
  throw new Error(`Runtime skills view validation failed: ${failures.join("; ")}`);
46628
46634
  }
@@ -46734,7 +46740,7 @@ async function runInitVerification(projectRoot) {
46734
46740
  const piPlan = await verifyPiRuntime(projectRoot);
46735
46741
  const projectResult = verifyProjectBootstrap(projectRoot);
46736
46742
  const skillsRuntimeResult = await checkRuntimeSkillsViews(projectRoot);
46737
- const allPassed = machinePlan.allRequiredPresent && claudeResult.hooksWired && piPlan.allRequiredPresent && skillsRuntimeResult.activeClaudeReady && skillsRuntimeResult.activePiReady && skillsRuntimeResult.claudePointerReady && skillsRuntimeResult.piPointerReady && projectResult.beadsInitialized;
46743
+ const allPassed = machinePlan.allRequiredPresent && claudeResult.hooksWired && piPlan.allRequiredPresent && skillsRuntimeResult.activeReady && skillsRuntimeResult.claudePointerReady && skillsRuntimeResult.piPointerReady && projectResult.beadsInitialized;
46738
46744
  return {
46739
46745
  machineBootstrap: {
46740
46746
  allRequiredPresent: machinePlan.allRequiredPresent,
@@ -46747,8 +46753,7 @@ async function runInitVerification(projectRoot) {
46747
46753
  missingPackages: piPlan.missingPackages.filter((s) => s.pkg.required).map((s) => s.pkg.displayName)
46748
46754
  },
46749
46755
  skillsRuntime: {
46750
- activeClaudeReady: skillsRuntimeResult.activeClaudeReady,
46751
- activePiReady: skillsRuntimeResult.activePiReady,
46756
+ activeReady: skillsRuntimeResult.activeReady,
46752
46757
  claudePointerReady: skillsRuntimeResult.claudePointerReady,
46753
46758
  piPointerReady: skillsRuntimeResult.piPointerReady,
46754
46759
  hasDeprecatedAgentsSkillsPath: skillsRuntimeResult.hasDeprecatedAgentsSkillsPath
@@ -46790,8 +46795,7 @@ function renderVerificationSummary(result) {
46790
46795
  console.log(` ${prIcon} ${prLabel} \u2014 ${parts.join("; ")}`);
46791
46796
  }
46792
46797
  const skillsParts = [];
46793
- if (!result.skillsRuntime.activeClaudeReady) skillsParts.push("active/claude");
46794
- if (!result.skillsRuntime.activePiReady) skillsParts.push("active/pi");
46798
+ if (!result.skillsRuntime.activeReady) skillsParts.push("active");
46795
46799
  if (!result.skillsRuntime.claudePointerReady) skillsParts.push(".claude/skills pointer");
46796
46800
  if (!result.skillsRuntime.piPointerReady) skillsParts.push(".pi settings skills pointer");
46797
46801
  const srIcon = skillsParts.length === 0 ? sym.ok : sym.warn;