xtrm-tools 0.7.13 → 0.7.15

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.
@@ -1,448 +1,154 @@
1
1
  ---
2
2
  name: update-specialists
3
3
  description: >
4
- Reconcile a project with current canonical specialists install state.
5
- Use this skill when a user says "update specialists", "specialists is broken",
6
- "sp is out of date", "hooks not firing", "skills not loading after update",
7
- or when drift is detected in installed specialists config, hooks, jobs, DB,
8
- extensions, or worktree cleanup.
9
- version: 1.4
10
- synced_at: 2026-04-29
4
+ Reconcile all xtrm-managed asset drift across repos.
5
+ Use this skill when user says "update specialists", "xtrm drift", "assets out of date",
6
+ or when operator needs guided refresh across one repo or many.
7
+ version: 2.0
8
+ synced_at: 2026-05-05
11
9
  ---
12
10
 
13
11
  # update-specialists
14
12
 
15
- Bring specialists install back to canonical state. Detect drift, apply targeted
16
- fixes, then verify with `sp doctor`. Treat canonical state as both:
17
- 1. healthy repo wiring and runtime behavior, and
18
- 2. parity with currently installed `@jaggerxtrm/specialists` package version
19
- when package-level comparison is available.
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
-
29
- ## Canonical State
30
-
31
- Check each item explicitly. This is what a healthy specialists-initialized project
32
- looks like.
33
-
34
- ### Package + runtime parity
35
-
36
- | Check | Expected value |
37
- |-------|----------------|
38
- | Installed `@jaggerxtrm/specialists` package version | Matches intended runtime version for repo install |
39
- | `sp --version` / `specialists --version` | Matches installed package version or same release line |
40
- | Installed package root | Resolvable from Node / npm environment |
41
- | Canonical package defaults | Available from installed package for direct diffing |
42
- | Repo install vs package install | No unexpected drift in canonical files unless intentionally customized |
43
-
44
- ### Specialists configs
45
-
46
- | Check | Expected value |
47
- |-------|----------------|
48
- | `.specialists/default/*.specialist.json` | JSON-first specialist configs present |
49
- | `metadata.name` | Matches filename stem |
50
- | `metadata.version` | Valid semver string and consistent with canonical shipped copy when comparing like-for-like |
51
- | `metadata.description` | Present |
52
- | `metadata.category` | Present |
53
- | `execution.model` | Present and pingable |
54
- | `execution.fallback_model` | Present, different provider from primary |
55
- | `execution.permission_required` | Valid enum |
56
- | `skills.paths` | Referenced skill paths resolve correctly |
57
- | `execution.interactive` | Matches intended keep-alive behavior |
58
- | Installed default specialist copy | Matches canonical package copy unless intentionally customized |
59
-
60
- ### Hooks wiring
61
-
62
- | Check | Expected value |
63
- |-------|----------------|
64
- | `.claude/settings.json` | Has hook entries for active events |
65
- | Hook events | At minimum: `SessionStart`, `PreToolUse`, `PostToolUse`, `Stop` |
66
- | Hook paths | Point at specialists runtime hook scripts, not stale xtrm-only paths |
67
- | Hook format | Matches project's installed settings format and loads cleanly |
68
- | Installed hook scripts | Match canonical package hook files unless intentionally customized |
69
-
70
- ### CLI reachability
71
-
72
- | Check | Expected value |
73
- |-------|----------------|
74
- | `sp` command | On PATH and runs |
75
- | `specialists` command | On PATH and runs |
76
- | Version compatibility | `sp doctor` reports matching runtime / install state |
77
- | Command surface | `sp doctor`, `sp init`, `sp clean`, `sp status` available |
78
-
79
- ### Jobs and runtime dirs
80
-
81
- | Check | Expected value |
82
- |-------|----------------|
83
- | `.specialists/jobs/` | Exists |
84
- | `.specialists/ready/` | Exists if used by runtime |
85
- | `.specialists/default/` | Canonical install copy present |
86
- | Orphaned worktrees | None under `.worktrees/` |
87
- | Worktree ownership | No stale entries for deleted jobs |
88
-
89
- ### SQLite / observability
90
-
91
- | Check | Expected value |
92
- |-------|----------------|
93
- | specialists DB | Opens cleanly (`.specialists/db/observability.db`) |
94
- | Schema version | Matches runtime expectation (current: v11; auto-migrates on next runtime startup) |
95
- | `specialist_job_metrics` table | Present at v11+ — holds aggregated per-job metrics |
96
- | `specialist_job_metrics` columns | Includes `active_runtime_ms` + `waiting_ms` (drs41.1 — auto-added by idempotent `migrateToV11` ALTER TABLE on first start of upgraded runtime; pre-existing rows get NULL until next aggregate) |
97
- | Auto-aggregation hook | Supervisor + `sp stop` invoke `aggregateJobMetricsBestEffort` after terminal-status persistence (drs41.1) — table populates without manual `sp db extract` under normal operation |
98
- | Merge target lookup | DB-first (post-ofjvj): `readAllJobStatuses()` reads `specialist_jobs` via `listStatuses()`. `sp merge` no longer reads `.specialists/jobs/<id>/status.json`. Older versions silently failed after `sp stop` cleaned status.json. |
99
- | WAL / busy timeout settings | Present when runtime uses SQLite |
100
- | Corruption / lock errors | None in `sp doctor` |
101
- | Pre-prune extract | `sp db prune --apply` extracts metrics to `specialist_job_metrics` before deleting events |
102
- | Extract backfill | `sp db extract --all-missing` populates metrics for jobs whose events still exist (still useful for backfilling historical jobs that ran before the auto-aggregate hook landed) |
103
- | Historical stats query | `sp db stats [--spec <name>] [--model <glob>] [--since <dur>]` reads the aggregated table; output includes `active_s`, `waiting_s`, `total_s` (drs41.1) |
104
-
105
- **Safety: `sp init` and `sp init --sync-defaults` do NOT touch `.specialists/db/observability.db`.** Init checks file existence and skips with "observability database already exists (not touched)" when present. Schema migrations run on next runtime startup (any `sp` invocation that opens the DB), additively via `ALTER TABLE ADD COLUMN`. No data loss path during a normal package upgrade.
106
-
107
- ### Skills + extensions parity
108
-
109
- | Check | Expected value |
110
- |-------|----------------|
111
- | `.xtrm/skills/default/` | Matches canonical package skill set for installed version |
112
- | Active skill links / copies | Resolve to expected default or active targets |
113
- | Skill frontmatter `version` / `synced_at` | Present and reasonable for shipped skills |
114
- | `quality-gates` | Registered if project uses quality gates |
115
- | `pi-gitnexus` | Registered when GitNexus integration is expected |
116
- | `pi-serena-tools` | Registered when Serena integration is expected |
117
- | Extension paths | Resolve from installed project, not stale workspace copies |
118
-
119
- ### Mandatory-rules template parity (three-tier)
120
-
121
- Loader unions indexes from three paths and probes set files in reverse precedence
122
- (overlay wins on set-id conflict). Full authoring guide:
123
- `config/mandatory-rules/README.md`.
124
-
125
- | Check | Expected value |
126
- |-------|----------------|
127
- | `.specialists/default/mandatory-rules/*` | Mirrors canonical package templates after `sp init --sync-defaults` (managed mirror, no hand edits) |
128
- | `.specialists/mandatory-rules/*` | Repo-specific overlay (user-maintained). Present when repo ships its own rules. NOT drift. |
129
- | Template frontmatter | YAML frontmatter present and parseable |
130
- | `specialist.mandatory_rules.template_sets` references | Resolve in order: `.specialists/mandatory-rules/` → `.specialists/default/mandatory-rules/` → `config/mandatory-rules/` |
131
- | Index files (`index.json`) | Any of the three tiers may define `required_template_sets` / `default_template_sets`; loader unions + dedups |
132
- | Prompt injection behavior | Runner appends resolved `MANDATORY_RULES` block at end of prompt; supervisor emits `mandatory_rules_injection` meta event |
133
-
134
- ## Discover Latest Release
135
-
136
- Before reconciling, determine whether a newer release is published. Compare local `package.json` version to the most recent `vX.Y.Z` tag on `origin`:
13
+ Interactive wrapper over `xt update` for xtrm-managed asset drift.
137
14
 
