@jaggerxtrm/specialists 3.7.1 → 3.10.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 (193) hide show
  1. package/README.md +23 -9
  2. package/config/mandatory-rules/README.md +113 -0
  3. package/config/mandatory-rules/gitnexus-required.md +12 -1
  4. package/config/mandatory-rules/serena-cheatsheet.md +41 -0
  5. package/config/skills/specialists-creator/SKILL.md +6 -32
  6. package/config/skills/specialists-creator/scripts/audit-spec-uniformity.mjs +86 -0
  7. package/config/skills/specialists-creator/scripts/scaffold-specialist.ts +1 -6
  8. package/config/skills/update-specialists/SKILL.md +83 -8
  9. package/config/skills/using-specialists/SKILL.md +13 -0
  10. package/config/skills/using-specialists-v2/SKILL.md +639 -0
  11. package/config/specialists/debugger.specialist.json +7 -9
  12. package/config/specialists/executor.specialist.json +5 -6
  13. package/config/specialists/explorer.specialist.json +3 -7
  14. package/config/specialists/memory-processor.specialist.json +8 -9
  15. package/config/specialists/node-coordinator.specialist.json +1 -5
  16. package/config/specialists/overthinker.specialist.json +7 -13
  17. package/config/specialists/planner.specialist.json +12 -12
  18. package/config/specialists/researcher.specialist.json +9 -14
  19. package/config/specialists/reviewer.specialist.json +8 -8
  20. package/config/specialists/specialists-creator.specialist.json +7 -2
  21. package/config/specialists/sync-docs.specialist.json +7 -11
  22. package/config/specialists/test-runner.specialist.json +4 -13
  23. package/config/specialists/xt-merge.specialist.json +3 -5
  24. package/dist/index.js +3066 -1368
  25. package/dist/lib.js +13219 -0
  26. package/dist/types/cli/attach.d.ts +2 -0
  27. package/dist/types/cli/attach.d.ts.map +1 -0
  28. package/dist/types/cli/clean.d.ts +2 -0
  29. package/dist/types/cli/clean.d.ts.map +1 -0
  30. package/dist/types/cli/config.d.ts +2 -0
  31. package/dist/types/cli/config.d.ts.map +1 -0
  32. package/dist/types/cli/db.d.ts +2 -0
  33. package/dist/types/cli/db.d.ts.map +1 -0
  34. package/dist/types/cli/doctor.d.ts +15 -0
  35. package/dist/types/cli/doctor.d.ts.map +1 -0
  36. package/dist/types/cli/edit.d.ts +2 -0
  37. package/dist/types/cli/edit.d.ts.map +1 -0
  38. package/dist/types/cli/end.d.ts +2 -0
  39. package/dist/types/cli/end.d.ts.map +1 -0
  40. package/dist/types/cli/epic.d.ts +8 -0
  41. package/dist/types/cli/epic.d.ts.map +1 -0
  42. package/dist/types/cli/feed.d.ts +19 -0
  43. package/dist/types/cli/feed.d.ts.map +1 -0
  44. package/dist/types/cli/follow-up.d.ts +2 -0
  45. package/dist/types/cli/follow-up.d.ts.map +1 -0
  46. package/dist/types/cli/format-helpers.d.ts +108 -0
  47. package/dist/types/cli/format-helpers.d.ts.map +1 -0
  48. package/dist/types/cli/help.d.ts +2 -0
  49. package/dist/types/cli/help.d.ts.map +1 -0
  50. package/dist/types/cli/init.d.ts +10 -0
  51. package/dist/types/cli/init.d.ts.map +1 -0
  52. package/dist/types/cli/install.d.ts +2 -0
  53. package/dist/types/cli/install.d.ts.map +1 -0
  54. package/dist/types/cli/list-rules.d.ts +2 -0
  55. package/dist/types/cli/list-rules.d.ts.map +1 -0
  56. package/dist/types/cli/list.d.ts +13 -0
  57. package/dist/types/cli/list.d.ts.map +1 -0
  58. package/dist/types/cli/memory.d.ts +2 -0
  59. package/dist/types/cli/memory.d.ts.map +1 -0
  60. package/dist/types/cli/merge.d.ts +79 -0
  61. package/dist/types/cli/merge.d.ts.map +1 -0
  62. package/dist/types/cli/models.d.ts +2 -0
  63. package/dist/types/cli/models.d.ts.map +1 -0
  64. package/dist/types/cli/node.d.ts +2 -0
  65. package/dist/types/cli/node.d.ts.map +1 -0
  66. package/dist/types/cli/poll.d.ts +25 -0
  67. package/dist/types/cli/poll.d.ts.map +1 -0
  68. package/dist/types/cli/ps.d.ts +2 -0
  69. package/dist/types/cli/ps.d.ts.map +1 -0
  70. package/dist/types/cli/quickstart.d.ts +2 -0
  71. package/dist/types/cli/quickstart.d.ts.map +1 -0
  72. package/dist/types/cli/result.d.ts +2 -0
  73. package/dist/types/cli/result.d.ts.map +1 -0
  74. package/dist/types/cli/resume.d.ts +2 -0
  75. package/dist/types/cli/resume.d.ts.map +1 -0
  76. package/dist/types/cli/run.d.ts +2 -0
  77. package/dist/types/cli/run.d.ts.map +1 -0
  78. package/dist/types/cli/script.d.ts +23 -0
  79. package/dist/types/cli/script.d.ts.map +1 -0
  80. package/dist/types/cli/serve.d.ts +17 -0
  81. package/dist/types/cli/serve.d.ts.map +1 -0
  82. package/dist/types/cli/setup.d.ts +2 -0
  83. package/dist/types/cli/setup.d.ts.map +1 -0
  84. package/dist/types/cli/status.d.ts +4 -0
  85. package/dist/types/cli/status.d.ts.map +1 -0
  86. package/dist/types/cli/steer.d.ts +2 -0
  87. package/dist/types/cli/steer.d.ts.map +1 -0
  88. package/dist/types/cli/stop.d.ts +2 -0
  89. package/dist/types/cli/stop.d.ts.map +1 -0
  90. package/dist/types/cli/tmux-utils.d.ts +23 -0
  91. package/dist/types/cli/tmux-utils.d.ts.map +1 -0
  92. package/dist/types/cli/validate.d.ts +11 -0
  93. package/dist/types/cli/validate.d.ts.map +1 -0
  94. package/dist/types/cli/version.d.ts +2 -0
  95. package/dist/types/cli/version.d.ts.map +1 -0
  96. package/dist/types/cli/view.d.ts +2 -0
  97. package/dist/types/cli/view.d.ts.map +1 -0
  98. package/dist/types/constants.d.ts +140 -0
  99. package/dist/types/constants.d.ts.map +1 -0
  100. package/dist/types/index.d.ts +8 -0
  101. package/dist/types/index.d.ts.map +1 -0
  102. package/dist/types/lib.d.ts +5 -0
  103. package/dist/types/lib.d.ts.map +1 -0
  104. package/dist/types/pi/backendMap.d.ts +3 -0
  105. package/dist/types/pi/backendMap.d.ts.map +1 -0
  106. package/dist/types/pi/session.d.ts +209 -0
  107. package/dist/types/pi/session.d.ts.map +1 -0
  108. package/dist/types/server.d.ts +10 -0
  109. package/dist/types/server.d.ts.map +1 -0
  110. package/dist/types/specialist/beads.d.ts +60 -0
  111. package/dist/types/specialist/beads.d.ts.map +1 -0
  112. package/dist/types/specialist/chain-identity.d.ts +18 -0
  113. package/dist/types/specialist/chain-identity.d.ts.map +1 -0
  114. package/dist/types/specialist/epic-lifecycle.d.ts +57 -0
  115. package/dist/types/specialist/epic-lifecycle.d.ts.map +1 -0
  116. package/dist/types/specialist/epic-readiness.d.ts +55 -0
  117. package/dist/types/specialist/epic-readiness.d.ts.map +1 -0
  118. package/dist/types/specialist/epic-reconciler.d.ts +35 -0
  119. package/dist/types/specialist/epic-reconciler.d.ts.map +1 -0
  120. package/dist/types/specialist/hooks.d.ts +44 -0
  121. package/dist/types/specialist/hooks.d.ts.map +1 -0
  122. package/dist/types/specialist/job-control.d.ts +29 -0
  123. package/dist/types/specialist/job-control.d.ts.map +1 -0
  124. package/dist/types/specialist/job-file-output.d.ts +4 -0
  125. package/dist/types/specialist/job-file-output.d.ts.map +1 -0
  126. package/dist/types/specialist/job-root.d.ts +21 -0
  127. package/dist/types/specialist/job-root.d.ts.map +1 -0
  128. package/dist/types/specialist/jobRegistry.d.ts +69 -0
  129. package/dist/types/specialist/jobRegistry.d.ts.map +1 -0
  130. package/dist/types/specialist/json-output.d.ts +2 -0
  131. package/dist/types/specialist/json-output.d.ts.map +1 -0
  132. package/dist/types/specialist/loader.d.ts +48 -0
  133. package/dist/types/specialist/loader.d.ts.map +1 -0
  134. package/dist/types/specialist/mandatory-rules.d.ts +41 -0
  135. package/dist/types/specialist/mandatory-rules.d.ts.map +1 -0
  136. package/dist/types/specialist/memory-retrieval.d.ts +34 -0
  137. package/dist/types/specialist/memory-retrieval.d.ts.map +1 -0
  138. package/dist/types/specialist/model-display.d.ts +4 -0
  139. package/dist/types/specialist/model-display.d.ts.map +1 -0
  140. package/dist/types/specialist/node-contract.d.ts +343 -0
  141. package/dist/types/specialist/node-contract.d.ts.map +1 -0
  142. package/dist/types/specialist/node-resolve.d.ts +5 -0
  143. package/dist/types/specialist/node-resolve.d.ts.map +1 -0
  144. package/dist/types/specialist/node-supervisor.d.ts +198 -0
  145. package/dist/types/specialist/node-supervisor.d.ts.map +1 -0
  146. package/dist/types/specialist/observability-db.d.ts +19 -0
  147. package/dist/types/specialist/observability-db.d.ts.map +1 -0
  148. package/dist/types/specialist/observability-sqlite.d.ts +218 -0
  149. package/dist/types/specialist/observability-sqlite.d.ts.map +1 -0
  150. package/dist/types/specialist/pipeline.d.ts +23 -0
  151. package/dist/types/specialist/pipeline.d.ts.map +1 -0
  152. package/dist/types/specialist/process-liveness.d.ts +2 -0
  153. package/dist/types/specialist/process-liveness.d.ts.map +1 -0
  154. package/dist/types/specialist/runner.d.ts +98 -0
  155. package/dist/types/specialist/runner.d.ts.map +1 -0
  156. package/dist/types/specialist/schema.d.ts +2875 -0
  157. package/dist/types/specialist/schema.d.ts.map +1 -0
  158. package/dist/types/specialist/script-runner.d.ts +47 -0
  159. package/dist/types/specialist/script-runner.d.ts.map +1 -0
  160. package/dist/types/specialist/supervisor.d.ts +142 -0
  161. package/dist/types/specialist/supervisor.d.ts.map +1 -0
  162. package/dist/types/specialist/templateEngine.d.ts +2 -0
  163. package/dist/types/specialist/templateEngine.d.ts.map +1 -0
  164. package/dist/types/specialist/timeline-events.d.ts +563 -0
  165. package/dist/types/specialist/timeline-events.d.ts.map +1 -0
  166. package/dist/types/specialist/timeline-query.d.ts +129 -0
  167. package/dist/types/specialist/timeline-query.d.ts.map +1 -0
  168. package/dist/types/specialist/worktree-gc.d.ts +24 -0
  169. package/dist/types/specialist/worktree-gc.d.ts.map +1 -0
  170. package/dist/types/specialist/worktree.d.ts +69 -0
  171. package/dist/types/specialist/worktree.d.ts.map +1 -0
  172. package/dist/types/tools/specialist/feed_specialist.tool.d.ts +51 -0
  173. package/dist/types/tools/specialist/feed_specialist.tool.d.ts.map +1 -0
  174. package/dist/types/tools/specialist/list_specialists.tool.d.ts +28 -0
  175. package/dist/types/tools/specialist/list_specialists.tool.d.ts.map +1 -0
  176. package/dist/types/tools/specialist/resume_specialist.tool.d.ts +46 -0
  177. package/dist/types/tools/specialist/resume_specialist.tool.d.ts.map +1 -0
  178. package/dist/types/tools/specialist/specialist_init.tool.d.ts +26 -0
  179. package/dist/types/tools/specialist/specialist_init.tool.d.ts.map +1 -0
  180. package/dist/types/tools/specialist/specialist_status.tool.d.ts +33 -0
  181. package/dist/types/tools/specialist/specialist_status.tool.d.ts.map +1 -0
  182. package/dist/types/tools/specialist/steer_specialist.tool.d.ts +38 -0
  183. package/dist/types/tools/specialist/steer_specialist.tool.d.ts.map +1 -0
  184. package/dist/types/tools/specialist/stop_specialist.tool.d.ts +31 -0
  185. package/dist/types/tools/specialist/stop_specialist.tool.d.ts.map +1 -0
  186. package/dist/types/tools/specialist/use_specialist.tool.d.ts +93 -0
  187. package/dist/types/tools/specialist/use_specialist.tool.d.ts.map +1 -0
  188. package/dist/types/utils/circuitBreaker.d.ts +19 -0
  189. package/dist/types/utils/circuitBreaker.d.ts.map +1 -0
  190. package/dist/types/utils/logger.d.ts +11 -0
  191. package/dist/types/utils/logger.d.ts.map +1 -0
  192. package/package.json +13 -2
  193. package/config/specialists/parallel-review.specialist.json +0 -65
