xtrm-tools 0.7.11 → 0.7.13
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.
- package/.xtrm/hooks/specialists/specialists-memory-cache-sync.mjs +57 -0
- package/.xtrm/registry.json +477 -389
- package/.xtrm/skills/default/premortem/SKILL.md +218 -0
- package/.xtrm/skills/default/releasing/SKILL.md +90 -0
- package/.xtrm/skills/default/sync-docs/SKILL.md +88 -208
- package/.xtrm/skills/default/sync-docs/scripts/pre-context.sh +17 -0
- package/.xtrm/skills/default/update-specialists/SKILL.md +448 -0
- package/.xtrm/skills/default/update-xt/SKILL.md +34 -0
- package/.xtrm/skills/default/using-kpi/SKILL.md +150 -0
- package/.xtrm/skills/default/using-specialists-v2/SKILL.md +683 -0
- package/cli/dist/index.cjs +839 -429
- package/cli/dist/index.cjs.map +1 -1
- package/cli/package.json +1 -1
- package/package.json +2 -2
- package/packages/pi-extensions/.serena/project.yml +119 -0
- package/packages/pi-extensions/extensions/pi-serena-compact/index.ts +4 -12
- package/packages/pi-extensions/extensions/xtrm-loader/index.ts +0 -1
- package/packages/pi-extensions/extensions/xtrm-ui/index.ts +201 -36
- package/packages/pi-extensions/extensions/xtrm-ui/themes/pidex-dark-flattools.json +79 -0
- package/packages/pi-extensions/extensions/xtrm-ui/themes/pidex-dark.json +85 -0
- package/packages/pi-extensions/extensions/xtrm-ui/themes/pidex-light-flattools.json +79 -0
- package/packages/pi-extensions/extensions/xtrm-ui/themes/pidex-light.json +85 -0
- package/packages/pi-extensions/package.json +1 -1
- package/packages/pi-extensions/themes/xtrm-ui/pidex-dark-flattools.json +79 -0
- package/packages/pi-extensions/themes/xtrm-ui/pidex-dark.json +3 -3
- package/packages/pi-extensions/themes/xtrm-ui/pidex-light-flattools.json +79 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: update-specialists
|
|
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
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# update-specialists
|
|
14
|
+
|
|
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`:
|
|
137
|
+
|
|
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
|
+
```
|
|
149
|
+
|
|
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.
|
|
151
|
+
|
|
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.
|
|
153
|
+
|
|
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.
|
|
155
|
+
|
|
156
|
+
## Detection
|
|
157
|
+
|
|
158
|
+
Run these in order. Report which checks pass and which drift.
|
|
159
|
+
|
|
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
|
+
```
|
|
270
|
+
|
|
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.
|
|
273
|
+
|
|
274
|
+
### Fix: Specialist configs drifted
|
|
275
|
+
|
|
276
|
+
If `sp doctor`, JSON validation, or direct diff against package canonical defaults
|
|
277
|
+
shows missing fields, wrong names, or schema mismatch:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
specialists init --sync-defaults
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
`--sync-defaults` refreshes specialists + mandatory-rules + nodes mirrors.
|
|
284
|
+
|
|
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:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
sp edit <name> --fork-from <base>
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Fix: Hooks not firing
|
|
293
|
+
|
|
294
|
+
If hooks are missing, wrong events, stale script paths, or hook files differ from
|
|
295
|
+
installed package canonical copies:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
specialists init
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
If runtime exposes a narrower hook sync command, prefer it. Use full init only
|
|
302
|
+
when hook-only sync is not enough.
|
|
303
|
+
|
|
304
|
+
### Fix: CLI not reachable
|
|
305
|
+
|
|
306
|
+
If `sp` or `specialists` is missing or incompatible:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
sp doctor
|
|
310
|
+
```
|
|
311
|
+
|
|
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:
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
specialists clean
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Then re-run `sp doctor`.
|
|
324
|
+
|
|
325
|
+
### Fix: SQLite schema drift
|
|
326
|
+
|
|
327
|
+
If doctor reports DB version mismatch or recovery issue:
|
|
328
|
+
|
|
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.
|
|
332
|
+
|
|
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.
|
|
334
|
+
|
|
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.
|
|
336
|
+
|
|
337
|
+
### Fix: metrics aggregation missing or stale
|
|
338
|
+
|
|
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`:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
# Backfill metrics for any job whose events still exist but lack a metrics row.
|
|
343
|
+
sp db extract --all-missing
|
|
344
|
+
|
|
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
|
+
```
|
|
353
|
+
|
|
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.
|
|
355
|
+
|
|
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.
|
|
361
|
+
|
|
362
|
+
### Fix: Skills/defaults differ from shipped package copy
|
|
363
|
+
|
|
364
|
+
If diff against the installed package shows `.specialists/default/`,
|
|
365
|
+
`.xtrm/skills/default/`, or `.claude/hooks/` drift from shipped canonical files:
|
|
366
|
+
|
|
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
|
|
377
|
+
|
|
378
|
+
If `quality-gates`, `pi-gitnexus`, or `pi-serena-tools` are missing:
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
specialists init --sync-skills
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
If project uses different extension packaging, re-run install step that writes
|
|
385
|
+
`.pi/settings.json`.
|
|
386
|
+
|
|
387
|
+
## Verification
|
|
388
|
+
|
|
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
|
+
```
|
|
@@ -74,6 +74,31 @@ node -e "const s=require('./.pi/settings.json'); console.log(s.skills)" 2>/dev/n
|
|
|
74
74
|
for f in .xtrm/skills/active/*; do [ -L "$f" ] || echo "NOT A SYMLINK: $f"; done
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
## Implementation Self-Check
|
|
78
|
+
|
|
79
|
+
Do not trust the surface commands alone. Before claiming that `xt init` handles
|
|
80
|
+
drift correctly, verify the underlying implementation behavior in the CLI source.
|
|
81
|
+
|
|
82
|
+
Required checks:
|
|
83
|
+
|
|
84
|
+
| File | What to verify |
|
|
85
|
+
|------|----------------|
|
|
86
|
+
| `cli/src/core/drift.ts` | Drift is classified by comparing installed user file hashes against registry hashes from the package payload |
|
|
87
|
+
| `cli/src/core/registry-scaffold.ts` | Drifted files are reported and skipped by default unless `force` is enabled |
|
|
88
|
+
| `cli/src/commands/init.ts` | `xt init` calls the registry install step with `force: false` |
|
|
89
|
+
|
|
90
|
+
What you must confirm from code before reporting success:
|
|
91
|
+
|
|
92
|
+
- `xt init` does check for local drift between the user's `.xtrm` files and the
|
|
93
|
+
package payload that bootstrapped them.
|
|
94
|
+
- That check is hash-based for registry-managed `.xtrm` files, not just a loose
|
|
95
|
+
status heuristic.
|
|
96
|
+
- `xt init -y` is non-destructive for drifted `.xtrm` files by default. It
|
|
97
|
+
preserves local edits unless a separate force path is used.
|
|
98
|
+
|
|
99
|
+
If the implementation no longer matches those rules, stop and report the mismatch
|
|
100
|
+
instead of repeating this skill's older assumptions.
|
|
101
|
+
|
|
77
102
|
## Remediation
|
|
78
103
|
|
|
79
104
|
Two commands cover almost all drift. Know which fixes what:
|
|
@@ -86,6 +111,8 @@ Two commands cover almost all drift. Know which fixes what:
|
|
|
86
111
|
### Fix: Skills symlink stale or active/ view wrong
|
|
87
112
|
|
|
88
113
|
`xt claude install` does NOT rebuild skills. Only `xt init` does (Phase 6b).
|
|
114
|
+
`xt init -y` will repair missing/outdated registry-managed files, but it will
|
|
115
|
+
preserve locally drifted `.xtrm` files by default.
|
|
89
116
|
|
|
90
117
|
```bash
|
|
91
118
|
xt init -y
|
|
@@ -159,6 +186,13 @@ node -e "const s=require('./.pi/settings.json'); console.log(s.skills.includes('
|
|
|
159
186
|
# Must output: true
|
|
160
187
|
```
|
|
161
188
|
|
|
189
|
+
Also restate the implementation-level conclusion in your report:
|
|
190
|
+
|
|
191
|
+
- `xt init` verified drift against package registry hashes
|
|
192
|
+
- local drifted `.xtrm` files were preserved by default
|
|
193
|
+
- no forced overwrite path was used unless explicitly requested
|
|
194
|
+
|
|
195
|
+
|
|
162
196
|
If `xt status` still shows drift after targeted fixes, run the full sync:
|
|
163
197
|
```bash
|
|
164
198
|
xt init
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: using-kpi
|
|
3
|
+
description: >-
|
|
4
|
+
Analyze specialist KPI data in observability SQLite. Use for runtime, payload,
|
|
5
|
+
waiting, tool-call, and outlier analysis. Token estimates use cl100k_base-style
|
|
6
|
+
approximation with ~±5% accuracy.
|
|
7
|
+
gemini-command: using-kpi
|
|
8
|
+
version: 3.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# using-kpi
|
|
12
|
+
|
|
13
|
+
KPI analysis skill for `sp db stats` / `sp db extract` data.
|
|
14
|
+
|
|
15
|
+
## Quick rule
|
|
16
|
+
|
|
17
|
+
`active_runtime_ms` = real paid runtime. Rank by that first. `elapsed_ms` is total wall time. `waiting_ms` catches forgotten keep-alives.
|
|
18
|
+
|
|
19
|
+
Token counts are approximate, cl100k_base-style, about ±5%. Bytes are exact UTF-8 size.
|
|
20
|
+
|
|
21
|
+
## Recipe 1 — specialist × model leaderboard by active cost
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
sp db stats --format json \
|
|
25
|
+
| jq -r '
|
|
26
|
+
.rows
|
|
27
|
+
| group_by([.specialist, .model])
|
|
28
|
+
| map({
|
|
29
|
+
specialist: .[0].specialist,
|
|
30
|
+
model: .[0].model,
|
|
31
|
+
jobs: length,
|
|
32
|
+
active_ms: (map((.active_runtime_ms // 0)) | add),
|
|
33
|
+
total_ms: (map((.total_runtime_ms // .elapsed_ms // 0)) | add),
|
|
34
|
+
turns: (map((.total_turns // 0)) | add),
|
|
35
|
+
tools: (map((.total_tools // 0)) | add),
|
|
36
|
+
payload_kb: (map((.payload_kb // 0)) | add)
|
|
37
|
+
})
|
|
38
|
+
| sort_by(-.active_ms, -.jobs)
|
|
39
|
+
| .[]
|
|
40
|
+
| [ .specialist, .model, .jobs, .active_ms, .total_ms, .turns, .tools, .payload_kb ]
|
|
41
|
+
| @tsv'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Recipe 2 — outliers above p95
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
sp db stats --format json \
|
|
48
|
+
| jq '
|
|
49
|
+
.rows as $rows
|
|
50
|
+
| {
|
|
51
|
+
active: ($rows | map(.active_runtime_ms // 0) | sort),
|
|
52
|
+
tools: ($rows | map(.total_tools // 0) | sort),
|
|
53
|
+
turns: ($rows | map(.total_turns // 0) | sort),
|
|
54
|
+
payload: ($rows | map(.payload_kb // 0) | sort)
|
|
55
|
+
} as $s
|
|
56
|
+
| {
|
|
57
|
+
active_p95: $s.active[(($s.active|length)*95/100|floor)],
|
|
58
|
+
tools_p95: $s.tools[(($s.tools|length)*95/100|floor)],
|
|
59
|
+
turns_p95: $s.turns[(($s.turns|length)*95/100|floor)],
|
|
60
|
+
payload_p95: $s.payload[(($s.payload|length)*95/100|floor)]
|
|
61
|
+
} as $p
|
|
62
|
+
| $rows
|
|
63
|
+
| map(select(
|
|
64
|
+
((.active_runtime_ms // 0) >= $p.active_p95) or
|
|
65
|
+
((.total_tools // 0) >= $p.tools_p95) or
|
|
66
|
+
((.total_turns // 0) >= $p.turns_p95) or
|
|
67
|
+
((.payload_kb // 0) >= $p.payload_p95)
|
|
68
|
+
))
|
|
69
|
+
| .[]
|
|
70
|
+
| [ .job_id, .specialist, .model, .active_runtime_ms, .total_tools, .total_turns, .payload_kb ]
|
|
71
|
+
| @tsv'
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Recipe 3 — payload bloat ranking
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
sp db stats --with-payload --format json \
|
|
78
|
+
| jq -r '
|
|
79
|
+
.rows
|
|
80
|
+
| group_by(.specialist)
|
|
81
|
+
| map({
|
|
82
|
+
specialist: .[0].specialist,
|
|
83
|
+
jobs: length,
|
|
84
|
+
avg_payload_kb: ((map((.payload_kb // 0)) | add) / length),
|
|
85
|
+
max_payload_kb: (map((.payload_kb // 0)) | max)
|
|
86
|
+
})
|
|
87
|
+
| sort_by(-.avg_payload_kb)
|
|
88
|
+
| .[:10]
|
|
89
|
+
| .[]
|
|
90
|
+
| [ .specialist, .jobs, (.avg_payload_kb|tostring), (.max_payload_kb|tostring) ]
|
|
91
|
+
| @tsv'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Recipe 4 — waiting-state hygiene
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
sp db stats --format json \
|
|
98
|
+
| jq -r '
|
|
99
|
+
.rows
|
|
100
|
+
| map(select((.waiting_s? // 0) != 0))
|
|
101
|
+
| map(. + {waiting_ratio: ((.waiting_ms // 0) / ((.total_runtime_ms // .elapsed_ms // 1) + 0.0))})
|
|
102
|
+
| sort_by(-.waiting_ratio, -.waiting_ms)
|
|
103
|
+
| .[]
|
|
104
|
+
| [ .job_id, .specialist, .model, (.waiting_ms|tostring), (.total_runtime_ms // .elapsed_ms|tostring), (.waiting_ratio|tostring) ]
|
|
105
|
+
| @tsv'
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Recipe 5 — tool-call distribution per specialist
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
sp db stats --format json \
|
|
112
|
+
| jq -r '
|
|
113
|
+
.rows
|
|
114
|
+
| group_by(.specialist)
|
|
115
|
+
| map({
|
|
116
|
+
specialist: .[0].specialist,
|
|
117
|
+
counts: (map(.tool_call_counts_json? // "{}")
|
|
118
|
+
| map(fromjson)
|
|
119
|
+
| add)
|
|
120
|
+
})
|
|
121
|
+
| .[]
|
|
122
|
+
| .counts
|
|
123
|
+
| to_entries
|
|
124
|
+
| sort_by(-.value)
|
|
125
|
+
| .[]
|
|
126
|
+
| [ .key, .value ]
|
|
127
|
+
| @tsv'
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Recipe 6 — payload vs active runtime correlation
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
sp db stats --with-payload --format json \
|
|
134
|
+
| jq -r '
|
|
135
|
+
.rows
|
|
136
|
+
| map(select((.payload_kb? // 0) > 0 and ((.active_runtime_ms? // 0) > 0)))
|
|
137
|
+
| map([(.payload_kb|tonumber), (.active_runtime_ms|tonumber)])
|
|
138
|
+
| if length < 2 then empty else
|
|
139
|
+
(map(.[0]) | add / length) as $mx |
|
|
140
|
+
(map(.[1]) | add / length) as $my |
|
|
141
|
+
(map((.[0]-$mx)*(.[1]-$my)) | add) /
|
|
142
|
+
((map((.[0]-$mx)^2) | add) * (map((.[1]-$my)^2) | add)) ^ 0.5
|
|
143
|
+
end'
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## References
|
|
147
|
+
|
|
148
|
+
- `docs/observability-metrics.md`
|
|
149
|
+
- `src/cli/db.ts`
|
|
150
|
+
- `src/specialist/observability-sqlite.ts`
|