138
- ```bash
139
- LOCAL=$(node -p "require('./package.json').version")
140
- LATEST=$(git ls-remote --tags --refs origin | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1 | sed 's/^v//')
141
- echo "local: $LOCAL latest: $LATEST"
142
- ```
143
-
144
- If `LATEST > LOCAL`, read the corresponding `CHANGELOG.md` section to summarize what shipped:
145
-
146
- ```bash
147
- awk -v ver="$LATEST" '/^## \[v?'"$LATEST"'\]/,/^## \[/{print}' CHANGELOG.md | head -60
148
- ```
15
+ Canonical-live model:
16
+ - **Category A**: specialist runtime / loader-live surfaces. No refresh needed; verify only.
17
+ - **Category B**: xtrm-managed snapshots under repos (`.xtrm/skills/default/`, `.xtrm/hooks/default/`, and related managed assets). These can drift and need operator-confirmed refresh.
149
18
 
150
- Surface a one-line summary to the user (Added/Changed/Fixed counts plus the headline) and **ask before pulling**. The reconcile flow below applies regardless of whether the user pulls a new release first or stays on the current version — drift detection is independent of release version.
19
+ Skill goal:
20
+ 1. find projects root,
21
+ 2. inspect drift,
22
+ 3. summarize per-repo state,
23
+ 4. ask operator which repos to refresh,
24
+ 5. run `xt update --apply`,
25
+ 6. re-check,
26
+ 7. report final state.
151
27
 