package/README.md CHANGED
@@ -47,7 +47,9 @@ specialists merge <bead-id> # single chain or epic (topological)
47
47
  specialists merge <bead-id> --rebuild # rebuild after merge
48
48
  ```
49
49
 
50
- `specialists run` prints `[job started: <id>]` early and also writes the id to `.specialists/jobs/latest`.
50
+ `specialists run` prints `[job started: <id>]` early. Normal runtime is DB-backed; `.specialists/jobs/latest` is legacy/operator-only.
51
+
52
+ Runtime state lives in `observability.db`; `.specialists/jobs/latest` is legacy convenience pointer only.
51
53
 
52
54
  Ad-hoc work:
53
55
 
@@ -77,6 +79,9 @@ specialists doctor
77
79
  | Need | Doc |
78
80
  |---|---|
79
81
  | Install and bootstrap a project | [docs/bootstrap.md](docs/bootstrap.md) |
82
+ | Run a script-class specialist over HTTP (`sp serve`) — overview & contract | [docs/specialists-service.md](docs/specialists-service.md) |
83
+ | Install `sp serve` in another project (sidecar Docker / Podman) | [docs/specialists-service-install.md](docs/specialists-service-install.md) |
84
+ | Build & publish the specialists-service image | [docs/release-image.md](docs/release-image.md) |
80
85
  | Bead-first workflow and semantics | [docs/workflow.md](docs/workflow.md) |
81
86
  | CLI commands and flags | [docs/cli-reference.md](docs/cli-reference.md) |
82
87
  | Background jobs, feed, result, stop | [docs/background-jobs.md](docs/background-jobs.md) |
@@ -90,25 +95,34 @@ specialists doctor
90
95
  | Pi subprocess isolation and extensions | [docs/pi-session.md](docs/pi-session.md) |
91
96
  | NodeSupervisor architecture, node lifecycle, and `sp node` CLI | [docs/nodes.md](docs/nodes.md) |
92
97
 
98
+ ## Ownership model
99
+
100
+ Specialists uses layered ownership with deterministic loader precedence: user layer overrides default layer, and default layer falls back to package source (`.specialists/user/*` > `.specialists/default/*` > `config/*`). Operationally: `config/*` is upstream source shipped by package, `.specialists/default/*` is managed mirror refreshed by `specialists init --sync-defaults` (scope: specialists + mandatory-rules + nodes), `.specialists/user/*` is repo customization layer, and `.specialists/{jobs,ready,db}` is runtime/generated state; `.specialists/jobs/` is legacy mirror/debug surface, not normal-runtime source of truth. Use `sp edit --fork-from <base>` to promote non-user specialist into user layer before editing.
101
+
93
102
  ## Project structure
94
103
 
95
104
  ```text
96
105
  config/
97
- ├── specialists/ canonical specialist definitions (.specialist.yaml)
98
- ├── hooks/ bundled hook scripts
99
- ├── skills/ repo-local skills used by specialists
100
- └── extensions/ pi extensions (future)
106
+ ├── specialists/ canonical specialist definitions (.specialist.json)
107
+ ├── mandatory-rules/ canonical rule sets injected into specialist prompts (+ README)
108
+ ├── nodes/ canonical node configs
109
+ ├── hooks/ bundled hook scripts
110
+ ├── skills/ repo-local skills used by specialists
111
+ └── extensions/ pi extensions (future)
101
112
  .specialists/
102
- ├── default/ canonical assets (from init)
113
+ ├── default/ managed mirror of canonical (from sp init --sync-defaults)
103
114
  │ ├── specialists/
115
+ │ ├── mandatory-rules/
116
+ │ ├── nodes/
104
117
  │ ├── hooks/
105
118
  │ └── skills/
106
- ├── user/ custom additions
119
+ ├── user/ repo-owned customizations (overrides default + canonical)
107
120
  │ ├── specialists/
108
121
  │ ├── hooks/
109
122
  │ └── skills/
110
- ├── jobs/ runtime gitignored
111
- └── ready/ runtime — gitignored
123
+ ├── mandatory-rules/ repo-specific rule overlay (wins on set-id conflict)
124
+ ├── jobs/ runtime — gitignored
125
+ └── ready/ runtime — gitignored
112
126
  src/ CLI, server, loader, runner, tools
113
127
  ```
114
128
 
@@ -0,0 +1,113 @@
1
+ # MANDATORY_RULES
2
+
3
+ Rule sets injected at the end of every specialist prompt at spawn time. Enforces
4
+ behaviors the model must follow regardless of the specific task.
5
+
6
+ > **Quick introspection:** `sp list-rules` prints the rule × specialist matrix.
7
+ > Filter with `sp list-rules --rule <id>` or `sp list-rules --specialist <name>`,
8
+ > machine output via `--json`.
9
+
10
+ ## Layout (three tiers)
11
+
12
+ The loader reads and unions indexes from three paths, in this precedence:
13
+
14
+ | Tier | Path | Writer | Role |
15
+ |------|------|--------|------|
16
+ | 1. Source | `config/mandatory-rules/` | specialists repo commits | Canonical source of truth. Ships with the tool. |
17
+ | 2. Canonical copy | `.specialists/default/mandatory-rules/` | `sp init --sync-defaults` | Mirror of canonical, placed in every downstream project. |
18
+ | 3. Overlay | `.specialists/mandatory-rules/` | you (per-repo) | Repo-specific additions and overrides. Wins on set-id conflict. |
19
+
20
+ A rule set defined in tier 3 overrides a same-id rule set from tier 2 or 1,
21
+ letting a repo tailor or replace canonical rules without editing the source.
22
+
23
+ ## What gets injected
24
+
25
+ At specialist spawn, the runner resolves sets from:
26
+
27
+ 1. `required_template_sets` — always loaded
28
+ 2. `default_template_sets` — loaded unless `mandatory_rules.disable_default_globals: true` on the specialist
29
+ 3. `specialist.mandatory_rules.template_sets` — per-specialist additions
30
+ 4. `specialist.mandatory_rules.inline_rules` — per-specialist inline rules (no file)
31
+
32
+ The resulting block is appended to the rendered task prompt as
33
+ `## MANDATORY_RULES` with one `### <set-id>` section per set.
34
+
35
+ ## Authoring a rule set
36
+
37
+ Create `<your-set>.md` in one of the three tiers. File format:
38
+
39
+ ```markdown
40
+ ---
41
+ name: <your-set>
42
+ kind: mandatory-rule
43
+ rules:
44
+ - id: my-rule-1
45
+ level: required
46
+ text: "Single-line rule text. Use quotes if it contains colons."
47
+ - id: my-rule-2
48
+ level: warn
49
+ text: "Another rule."
50
+ when: "optional guard, e.g. 'node > 20'"
51
+ ---
52
+ ```
53
+
54
+ - **`id`** — stable identifier, shown in the rendered block. Auto-filled from `<set-id>-<n>` if omitted.
55
+ - **`level`** — `required`, `error`, `warn`, `info`. Display only; the model treats them as priority hints.
56
+ - **`text`** — one-line rule (multi-line supported via YAML `|` block).
57
+ - **`when`** — optional conditional context.
58
+
59
+ **Shorthand**: a file with only a body (no `rules:` frontmatter) is loaded as
60
+ a single rule with `level: required` and the body as `text`.
61
+
62
+ ## Wiring a set
63
+
64
+ ### Option 1 — via index.json (applies to all specialists)
65
+
66
+ Add the set id to the index in the tier you're writing to. Tier-3 example:
67
+
68
+ ```json
69
+ // .specialists/mandatory-rules/index.json
70
+ {
71
+ "required_template_sets": [],
72
+ "default_template_sets": ["my-repo-rule"]
73
+ }
74
+ ```
75
+
76
+ Index files are union-merged across tiers; dedup by set id.
77
+
78
+ ### Option 2 — per specialist
79
+
80
+ Reference the set id in the specialist's JSON:
81
+
82
+ ```json
83
+ // config/specialists/<name>.specialist.json
84
+ {
85
+ "specialist": {
86
+ "mandatory_rules": {
87
+ "template_sets": ["my-set"],
88
+ "inline_rules": [
89
+ { "id": "xtra-1", "level": "required", "text": "One-off rule inline." }
90
+ ],
91
+ "disable_default_globals": false
92
+ }
93
+ }
94
+ }
95
+ ```
96
+
97
+ ## Keep repo-specific rules out of canonical
98
+
99
+ Canonical rules (`config/` tier) ship to every downstream project. If a rule
100
+ only applies to this repo (e.g. "use bunx here"), put it in
101
+ `.specialists/mandatory-rules/` (tier 3). Other repos won't see it.
102
+
103
+ ## Budget
104
+
105
+ The full injection block is capped at ~2000 tokens (`src/specialist/runner.ts`).
106
+ Over-budget: the block is skipped and a warning is logged.
107
+
108
+ ## Debugging
109
+
110
+ - Loader warnings appear on stderr prefixed `[specialist runner]`.
111
+ - The supervisor emits a `mandatory_rules_injection` meta event on every run
112
+ with `sets_loaded`, `rules_count`, `inline_rules_count`, `token_estimate`.
113
+ - Inspect any run's injection: `sp ps <job-id>` shows the metadata.
@@ -2,4 +2,15 @@
2
2
  name: gitnexus-required
3
3
  kind: mandatory-rule
4
4
  ---
5
- Use GitNexus for symbol changes. Run impact before edit and detect_changes before finish.
5
+ Use GitNexus before editing any function/class/method.
6
+
7
+ Tools (prefer MCP; fall back to CLI if MCP unavailable):
8
+ - Blast radius before edit: `gitnexus_impact({target, direction:"upstream"})` or `npx gitnexus impact <target>`. STOP and warn if HIGH/CRITICAL.
9
+ - Symbol callers/callees: `gitnexus_context({name})` or `npx gitnexus context <name>`.
10
+ - Concept search: `gitnexus_query({query})` or `npx gitnexus query "<text>"`.
11
+ - Pre-commit scope check: `gitnexus_detect_changes()` (MCP only — fallback: `git diff --stat`).
12
+
13
+ Rules:
14
+ - Run impact for every symbol you modify; never edit without it.
15
+ - Never rename via find-replace — use `gitnexus_rename({symbol_name, new_name, dry_run:true})` first.
16
+ - If index is stale, ask the user to run `npx gitnexus analyze`.
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: serena-cheatsheet
3
+ kind: mandatory-rule
4
+ rules:
5
+ - id: prefer-serena
6
+ level: required
7
+ text: "Prefer Serena tools over read/grep/find/ls for source code (.ts .py .go .rs .js etc.). Native tools fine for .md .json .yaml configs."
8
+ - id: get_symbols_overview
9
+ level: required
10
+ text: "get_symbols_overview <path> — symbol skeleton of a file or dir (~300 tokens vs reading the whole file). Use first to pick what you actually need."
11
+ - id: find_symbol
12
+ level: required
13
+ text: "find_symbol <name_path> — fetch one symbol's body (or just signature). Drop-in replacement for read-then-scan-for-function workflows."
14
+ - id: find_referencing_symbols
15
+ level: required
16
+ text: "find_referencing_symbols <name_path> — who calls or uses this symbol across the codebase."
17
+ - id: search_for_pattern
18
+ level: required
19
+ text: "search_for_pattern <regex> — replaces grep. Returns matches with file:line context."
20
+ - id: find_file
21
+ level: required
22
+ text: "find_file <glob> and list_dir <path> — replace native find and ls."
23
+ - id: read_file
24
+ level: required
25
+ text: "read_file <path> — full or sliced read. Use only when navigation tools are not enough; check get_symbols_overview first."
26
+ - id: replace_symbol_body
27
+ level: required
28
+ text: "replace_symbol_body <name_path> — swap a function or class body in place. Use instead of Edit string-match for symbol-scoped changes (MEDIUM+ permission)."
29
+ - id: insert_around_symbol
30
+ level: required
31
+ text: "insert_before_symbol / insert_after_symbol <name_path> — add adjacent code such as imports or helpers (MEDIUM+ permission)."
32
+ - id: rename_symbol
33
+ level: required
34
+ text: "rename_symbol <name_path> <new_name> — refactor-safe across all references. Use instead of find-and-replace (MEDIUM+ permission)."
35
+ - id: replace_content
36
+ level: required
37
+ text: "replace_content <path> <pattern> <replacement> — line-range or regex edits when no symbol target fits (MEDIUM+ permission)."
38
+ - id: cost-rule
39
+ level: info
40
+ text: "Rule of thumb: read of a 500-line source file ~5000 tokens; find_symbol on one function ~200 tokens (~25x cheaper). Use get_symbols_overview before deciding."
41
+ ---
@@ -169,7 +169,7 @@ specialists models # confirm assignments look balanced
169
169
  node config/skills/specialists-creator/scripts/scaffold-specialist.ts config/specialists/my-specialist.specialist.json
170
170
 
171
171
  # 2. Apply a preset for common model/thinking defaults (optional but preferred)
172
- sp edit my-specialist --preset standard
172
+ sp edit my-specialist --preset medium
173
173
 
174
174
  # 3. Set individual fields via dot.path (primary mutation workflow)
175
175
  sp edit my-specialist specialist.metadata.name my-specialist
@@ -188,7 +188,7 @@ sp edit my-specialist specialist.prompt.task_template --file .tmp/task-template.
188
188
  sp view my-specialist
189
189
 
190
190
  # 6. Validate schema
191
- bun skills/specialist-author/scripts/validate-specialist.ts config/specialists/my-specialist.specialist.json
191
+ bun config/skills/specialists-creator/scripts/validate-specialist.ts config/specialists/my-specialist.specialist.json
192
192
  ```
193
193
 
194
194
  ---
@@ -383,8 +383,6 @@ planner — epic result:
383
383
 
384
384
  `run` accepts either a **file path** (`./scripts/foo.sh`, `~/scripts/foo.sh`) or a **shell command** (`bd ready`, `git status`). Pre-run validation checks that file paths exist and shell commands are on `PATH`. Shebang typos (e.g. `pytho` instead of `python`) are caught and reported as errors before the session starts.
385
385
 
386
- `path` is accepted as a deprecated alias for `run`.
387
-
388
386
  ### `specialist.capabilities` (optional)
389
387
 
390
388
  Informational declarations used by pre-run validation and future tooling (e.g. `specialists doctor`).
@@ -410,27 +408,6 @@ Informational declarations used by pre-run validation and future tooling (e.g. `
410
408
 
411
409
  Writes the final session output to this file path after the session completes. Relative to the working directory.
412
410
 
413
- ### `specialist.communication` (optional)
414
-
415
- ```json
416
- {
417
- "communication": {
418
- "next_specialists": "planner"
419
- }
420
- }
421
- ```
422
-
423
- Or as an array:
424
- ```json
425
- {
426
- "communication": {
427
- "next_specialists": ["planner", "test-runner"]
428
- }
429
- }
430
- ```
431
-
432
- `next_specialists` declares which specialist(s) should receive this specialist's output as `$previous_result`. Chaining is executed by the caller (e.g. `run_parallel` pipeline) — this field is declarative metadata.
433
-
434
411
  ### `specialist.validation` (optional)
435
412
 
436
413
  Drives the staleness detection shown in `specialists status` and `specialists list`.
@@ -507,7 +484,7 @@ Files listed under `skills.paths` are read and appended to the system prompt at
507
484
  {
508
485
  "skills": {
509
486
  "paths": [
510
- "skills/specialist-author/SKILL.md",
487
+ ".xtrm/skills/active/specialists-creator/SKILL.md",
511
488
  ".claude/agents.md"
512
489
  ]
513
490
  }
@@ -603,9 +580,6 @@ Scripts run **locally** (not inside the agent session):
603
580
  "required_tools": ["bash", "read"],
604
581
  "external_commands": ["git"]
605
582
  },
606
- "communication": {
607
- "next_specialists": ["sync-docs"]
608
- },
609
583
  "output_file": ".specialists/review.md",
610
584
  "beads_integration": "auto"
611
585
  }
@@ -708,7 +682,7 @@ pi --model <provider>/<fallback-model-id> --print "ping" # must return "pong"
708
682
  node config/skills/specialists-creator/scripts/scaffold-specialist.ts config/specialists/my-specialist.specialist.json
709
683
 
710
684
  # 3. Mutate with sp edit (dot.path + presets)
711
- sp edit my-specialist --preset standard
685
+ sp edit my-specialist --preset medium
712
686
  sp edit my-specialist specialist.execution.model <provider>/<primary-model-id>
713
687
  sp edit my-specialist specialist.execution.fallback_model <provider>/<fallback-model-id>
714
688
 
@@ -720,7 +694,7 @@ sp edit my-specialist specialist.prompt.task_template --file .tmp/task-template.
720
694
  sp view my-specialist
721
695
 
722
696
  # 6. Validate schema with the bundled helper
723
- bun skills/specialist-author/scripts/validate-specialist.ts config/specialists/my-specialist.specialist.json
697
+ bun config/skills/specialists-creator/scripts/validate-specialist.ts config/specialists/my-specialist.specialist.json
724
698
 
725
699
  # 7. List to confirm discovery
726
700
  specialists list
@@ -729,4 +703,4 @@ specialists list
729
703
  specialists run my-specialist --prompt "ping" --no-beads
730
704
  ```
731
705
 
732
- If you need the underlying implementation, read `skills/specialist-author/scripts/validate-specialist.ts`. It is a thin Bun/TypeScript wrapper over `parseSpecialist()` from `src/specialist/schema.ts`, which keeps the helper cross-platform for Windows, macOS, and Linux.
706
+ If you need the underlying implementation, read `config/skills/specialists-creator/scripts/validate-specialist.ts`. It is a thin Bun/TypeScript wrapper over `parseSpecialist()` from `src/specialist/schema.ts`, which keeps the helper cross-platform for Windows, macOS, and Linux.
@@ -0,0 +1,86 @@
1
+ // Audit every .specialist.json under config/specialists/ and .specialists/default/
2
+ // for: (1) schema parse failures, (2) unknown keys that survive .passthrough() silently.
3
+ //
4
+ // Usage (from repo root): bun config/skills/specialists-creator/scripts/audit-spec-uniformity.mjs
5
+ //
6
+ // Keep KNOWN sets in sync with src/specialist/schema.ts. If a sub-schema gains
7
+ // or drops a field, update this file in the same commit.
8
+
9
+ import { readFileSync, readdirSync } from 'node:fs';
10
+ import { join, resolve } from 'node:path';
11
+ import { fileURLToPath } from 'node:url';
12
+
13
+ const here = fileURLToPath(import.meta.url);
14
+ const repoRoot = resolve(here, '../../../../..');
15
+ const { validateSpecialist } = await import(resolve(repoRoot, 'src/specialist/schema.ts'));
16
+
17
+ // Walk known schema keys to detect "unknown" passthrough survivors
18
+ const KNOWN = {
19
+ root: new Set(['specialist']),
20
+ specialist: new Set(['metadata','execution','prompt','skills','capabilities','communication','validation','beads_integration','beads_write_notes','stall_detection','heartbeat','mandatory_rules','output_file']),
21
+ metadata: new Set(['name','version','description','category','author','created','updated','tags']),
22
+ execution: new Set(['mode','model','fallback_model','timeout_ms','stall_timeout_ms','max_retries','interactive','response_format','output_type','permission_required','requires_worktree','thinking_level','auto_commit','extensions','preferred_profile','approval_mode']),
23
+ 'execution.extensions': new Set(['serena','gitnexus']),
24
+ prompt: new Set(['system','task_template','normalize_template','output_schema','examples','skill_inherit']),
25
+ skills: new Set(['paths','scripts']),
26
+ 'skills.scripts.item': new Set(['run','path','phase','inject_output']),
27
+ capabilities: new Set(['required_tools','external_commands','diagnostic_scripts']),
28
+ communication: new Set(['next_specialists','publishes']),
29
+ validation: new Set(['files_to_watch','stale_threshold_days']),
30
+ stall_detection: new Set(['running_idle_warn_ms','running_idle_kill_ms','waiting_stale_ms','tool_duration_warn_ms']),
31
+ };
32
+
33
+ function unknownKeys(obj, knownSet, path) {
34
+ const out = [];
35
+ for (const k of Object.keys(obj || {})) if (!knownSet.has(k)) out.push(`${path}.${k}`);
36
+ return out;
37
+ }
38
+
39
+ function audit(file) {
40
+ const raw = JSON.parse(readFileSync(file,'utf8'));
41
+ const findings = [];
42
+ // raw key check (before parse strips/preserves)
43
+ findings.push(...unknownKeys(raw, KNOWN.root, ''));
44
+ const s = raw.specialist ?? {};
45
+ findings.push(...unknownKeys(s, KNOWN.specialist, 'specialist'));
46
+ if (s.metadata) findings.push(...unknownKeys(s.metadata, KNOWN.metadata, 'specialist.metadata'));
47
+ if (s.execution) findings.push(...unknownKeys(s.execution, KNOWN.execution, 'specialist.execution'));
48
+ if (s.execution?.extensions) findings.push(...unknownKeys(s.execution.extensions, KNOWN['execution.extensions'], 'specialist.execution.extensions'));
49
+ if (s.prompt) findings.push(...unknownKeys(s.prompt, KNOWN.prompt, 'specialist.prompt'));
50
+ if (s.skills) findings.push(...unknownKeys(s.skills, KNOWN.skills, 'specialist.skills'));
51
+ if (Array.isArray(s.skills?.scripts)) for (const [i, sc] of s.skills.scripts.entries()) findings.push(...unknownKeys(sc, KNOWN['skills.scripts.item'], `specialist.skills.scripts[${i}]`));
52
+ if (s.capabilities) findings.push(...unknownKeys(s.capabilities, KNOWN.capabilities, 'specialist.capabilities'));
53
+ if (s.communication) findings.push(...unknownKeys(s.communication, KNOWN.communication, 'specialist.communication'));
54
+ if (s.validation) findings.push(...unknownKeys(s.validation, KNOWN.validation, 'specialist.validation'));
55
+ if (s.stall_detection) findings.push(...unknownKeys(s.stall_detection, KNOWN.stall_detection, 'specialist.stall_detection'));
56
+ return findings;
57
+ }
58
+
59
+ const files = [
60
+ ...readdirSync(resolve(repoRoot,'config/specialists')).filter(f=>f.endsWith('.specialist.json')).map(f=>join(repoRoot,'config/specialists',f)),
61
+ ...readdirSync(resolve(repoRoot,'.specialists/default')).filter(f=>f.endsWith('.specialist.json')).map(f=>join(repoRoot,'.specialists/default',f)),
62
+ ];
63
+
64
+ let totalUnknown = 0;
65
+ let parseErrors = 0;
66
+ for (const file of files) {
67
+ try {
68
+ const v = await validateSpecialist(readFileSync(file,'utf8'));
69
+ if (!v.valid) {
70
+ parseErrors++;
71
+ console.log(`\n✗ PARSE FAIL ${file}`);
72
+ for (const e of v.errors) console.log(` ${e.path}: ${e.message}`);
73
+ continue;
74
+ }
75
+ const unk = audit(file);
76
+ if (unk.length) {
77
+ totalUnknown += unk.length;
78
+ console.log(`\n⚠ ${file}`);
79
+ for (const k of unk) console.log(` unknown key: ${k}`);
80
+ }
81
+ } catch (e) {
82
+ parseErrors++;
83
+ console.log(`\n✗ ERROR ${file}: ${e.message}`);
84
+ }
85
+ }
86
+ console.log(`\n=== ${files.length} specs · ${parseErrors} parse errors · ${totalUnknown} unknown keys ===`);
@@ -3,12 +3,7 @@ import { join } from "node:path";
3
3
  import * as z from "zod";
4
4
  import { SpecialistSchema } from "../../../../src/specialist/schema.ts";
5
5
 
6
- const DEAD_FIELDS = new Set([
7
- "preferred_profile",
8
- "approval_mode",
9
- "normalize_template",
10
- "heartbeat",
11
- ]);
6
+ const DEAD_FIELDS = new Set<string>([]);
12
7
 
13
8
  interface AddedField {
14
9
  path: string;
@@ -6,8 +6,8 @@ description: >
6
6
  "sp is out of date", "hooks not firing", "skills not loading after update",
7
7
  or when drift is detected in installed specialists config, hooks, jobs, DB,
8
8
  extensions, or worktree cleanup.
9
- version: 1.1
10
- synced_at: 00000000
9
+ version: 1.3
10
+ synced_at: 2026-04-25
11
11
  ---
12
12
 
13
13
  # update-specialists
@@ -15,9 +15,17 @@ synced_at: 00000000
15
15
  Bring specialists install back to canonical state. Detect drift, apply targeted
16
16
  fixes, then verify with `sp doctor`. Treat canonical state as both:
17
17
  1. healthy repo wiring and runtime behavior, and
18
- 2. parity with the currently installed `@jaggerxtrm/specialists` package version
18
+ 2. parity with currently installed `@jaggerxtrm/specialists` package version
19
19
  when package-level comparison is available.
20
20
 
21
+ Ownership contract during repair:
22
+ - upstream source: package `config/*` (read-only for repo operators)
23
+ - managed mirror: `.specialists/default/*` (refresh via `sp init --sync-defaults`; sync scope = specialists + mandatory-rules + nodes; no hand edits)
24
+ - repo custom layer: `.specialists/user/*` + `config/nodes/*` + `.specialists/mandatory-rules/*` (rule overlay, wins on set-id conflict; NOT drift — do not overwrite or flag)
25
+ - runtime/generated: `.specialists/{jobs,ready,db}`
26
+
27
+ Isolation rule: backlog-clean surfaces out of scope for this skill.
28
+
21
29
  ## Canonical State
22
30
 
23
31
  Check each item explicitly. This is what a healthy specialists-initialized project
@@ -82,10 +90,14 @@ looks like.
82
90
 
83
91
  | Check | Expected value |
84
92
  |-------|----------------|
85
- | specialists DB | Opens cleanly |
86
- | Schema version | Matches runtime expectation |
93
+ | specialists DB | Opens cleanly (`.specialists/db/observability.db`) |
94
+ | Schema version | Matches runtime expectation (current: v11) |
95
+ | `specialist_job_metrics` table | Present at v11+ — holds aggregated per-job metrics |
87
96
  | WAL / busy timeout settings | Present when runtime uses SQLite |
88
97
  | Corruption / lock errors | None in `sp doctor` |
98
+ | Pre-prune extract | `sp db prune --apply` extracts metrics to `specialist_job_metrics` before deleting events |
99
+ | Extract backfill | `sp db extract --all-missing` populates metrics for jobs whose events still exist |
100
+ | Historical stats query | `sp db stats [--spec <name>] [--model <glob>] [--since <dur>]` reads the aggregated table |
89
101
 
90
102
  ### Skills + extensions parity
91
103
 
@@ -99,6 +111,21 @@ looks like.
99
111
  | `pi-serena-tools` | Registered when Serena integration is expected |
100
112
  | Extension paths | Resolve from installed project, not stale workspace copies |
101
113
 
114
+ ### Mandatory-rules template parity (three-tier)
115
+
116
+ Loader unions indexes from three paths and probes set files in reverse precedence
117
+ (overlay wins on set-id conflict). Full authoring guide:
118
+ `config/mandatory-rules/README.md`.
119
+
120
+ | Check | Expected value |
121
+ |-------|----------------|
122
+ | `.specialists/default/mandatory-rules/*` | Mirrors canonical package templates after `sp init --sync-defaults` (managed mirror, no hand edits) |
123
+ | `.specialists/mandatory-rules/*` | Repo-specific overlay (user-maintained). Present when repo ships its own rules. NOT drift. |
124
+ | Template frontmatter | YAML frontmatter present and parseable |
125
+ | `specialist.mandatory_rules.template_sets` references | Resolve in order: `.specialists/mandatory-rules/` → `.specialists/default/mandatory-rules/` → `config/mandatory-rules/` |
126
+ | Index files (`index.json`) | Any of the three tiers may define `required_template_sets` / `default_template_sets`; loader unions + dedups |
127
+ | Prompt injection behavior | Runner appends resolved `MANDATORY_RULES` block at end of prompt; supervisor emits `mandatory_rules_injection` meta event |
128
+
102
129
  ## Detection
103
130
 
104
131
  Run these in order. Report which checks pass and which drift.
@@ -145,6 +172,7 @@ node -e "const fs=require('fs'); const p='.claude/settings.json'; if (fs.existsS
145
172
  command -v sp
146
173
  command -v specialists
147
174
  specialists init --help | sed -n '1,120p'
175
+ specialists edit --help | sed -n '1,120p' | grep -E -- '--fork-from|fork-from' || true
148
176
  sp doctor --json 2>/dev/null || true
149
177
 
150
178
  # 11. Jobs and worktrees
@@ -154,7 +182,15 @@ find .worktrees -maxdepth 2 -mindepth 1 -type d 2>/dev/null || true
154
182
  # 12. Extension registration
155
183
  node -e "const fs=require('fs'); const p='.pi/settings.json'; if (fs.existsSync(p)) console.log(JSON.stringify(JSON.parse(fs.readFileSync(p,'utf8')).skills ?? JSON.parse(fs.readFileSync(p,'utf8')).extensions ?? {}, null, 2)); else console.log('MISSING .pi/settings.json')"
156
184
 
157
- # 13. Shipped skill frontmatter parity
185
+ # 13a. Observability schema + metrics coverage
186
+ node -e "const {Database} = require('bun:sqlite'); const p='.specialists/db/observability.db'; const fs=require('fs'); if (!fs.existsSync(p)) { console.log('NO_DB'); process.exit(0); } const db=new Database(p,{readonly:true}); const v=db.query(\"SELECT value FROM schema_meta WHERE key='version'\").get(); const has=db.query(\"SELECT name FROM sqlite_master WHERE type='table' AND name='specialist_job_metrics'\").get(); const jobs=db.query('SELECT COUNT(*) c FROM specialist_jobs').get(); const metrics=has ? db.query('SELECT COUNT(*) c FROM specialist_job_metrics').get() : null; console.log(JSON.stringify({schema_version: v?.value, has_metrics_table: !!has, jobs: jobs.c, metrics_rows: metrics?.c ?? 0, metrics_coverage: metrics ? (metrics.c/jobs.c).toFixed(2) : null}, null, 2));" 2>/dev/null || echo "REQUIRES_BUN_RUNTIME"
187
+
188
+ # 13. Mandatory-rules template tiers + reference checks (three-tier resolution)
189
+ find .specialists/default/mandatory-rules -maxdepth 1 -type f 2>/dev/null || true
190
+ find .specialists/mandatory-rules -maxdepth 1 -type f 2>/dev/null || true
191
+ node -e "const fs=require('fs'); const path=require('path'); const roots=['.specialists/default/specialists','.specialists/user/specialists']; const missing=[]; for (const root of roots) { if (!fs.existsSync(root)) continue; for (const file of fs.readdirSync(root)) { if (!file.endsWith('.specialist.json')) continue; const spec=JSON.parse(fs.readFileSync(path.join(root,file),'utf8')); const sets=spec.specialist?.mandatory_rules?.template_sets ?? []; for (const set of sets) { const candidates=[path.join('.specialists/mandatory-rules',set+'.md'), path.join('.specialists/default/mandatory-rules',set+'.md'), path.join('config/mandatory-rules',set+'.md')]; if (!candidates.some((p)=>fs.existsSync(p))) missing.push(file+': missing template set '+set); } } } if (missing.length) console.log(missing.join('\n'));"
192
+
193
+ # 14. Shipped skill frontmatter parity
158
194
  node -e "const fs=require('fs'); const path=require('path'); const dir='.xtrm/skills/default'; if (!fs.existsSync(dir)) process.exit(0); for (const name of fs.readdirSync(dir)) { const p=path.join(dir,name,'SKILL.md'); if (!fs.existsSync(p)) continue; const head=fs.readFileSync(p,'utf8').split('---')[1] || ''; const version=(head.match(/version:\s*([^\n]+)/)||[])[1]; const synced=(head.match(/synced_at:\s*([^\n]+)/)||[])[1]; console.log(name+': version='+(version||'missing')+' synced_at='+(synced||'missing')); }"
159
195
  ```
160
196
 
@@ -167,7 +203,8 @@ Use targeted fixes first. Escalate to full sync only if needed.
167
203
  | Installed package version mismatch | reinstall / upgrade `@jaggerxtrm/specialists`, then re-run checks |
168
204
  | CLI version mismatch vs package | reinstall runtime so `sp` / `specialists` align with installed package |
169
205
  | Specialist JSON missing required fields | `sp edit <name> ...` or regenerate via `specialists init --sync-defaults` |
170
- | Specialist JSON schema mismatch | `specialists init --sync-defaults` |
206
+ | Need user-layer override from default/package specialist | `sp edit <name> --fork-from <base>` to materialize editable copy in `.specialists/user/` |
207
+ | Specialist JSON schema mismatch | `specialists init --sync-defaults` (refreshes specialists + mandatory-rules + nodes) |
171
208
  | Installed specialist default differs from canonical package copy | `specialists init --sync-defaults` unless local customization is intentional |
172
209
  | Hooks missing or stale | `specialists init` |
173
210
  | Installed hook file differs from canonical package copy | `specialists init` unless local customization is intentional |
@@ -175,11 +212,18 @@ Use targeted fixes first. Escalate to full sync only if needed.
175
212
  | Job dir missing | `specialists init` |
176
213
  | Orphaned `.worktrees/` entries | `specialists clean` |
177
214
  | SQLite schema/version mismatch | `sp doctor` first, then `specialists init --sync-defaults` or runtime migration command |
215
+ | Schema below v11 (no `specialist_job_metrics`) | Reinstall / upgrade runtime; table is created by initSchema / migrateToV11. No data loss — raw events untouched. |
216
+ | Events about to be pruned but never aggregated | `sp db extract --all-missing` BEFORE `sp db prune --apply`. Prune refuses when extract fails (safe by design). |
217
+ | Emergency: need to prune but extract is wedged | `sp db prune --apply --skip-extract` — raw events deleted without aggregation. Use only when data loss is acceptable. |
218
+ | Historical per-job stats needed | `sp db stats` reads `specialist_job_metrics`. Replaces ad-hoc `status.json` scans. Supports `--format json\|table`. |
178
219
  | Pi extensions missing | `specialists init --sync-skills` or reinstall extension registration |
179
220
  | Hook config format stale | `specialists init` |
180
221
  | Skill symlink / active-skill drift | `specialists init --sync-skills` |
181
222
  | Installed default skill differs from canonical package copy | `specialists init --sync-skills` unless local customization is intentional |
182
223
  | Skill frontmatter version / synced_at drift | `specialists init --sync-skills` or refresh packaged skills |
224
+ | Mandatory-rules mirror drift (`.specialists/default/mandatory-rules`) | `specialists init --sync-defaults` |
225
+ | `.specialists/mandatory-rules/` overlay present | Leave alone — this is repo overlay, NOT drift |
226
+ | Missing/invalid `template_sets` references | Check all three tiers first; `sp edit <name> --fork-from <base>` then fix references, or sync defaults if mirror missing, or add set to overlay if intended |
183
227
  | Unknown manual drift | Stop, inspect, then apply user-approved fix |
184
228
 
185
229
  ## Remediation
@@ -206,8 +250,14 @@ shows missing fields, wrong names, or schema mismatch:
206
250
  specialists init --sync-defaults
207
251
  ```
208
252
 
253
+ `--sync-defaults` refreshes specialists + mandatory-rules + nodes mirrors.
254
+
209
255
  If one specialist needs a small repair and `sp edit` supports it, prefer that over
210
- full sync.
256
+ full sync. If target specialist lives in default/package layer, fork first:
257
+
258
+ ```bash
259
+ sp edit <name> --fork-from <base>
260
+ ```
211
261
 
212
262
  ### Fix: Hooks not firing
213
263
 
@@ -250,6 +300,31 @@ If doctor reports DB version mismatch or recovery issue:
250
300
  2. Apply runtime migration command if available.
251
301
  3. If no automated migration exists, flag manual intervention.
252
302
 
303
+ ### Fix: metrics aggregation missing or stale
304
+
305
+ Schema v11 introduced `specialist_job_metrics` (aggregated per-job stats). If you see low `metrics_coverage` in the detection output, or want historical stats before running `sp db prune`:
306
+
307
+ ```bash
308
+ # Backfill metrics for any job whose events still exist but lack a metrics row.
309
+ sp db extract --all-missing
310
+
311
+ # Inspect specific job metrics.
312
+ sp db extract --job <job-id>
313
+
314
+ # Query aggregates.
315
+ sp db stats
316
+ sp db stats --spec executor --since 7d --format json
317
+ sp db stats --model 'openai-codex/*' --since 30d
318
+ ```
319
+
320
+ `sp db prune --apply` automatically extracts for every job whose events will be deleted (unless `--skip-extract`). If extract throws, prune aborts — investigate the failing job instead of bypassing.
321
+
322
+ Safe order before a retention cleanup:
323
+ 1. `sp db extract --all-missing` — verify no extract errors.
324
+ 2. `sp db prune --before 30d --dry-run` — confirm scope.
325
+ 3. `sp db prune --before 30d --apply` — prune with pre-extract built in.
326
+ 4. `sp db vacuum` — compact file size.
327
+
253
328
  ### Fix: Skills/defaults differ from shipped package copy
254
329
 
255
330
  If diff against the installed package shows `.specialists/default/`,
@@ -62,6 +62,17 @@ Specialists are autonomous AI agents that run independently — fresh context, d
62
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.
63
63
  9. **Executor does not run tests.** Executor runs lint + tsc only. Tests are the reviewer's and test-runner's responsibility in the chained pipeline.
64
64
  10. **Keep specialists alive through the review cycle.** Never `sp stop` an executor or debugger before the reviewer delivers its verdict. The specialist stays in `waiting` so you can `resume` it — to commit changes, apply fixes from reviewer feedback, or continue work. Only stop after final reviewer PASS and confirmed commit.
65
+ 11. **Respect ownership layers and loader precedence.** Loader resolution order is `.specialists/user/*` > `.specialists/default/*` > package fallback `config/*`. Upstream source = package `config/*` (read-only for repo operators); managed mirror = `.specialists/default/*` (no hand edits); repo custom layer = `.specialists/user/*`; runtime/generated = `.specialists/{jobs,ready,db}`.
66
+ 12. **Keep backlog-clean isolated.** Do not mix backlog-clean changes into specialist ownership/migration tasks.
67
+
68
+ ## Mandatory-rules template sets
69
+
70
+ Use template-driven mandatory rules for repeatable policy bundles.
71
+
72
+ - Specialist config field: `specialist.mandatory_rules.template_sets`
73
+ - Template source: `config/mandatory-rules/*.md`
74
+ - Template format: YAML frontmatter + body content
75
+ - Runtime behavior: runner resolves templates and injects rendered rules at end of prompt
65
76
 
66
77
  ---
67
78
 
@@ -127,11 +138,13 @@ specialists stop <job-id> --force # 5s SIGTERM timeout, then pgroup
127
138
 
128
139
  # Management
129
140
  specialists edit <name> # edit specialist config (dot-path, --preset)
141
+ specialists edit <name> --fork-from <base> # fork non-user specialist into .specialists/user/ then edit
130
142
  specialists clean # purge old job dirs + worktree GC
131
143
  specialists clean --processes # kill all running/starting specialist jobs
132
144
  specialists db vacuum # compact SQLite storage (refuses if jobs running)
133
145
  specialists db prune --before <iso|duration> --dry-run|--apply # prune old events/results/terminal jobs
134
146
  specialists doctor orphans # integrity scan: orphan, stale-pointer, integrity-violation
147
+ specialists init --sync-defaults # refresh specialists + mandatory-rules + nodes from canonical defaults
135
148
  specialists init --sync-skills # re-sync skills only (no full init)
136
149
  specialists init --no-xtrm-check # skip xtrm prerequisite check (CI/testing)
137
150
  ```