152
- Skip this discovery step entirely when `SPECIALISTS_OFFLINE=1` is set, when offline, or when the user already specified the version. The `using-specialists-v2` skill performs the same lightweight check on session-load and may have already surfaced the notice; do not repeat it.
28
+ No automatic execution. Always operator-confirmed.
153
29
 
154
- After the user confirms a pull (e.g. `git fetch && git pull origin master`), proceed with detection below to catch any drift introduced by the new release.
30
+ ## Operator Flow
155
31
 
156
- ## Detection
32
+ ### 1) Discover projects root
157
33
 
158
- Run these in order. Report which checks pass and which drift.
34
+ Ask for root if user did not name one.
159
35
 
160
- ```bash
161
- # 1. Primary health check
162
- sp doctor
163
-
164
- # 2. Runtime status
165
- sp status
166
-
167
- # 3. Installed package + CLI version parity
168
- npm ls @jaggerxtrm/specialists --depth=0 2>/dev/null || true
169
- node -e "try { const pkg=require(require.resolve('@jaggerxtrm/specialists/package.json')); console.log(JSON.stringify({installed_package_version: pkg.version}, null, 2)); } catch (err) { console.log('PACKAGE_NOT_RESOLVABLE'); }"
170
- sp --version 2>/dev/null || true
171
- specialists --version 2>/dev/null || true
172
-
173
- # 4. Resolve canonical package root for direct drift diff
174
- node -e "try { const path=require('path'); const pkgPath=require.resolve('@jaggerxtrm/specialists/package.json'); console.log(path.dirname(pkgPath)); } catch (err) { console.log('PACKAGE_ROOT_UNAVAILABLE'); }"
175
-
176
- # 5. Config shape
177
- find .specialists/default -maxdepth 1 -name '*.specialist.json' -print
178
-
179
- # 6. Validate specialist JSON files
180
- node -e "const fs=require('fs'); const path=require('path'); const dir='.specialists/default'; for (const file of fs.readdirSync(dir)) { if (!file.endsWith('.specialist.json')) continue; const s=JSON.parse(fs.readFileSync(path.join(dir,file),'utf8')); const m=s.metadata||{}; const e=s.execution||{}; const missing=[]; for (const key of ['name','version','description','category']) if (!m[key]) missing.push('metadata.'+key); for (const key of ['model','fallback_model','permission_required']) if (!e[key]) missing.push('execution.'+key); if (missing.length) console.log(file+': MISSING '+missing.join(', ')); if (m.name && m.name !== file.replace(/\.specialist\.json$/, '')) console.log(file+': NAME MISMATCH '+m.name); }"
181
-
182
- # 7. Validate referenced skill paths
183
- node -e "const fs=require('fs'); const path=require('path'); const dir='.specialists/default'; for (const file of fs.readdirSync(dir)) { if (!file.endsWith('.specialist.json')) continue; const s=JSON.parse(fs.readFileSync(path.join(dir,file),'utf8')); for (const p of (s.skills?.paths ?? [])) { if (!fs.existsSync(p)) console.log(file+': MISSING SKILL PATH '+p); } }"
184
-
185
- # 8. Compare repo defaults against installed package defaults (if package root resolvable)
186
- PKG_ROOT="$(node -e "try { const path=require('path'); process.stdout.write(path.dirname(require.resolve('@jaggerxtrm/specialists/package.json'))); } catch (err) {}")"
187
- if [ -n "$PKG_ROOT" ]; then
188
- diff -rq .specialists/default "$PKG_ROOT/config/specialists" || true
189
- diff -rq .xtrm/skills/default "$PKG_ROOT/config/skills" || true
190
- diff -rq .claude/hooks "$PKG_ROOT/config/hooks" || true
191
- else
192
- echo PACKAGE_COMPARE_UNAVAILABLE
193
- fi
194
-
195
- # 9. Hooks wiring
196
- node -e "const fs=require('fs'); const p='.claude/settings.json'; if (fs.existsSync(p)) { const s=JSON.parse(fs.readFileSync(p,'utf8')); console.log(JSON.stringify(s.hooks ?? s, null, 2)); } else { console.log('MISSING .claude/settings.json'); }"
197
-
198
- # 10. Command availability
199
- command -v sp
200
- command -v specialists
201
- specialists init --help | sed -n '1,120p'
202
- specialists edit --help | sed -n '1,120p' | grep -E -- '--fork-from|fork-from' || true
203
- sp doctor --json 2>/dev/null || true
204
-
205
- # 11. Jobs and worktrees
206
- ls -1 .specialists/jobs 2>/dev/null || true
207
- find .worktrees -maxdepth 2 -mindepth 1 -type d 2>/dev/null || true
208
-
209
- # 12. Extension registration
210
- 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')"
211
-
212
- # 13a. Observability schema + metrics coverage + drs41.1 column presence
213
- 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; const cols=has ? new Set(db.query('PRAGMA table_info(specialist_job_metrics)').all().map(r=>r.name)) : new Set(); const drs41Cols={active_runtime_ms: cols.has('active_runtime_ms'), waiting_ms: cols.has('waiting_ms')}; console.log(JSON.stringify({schema_version: v?.value, has_metrics_table: !!has, drs41_columns_present: drs41Cols, 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"
214
-
215
- # 13. Mandatory-rules template tiers + reference checks (three-tier resolution)
216
- find .specialists/default/mandatory-rules -maxdepth 1 -type f 2>/dev/null || true
217
- find .specialists/mandatory-rules -maxdepth 1 -type f 2>/dev/null || true
218
- 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'));"
219
-
220
- # 14. Shipped skill frontmatter parity
221
- 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')); }"
222
- ```
223
-
224
- ## Drift -> Fix Mapping
225
-
226
- Use targeted fixes first. Escalate to full sync only if needed.
227
-
228
- | Drift | Fix |
229
- |-------|-----|
230
- | Installed package version mismatch | reinstall / upgrade `@jaggerxtrm/specialists`, then re-run checks |
231
- | CLI version mismatch vs package | reinstall runtime so `sp` / `specialists` align with installed package |
232
- | Specialist JSON missing required fields | `sp edit <name> ...` or regenerate via `specialists init --sync-defaults` |
233
- | Need user-layer override from default/package specialist | `sp edit <name> --fork-from <base>` to materialize editable copy in `.specialists/user/` |
234
- | Specialist JSON schema mismatch | `specialists init --sync-defaults` (refreshes specialists + mandatory-rules + nodes) |
235
- | Installed specialist default differs from canonical package copy | `specialists init --sync-defaults` unless local customization is intentional |
236
- | Hooks missing or stale | `specialists init` |
237
- | Installed hook file differs from canonical package copy | `specialists init` unless local customization is intentional |
238
- | `sp` / `specialists` missing from PATH | Reinstall / re-bootstrap specialists runtime |
239
- | Job dir missing | `specialists init` |
240
- | Orphaned `.worktrees/` entries | `specialists clean` |
241
- | SQLite schema/version mismatch | `sp doctor` first, then `specialists init --sync-defaults` or runtime migration command |
242
- | Schema below v11 (no `specialist_job_metrics`) | Reinstall / upgrade runtime; table is created by initSchema / migrateToV11. No data loss — raw events untouched. |
243
- | `specialist_job_metrics` missing `active_runtime_ms` / `waiting_ms` columns (post-drs41.1) | Open any `sp` command — `migrateToV11` is idempotent and ALTERs the table to add the columns. No reinstall needed. Pre-existing rows show NULL until next aggregate or `sp db extract --all-missing`. |
244
- | Auto-aggregate hook absent (older runtime) — empty `specialist_job_metrics` despite job activity | Upgrade `@jaggerxtrm/specialists` package. Post-drs41.1, supervisor + `sp stop` invoke `aggregateJobMetricsBestEffort` on every terminal status, so the table fills under normal operation. Backfill historical with `sp db extract --all-missing`. |
245
- | `sp merge` fails after `sp stop` (older runtime) — "No chain-root job with worktree metadata found" | Upgrade `@jaggerxtrm/specialists` past ofjvj fix. Merge lookup is now DB-first via `readAllJobStatuses()` / `listStatuses()`. Pre-fix workaround was manual `git merge --no-ff feature/<branch>` (skips tsc + conflict gates). |
246
- | 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). |
247
- | 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. |
248
- | Historical per-job stats needed | `sp db stats` reads `specialist_job_metrics`. Replaces ad-hoc `status.json` scans. Supports `--format json\|table`. |
249
- | Pi extensions missing | `specialists init --sync-skills` or reinstall extension registration |
250
- | Hook config format stale | `specialists init` |
251
- | Skill symlink / active-skill drift | `specialists init --sync-skills` |
252
- | Installed default skill differs from canonical package copy | `specialists init --sync-skills` unless local customization is intentional |
253
- | Skill frontmatter version / synced_at drift | `specialists init --sync-skills` or refresh packaged skills |
254
- | Mandatory-rules mirror drift (`.specialists/default/mandatory-rules`) | `specialists init --sync-defaults` |
255
- | `.specialists/mandatory-rules/` overlay present | Leave alone — this is repo overlay, NOT drift |
256
- | 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 |
257
- | Unknown manual drift | Stop, inspect, then apply user-approved fix |
258
-
259
- ## Remediation
260
-
261
- ### Fix: Package/runtime version drift
262
-
263
- If installed npm package version, CLI version, or package root parity checks disagree:
264
-
265
- ```bash
266
- npm ls @jaggerxtrm/specialists --depth=0
267
- specialists --version
268
- sp --version
269
- ```
36
+ Default order:
37
+ 1. explicit user root,
38
+ 2. `~/dev`,
39
+ 3. git-discovered repo root / workspace root,
40
+ 4. current directory as last fallback.
270
41
 
271
- If versions do not align, reinstall or upgrade the package first. After runtime
272
- version is correct, re-run `specialists init` / sync commands to repair repo drift.
42
+ If multiple candidate roots exist, ask which one to use.
273
43
 
274
- ### Fix: Specialist configs drifted
44
+ ### 2) Run doctor
275
45
 
276
- If `sp doctor`, JSON validation, or direct diff against package canonical defaults
277
- shows missing fields, wrong names, or schema mismatch:
46
+ Use:
278
47
 
279
48
  ```bash
280
- specialists init --sync-defaults
49
+ xt doctor --cwd <root> --json
281
50
  ```
282
51
 
283
- `--sync-defaults` refreshes specialists + mandatory-rules + nodes mirrors.
52
+ If `xt` is unavailable, stop and switch to fallback guidance below.
284
53
 
285
- If one specialist needs a small repair and `sp edit` supports it, prefer that over
286
- full sync. If target specialist lives in default/package layer, fork first:
54
+ ### 3) Summarize drift
287
55
 
288
- ```bash
289
- sp edit <name> --fork-from <base>
290
- ```
56
+ Render clean table grouped by repo:
57
+ - repo path
58
+ - status
59
+ - drift count
60
+ - missing / extra / mismatched assets
61
+ - suggested action
291
62
 
292
- ### Fix: Hooks not firing
63
+ Keep focus on operator action, not internal diagnostics.
293
64
 
294
- If hooks are missing, wrong events, stale script paths, or hook files differ from
295
- installed package canonical copies:
65
+ ### 4) Ask for confirm
296
66
 
297
- ```bash
298
- specialists init
299
- ```
67
+ Offer three paths:
68
+ - refresh all repos,
69
+ - refresh specific repos,
70
+ - dry-run only.
300
71
 
301
- If runtime exposes a narrower hook sync command, prefer it. Use full init only
302
- when hook-only sync is not enough.
72
+ If user names one repo, keep flow narrow and confirm only that repo.
303
73
 
304
- ### Fix: CLI not reachable
74
+ ### 5) Apply refresh
305
75
 
306
- If `sp` or `specialists` is missing or incompatible:
76
+ Use:
307
77
 
308
78
  ```bash
309
- sp doctor
79
+ xt update --apply --root <root>
310
80
  ```
311
81
 
312
- If doctor confirms install drift, reinstall or re-bootstrap specialists runtime.
313
- Do not guess at file edits when command surface itself is broken.
314
-
315
- ### Fix: Job dirs or worktree GC drift
316
-
317
- If jobs exist without owners, worktrees are orphaned, or cleanup state is stale:
82
+ Or for one repo:
318
83
 
319
84
  ```bash
320
- specialists clean
85
+ xt update --apply --repo <repo>
321
86
  ```
322
87
 
323
- Then re-run `sp doctor`.
324
-
325
- ### Fix: SQLite schema drift
88
+ For dry-run, omit `--apply`.
326
89
 
327
- If doctor reports DB version mismatch or recovery issue:
90
+ ### 6) Re-run doctor
328
91
 
329
- 1. Run `sp doctor` and capture exact schema error.
330
- 2. Apply runtime migration command if available.
331
- 3. If no automated migration exists, flag manual intervention.
92
+ Run same doctor command again after update and confirm clean state.
332
93
 
333
- For additive schema bumps (e.g. drs41.1 added `active_runtime_ms` / `waiting_ms` columns within v11): just open any `sp` command in the repo. `initSchema()` runs every migrate-up function on every startup; `migrateToV11` is idempotent — it detects existing v11 schema and `ALTER TABLE ADD COLUMN`s the missing fields. Existing rows are preserved (new fields = NULL until next aggregate). No data loss, no manual SQL.
94
+ ### 7) Final report
334
95
 
335
- `sp init` and `sp init --sync-defaults` skip the DB entirely when it exists — the only way to wipe `.specialists/db/observability.db` is to delete the file manually.
96
+ State:
97
+ - what drift existed,
98
+ - what refreshed,
99
+ - what stayed untouched,
100
+ - any residual manual fixes.
336
101
 
337
- ### Fix: metrics aggregation missing or stale
102
+ ## Fallback When xt Missing
338
103
 
339
- 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`:
104
+ If `xt` / `xtrm` not installed or doctor/update help unavailable:
105
+ - do not block user,
106
+ - switch to per-repo guidance,
107
+ - tell user to run repo-local checks manually,
108
+ - do not invent bulk repair commands.
340
109
 
341
- ```bash
342
- # Backfill metrics for any job whose events still exist but lack a metrics row.
343
- sp db extract --all-missing
110
+ Fallback response shape:
111
+ - identify likely drifted repos,
112
+ - point user at repo-local `sp doctor` / package-specific checks already available in that repo,
113
+ - say bulk refresh needs `xt` installed.
344
114
 
345
- # Inspect specific job metrics.
346
- sp db extract --job <job-id>
347
-
348
- # Query aggregates.
349
- sp db stats
350
- sp db stats --spec executor --since 7d --format json
351
- sp db stats --model 'openai-codex/*' --since 30d
352
- ```
115
+ ## Drift Review Rules
353
116
 
354
- `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.
117
+ - Treat repo-custom overlays as intentional unless doctor marks them mismatched against managed snapshot.
118
+ - Do not overwrite user-owned layers.
119
+ - Prefer dry-run first when drift touches multiple repos.
120
+ - If only one repo needs refresh, keep output narrow and use single-repo update path.
121
+ - If doctor shows mixed drift across 3 repos, summarize each repo separately and ask which to refresh.
355
122
 
356
- Safe order before a retention cleanup:
357
- 1. `sp db extract --all-missing` — verify no extract errors.
358
- 2. `sp db prune --before 30d --dry-run` — confirm scope.
359
- 3. `sp db prune --before 30d --apply` — prune with pre-extract built in.
360
- 4. `sp db vacuum` — compact file size.
123
+ ## Output Shape
361
124
 
362
- ### Fix: Skills/defaults differ from shipped package copy
125
+ Use this order:
126
+ 1. root chosen
127
+ 2. doctor summary
128
+ 3. drift table
129
+ 4. confirm prompt
130
+ 5. update action
131
+ 6. post-update doctor result
132
+ 7. final status
363
133
 
364
- If diff against the installed package shows `.specialists/default/`,
365
- `.xtrm/skills/default/`, or `.claude/hooks/` drift from shipped canonical files:
134
+ ## Example Operator Loop
366
135
 
367
- - If drift is intentional project customization, report it and do not overwrite silently.
368
- - If drift is unintentional, use the narrowest sync that fixes the affected area:
369
-
370
- ```bash
371
- specialists init --sync-defaults
372
- specialists init --sync-skills
373
- specialists init
374
- ```
375
-
376
- ### Fix: Pi extensions not registered
136
+ ```text
137
+ Root: ~/dev
138
+ Doctor: 3 repos checked
377
139
 
378
- If `quality-gates`, `pi-gitnexus`, or `pi-serena-tools` are missing:
140
+ repo status drift
141
+ repo-a drifted 4 assets
142
+ repo-b in-sync 0 assets
143
+ repo-c drifted 1 asset
379
144
 
380
- ```bash
381
- specialists init --sync-skills
145
+ Refresh all / specific repos / dry-run?
382
146
  ```
383
147
 
384
- If project uses different extension packaging, re-run install step that writes
385
- `.pi/settings.json`.
386
-
387
148
  ## Verification
388
149
 
389
- After fixes, confirm canonical state restored.
390
-
391
- ```bash
392
- sp doctor
393
- sp status
394
- npm ls @jaggerxtrm/specialists --depth=0 2>/dev/null || true
395
- specialists --version 2>/dev/null || true
396
- sp --version 2>/dev/null || true
397
-
398
- node -e "const fs=require('fs'); const p='.claude/settings.json'; const s=JSON.parse(fs.readFileSync(p,'utf8')); console.log(Boolean(s.hooks || Object.keys(s).length))"
399
- ```
400
-
401
- Expected outcome:
402
- - `sp doctor` clean
403
- - `sp status` no drift / no repair hints
404
- - `sp` and `specialists` reachable (`sp` is shorthand; `specialists` is canonical)
405
- - installed package / CLI versions aligned
406
- - specialist JSON files valid
407
- - repo defaults match installed package defaults (or intentional custom drift acknowledged)
408
- - hooks present on required events and canonical hook files match installed package copy
409
- - no orphaned worktrees
410
- - SQLite state healthy
411
-
412
- ## Manual Intervention
413
-
414
- Flag these when automatic fix is unsafe or impossible:
415
-
416
- - `sp doctor` reports corrupt DB / unreadable SQLite file
417
- - command surface missing because install itself is broken
418
- - hook scripts absent from repo and cannot be regenerated
419
- - schema mismatch with no available migration path
420
- - worktree cleanup would remove user changes
421
- - extensions required by project are not installed at package level
422
- - package root is unavailable, so package-vs-installed diff cannot be computed
423
- - repo intentionally diverges from canonical package copies and user must preserve customizations
424
-
425
- When manual intervention needed, report:
426
- 1. exact drift
427
- 2. exact command tried
428
- 3. why auto-fix stopped
429
- 4. next safe operator action
430
-
431
- ## User Summary Format
432
-
433
- After detection + remediation, answer with compact status:
434
-
435
- ```text
436
- ## specialists update complete
437
-
438
- ✓ sp doctor clean
439
- ✓ package / CLI versions aligned
440
- ✓ specialist configs valid
441
- ✓ hooks wired
442
- ✓ canonical package parity checked
443
- ✓ jobs/worktrees clean
444
- ✓ SQLite healthy
445
- ✓ extensions registered
446
-
447
- [manual items, if any]
448
- ```
150
+ After refresh:
151
+ - `xt doctor --cwd <root> --json` clean or reduced to intentional custom drift,
152
+ - repo-specific follow-up actions called out only when needed,
153
+ - single-repo case stays single-repo,
154
+ - missing `xt` path falls back cleanly.