@jaggerxtrm/specialists 3.15.1 → 3.15.3

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/README.md CHANGED
@@ -1,162 +1,241 @@
1
1
  # Specialists
2
2
 
3
- **One MCP server. Many specialists. Bead-first orchestration.**
3
+ **One MCP server. Many specialist agents. Bead-first orchestration.**
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@jaggerxtrm/specialists.svg)](https://www.npmjs.com/package/@jaggerxtrm/specialists)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue.svg)](https://www.typescriptlang.org/)
8
8
 
9
- **Specialists is a universal framework for defining and running specialist agents.** You can invoke the same specialist from the terminal, through MCP inside coding agents, inside autonomous multi-agent runtimes, or from scripts and CI/CD pipelines. Each run can explicitly define the model, tool access, system prompt, task input, permission level, timeout, output format, tracking behavior, memory sources, and dependency context.
9
+ Specialists is a project-scoped runtime for running focused AI agents from the CLI, MCP, scripts, CI, or HTTP sidecars. A specialist definition declares its model, tools, permission tier, prompt, skills, output contract, timeout/stall policy, worktree behavior, and tracking behavior. The orchestrator keeps task identity in a **bead**; specialists run as fresh scoped sessions that report evidence, changes, and results back to that bead.
10
10
 
11
- Specialists is built on top of the **[pi coding agent](https://github.com/Jaggerxtrm/pi-coding-agent)** as its base execution technology. That gives Specialists access to a broad provider surface across many OAuth and API-backed models, a richer lifecycle event stream for tracking session progress and tool execution, and a usable RPC protocol for orchestrating specialist runs as a stable subprocess boundary.
11
+ Specialists sits in the xt/xtrm stack:
12
12
 
13
- Specialists is intended to run inside the **xt/xtrm architecture** provided by **[xtrm-tools](https://github.com/Jaggerxtrm/xtrm-tools)**. xtrm-tools provides the worktree isolation, execution boundaries, session model, and surrounding workflow environment that Specialists expects. Specialists handles specialist execution; xtrm-tools owns the broader operator workflow and beads enforcement hooks. For tracking and coordination Specialists uses **beads** by **Steven Yegge** as the issue, dependency, and communication layer. I built a similar issue system for Mercury AACS and Terminal back in November, but Beads is already widely used and actively maintained, so xt/Specialists is built around Beads instead of carrying a separate workflow stack. When a specialist run originates from a bead, its output is written back to that same bead, so the task spec, dependency context, coordination state, and result stay inside one tight, controlled loop.
13
+ - **[pi coding agent](https://github.com/Jaggerxtrm/pi-coding-agent)** supplies the model/provider execution layer, JSONL/RPC subprocess boundary, tool events, and extension hooks.
14
+ - **[xtrm-tools](https://github.com/Jaggerxtrm/xtrm-tools)** supplies the surrounding operator workflow: worktree sessions, `.xtrm/` skills/hooks, session reports, update tooling, and workflow enforcement.
15
+ - **[beads](https://github.com/steveyegge/beads)** supplies issue IDs, dependency edges, claims, and durable task/result notes.
14
16
 
15
- A specialist is a reusable execution spec: model, allowed tools, skills, system prompt, task prompt, timeout, permission level, output format, and background-job behavior. It can run from a plain prompt, from a system+task prompt pair, or directly from an **issue/bead ID as the task source**. Dependency chains can be injected as context, centralized memory can be reused across runs, and jobs can execute in the foreground or as background processes with status, events, and results exposed through the CLI and MCP surfaces.
17
+ When a run starts from `--bead <id>`, the bead is the task prompt. Dependency context and relevant memory can be injected, the specialist output is appended back to the same bead, and edit-capable specialists work in isolated branches/worktrees that can be reviewed and merged through `sp merge` or `sp epic merge`.
16
18
 
17
19
  ---
18
20
 
19
21
  ## Vision
20
22
 
21
- Specialists turns one overloaded agent chat into a coordinated agent mind: a central orchestrator keeps task identity and evidence, while fresh specialist sessions act as scoped capabilities with their own prompts, rules, memory, and contracts. See [specialists.scheme.md](specialists.scheme.md) for diagrams comparing the single-chat model with specialist pipelines, herd memory, adaptive chains, and service specialists.
23
+ Specialists turns one overloaded agent chat into a coordinated agent mind: a central orchestrator keeps task identity, evidence, and publication control, while fresh specialist sessions act as scoped capabilities with their own prompts, rules, tools, memory, and output contracts.
24
+
25
+ The problem it solves is not just token count. Long single-agent sessions accumulate old hypotheses, partial plans, tool residue, self-review bias, and forgotten constraints. Specialists uses **contract-bound cognition** instead: write the task contract once, dispatch the right expert role with only the relevant context, require a structured handoff, and let the orchestrator decide the next step.
26
+
27
+ See [specialists.scheme.md](specialists.scheme.md) for the full diagrams and rationale. The core shape is:
28
+
29
+ ```mermaid
30
+ flowchart TD
31
+ U[User / project need] --> O[Orchestrator]
32
+ O --> B[Bead issue contract]
33
+ B --> C{Contract ready?}
34
+ C -->|no| R[Repair scope, success, constraints]
35
+ R --> B
36
+ C -->|yes| D[Dispatch specialist]
37
+
38
+ D --> E[Explorer\nfresh read-only context]
39
+ D --> G[Debugger\nfresh root-cause context]
40
+ D --> X[Executor\nfresh implementation context]
41
+ D --> T[Test-runner\nfresh validation context]
42
+ D --> S[Code-sanity / Security\nadvisory context]
43
+ D --> V[Reviewer\ncontract compliance context]
44
+
45
+ B --> E
46
+ B --> G
47
+ B --> X
48
+ B --> T
49
+ B --> S
50
+ B --> V
51
+
52
+ E --> H[Structured handoff / evidence]
53
+ G --> H
54
+ X --> H
55
+ T --> H
56
+ S --> H
57
+ V --> H
58
+ H --> O
59
+ O --> P[Merge, resume, re-review, or close]
60
+ ```
22
61
 
23
- ## Quick start
62
+ ## What you can run
24
63
 
25
- 1. Install Bun.
64
+ | Need | Specialist / surface |
65
+ |---|---|
66
+ | Map unfamiliar local code | `explorer` |
67
+ | Diagnose a bug with unknown cause | `debugger` |
68
+ | Implement a scoped change | `executor` |
69
+ | Review an executor/debugger result | `reviewer --job <exec-job>` |
70
+ | Run/classify tests | `test-runner` |
71
+ | Current library/API/GitHub research | `researcher` |
72
+ | Plan a multi-file feature into beads | `planner` |
73
+ | Check code shape before review | `code-sanity` |
74
+ | Audit security-sensitive diffs | `security-auditor` |
75
+ | Sync exactly one doc | `sync-docs` |
76
+ | Draft changelog gaps | `changelog-keeper` |
77
+ | One-shot script/HTTP generation | `sp script` / `sp serve` |
78
+
79
+ The live registry is authoritative:
26
80
 
27
81
  ```bash
28
- bun --version
29
- curl -fsSL https://bun.sh/install | bash
82
+ sp list
83
+ sp list --compact
84
+ sp list-rules
85
+ sp help
30
86
  ```
31
87
 
32
- 2. Install xtrm-tools.
88
+ ## Install and bootstrap
89
+
90
+ Specialists is **Bun-only** and expects xtrm-tools to be installed explicitly. xtrm-tools is a runtime prerequisite, not an npm dependency of this package.
33
91
 
34
92
  ```bash
93
+ # 1. Bun
94
+ curl -fsSL https://bun.sh/install | bash
95
+ bun --version
96
+
97
+ # 2. xtrm-tools
35
98
  npm install -g xtrm-tools
36
99
  xt install
37
100
  xt init
38
- ```
39
101
 
40
- 3. Install Specialists.
41
-
42
- ```bash
102
+ # 3. Specialists
43
103
  npm install -g @jaggerxtrm/specialists
44
104
  sp init
105
+ sp doctor
45
106
  sp list
46
107
  ```
47
108
 
48
- `sp` is a shorter alias for `specialists` — both commands are identical:
109
+ `sp` is an alias for `specialists`.
49
110
 
50
- ```bash
51
- sp list
52
- sp run bug-hunt --bead <id>
53
- ```
111
+ `sp init` is an interactive, human-run bootstrap. It checks for `xt` and `.xtrm/`, wires project MCP registration, hooks, skill symlinks, `.specialists/` runtime directories, and the Specialists block in `AGENTS.md`. It does **not** require copying package-owned defaults into every repo.
54
112
 
55
- Tracked work:
113
+ ## Update and drift repair
114
+
115
+ Specialists uses two distribution tracks:
116
+
117
+ | Track | Owned by | What it covers | Check / update |
118
+ |---|---|---|---|
119
+ | **Category A** runtime assets | `@jaggerxtrm/specialists` package | specialist JSON, mandatory rules, catalog, nodes, hooks shipped with the package | `sp doctor --check-drift`, `sp prune-stale-defaults --dry-run`, `sp prune-stale-defaults` |
120
+ | **Category B** filesystem assets | xtrm-tools | `.xtrm/skills`, `.claude/skills`, `.pi/skills`, hook snapshots read directly from disk | `xt doctor --cwd <repo> --json`, `xt update --repo <repo> --apply` |
121
+
122
+ `.specialists/user/` is your customization layer. `.specialists/default/` is now only for intentional pins or compatibility snapshots; stale default files are drift debt and `sp prune-stale-defaults` removes them by default. `sp init --sync-defaults` remains as a compatibility path, but it is deprecated because it creates repo-local snapshots that can drift from the package-canonical source.
123
+
124
+ ## Core tracked workflow
56
125
 
57
126
  ```bash
58
127
  bd create "Investigate auth bug" -t bug -p 1 --json
59
- specialists run bug-hunt --bead <id>
60
- specialists feed -f
61
- bd close <id> --reason "Done"
128
+ bd update <id> --claim --json
129
+
130
+ sp run debugger --bead <id> --context-depth 3
131
+ sp ps
132
+ sp feed <job-id> --follow
133
+ sp result <job-id>
134
+
135
+ # After implementation and reviewer PASS
136
+ sp merge <chain-root-bead> # standalone chain
137
+ sp epic status <epic-id> # multi-chain publication check
138
+ sp epic merge <epic-id> # canonical epic publication
139
+
140
+ bd close <id> --reason "Done" --json
62
141
  ```
63
142
 
64
- Merge worktree branches:
143
+ Ad-hoc work is still available, but tracked work should use beads:
65
144
 
66
145
  ```bash
67
- specialists merge <bead-id> # single chain or epic (topological)
68
- specialists merge <bead-id> --rebuild # rebuild after merge
146
+ sp run explorer --prompt "Map the CLI architecture"
69
147
  ```
70
148
 
71
- `specialists run` prints `[job started: <id>]` early. Normal runtime is DB-backed; `.specialists/jobs/latest` is legacy/operator-only.
149
+ ## Background jobs and monitoring
72
150
 
73
- Runtime state lives in `observability.db`; `.specialists/jobs/latest` is legacy convenience pointer only.
151
+ Normal runtime is DB-first: `.specialists/db/observability.db` stores jobs, events, and results. File mirrors under `.specialists/jobs/` are legacy/operator recovery surfaces.
74
152
 
75
- Ad-hoc work:
153
+ Useful commands:
76
154
 
77
155
  ```bash
78
- specialists run codebase-explorer --prompt "Map the CLI architecture"
156
+ sp ps # actionable dashboard
157
+ sp ps -f # TTY dashboard follow; pipes emit ANSI-free snapshots
158
+ sp feed <job-id> # full DB-backed event replay
159
+ sp feed -f # follow all active jobs
160
+ sp result <job-id> --wait
161
+ sp steer <job-id> "focus only on X"
162
+ sp resume <job-id> "continue with these findings"
163
+ sp finalize <any-chain-job> # cascade-close waiting keep-alive chain after PASS if needed
164
+ sp clean --reap-orphans --dry-run
165
+ sp clean --ps # hide terminal dashboard history without deleting DB audit rows
79
166
  ```
80
167
 
81
- ## What `specialists init` does
168
+ ## Script and service specialists
82
169
 
83
- - creates `specialists/`
84
- - creates `.specialists/` runtime dirs (`jobs/`, `ready/`)
85
- - adds `.specialists/` to `.gitignore`
86
- - injects the canonical Specialists Workflow block into `AGENTS.md`
87
- - registers the Specialists MCP server at project scope
88
-
89
- Verify bootstrap state:
170
+ Use `sp run` for interactive agent orchestration. Use the script/service surfaces when you need a synchronous, READ_ONLY, one-shot generation path:
90
171
 
91
172
  ```bash
92
- specialists status
93
- specialists doctor
173
+ sp script <name> --vars key=value --json
174
+ sp serve --port 8000 --readiness-canary warn
175
+ curl -sS http://localhost:8000/v1/generate \
176
+ -H 'content-type: application/json' \
177
+ -d '{"specialist":"hello","variables":{"name":"world"}}'
94
178
  ```
95
179
 
96
- ## Documentation map
180
+ `sp serve` is intended as a sidecar for script-class specialists. For container deployments, mount the whole `.specialists/` directory read-write, set `HOME=/pi-home`, and align container UID/GID with the host user. See [docs/specialists-service.md](docs/specialists-service.md), [docs/specialists-service-install.md](docs/specialists-service-install.md), and [docs/deploying-alongside.md](docs/deploying-alongside.md).
97
181
 
98
- `docs/` is the source of truth for detailed documentation. Start with the page that matches your task:
182
+ ## Documentation map
99
183
 
100
184
  | Need | Doc |
101
185
  |---|---|
102
- | Install and bootstrap a project | [docs/bootstrap.md](docs/bootstrap.md) |
103
- | Release notes and version history | [CHANGELOG.md](CHANGELOG.md) |
104
- | Changelog drafting specialist | [config/specialists/changelog-keeper.specialist.json](config/specialists/changelog-keeper.specialist.json) |
105
- | Run a script-class specialist over HTTP (`sp serve`) — overview & contract | [docs/specialists-service.md](docs/specialists-service.md) |
106
- | Install `sp serve` in another project (sidecar Docker / Podman) | [docs/specialists-service-install.md](docs/specialists-service-install.md) |
107
- | Build & publish the specialists-service image | [docs/release-image.md](docs/release-image.md) |
108
- | Release flow (skill + specialist) | [config/skills/releasing/SKILL.md](config/skills/releasing/SKILL.md) |
109
- | Bead-first workflow and semantics | [docs/workflow.md](docs/workflow.md) |
186
+ | Install, update, and distribution model | [docs/installation.md](docs/installation.md) |
187
+ | Project bootstrap and `sp init` | [docs/bootstrap.md](docs/bootstrap.md) |
188
+ | Bead-first workflow | [docs/workflow.md](docs/workflow.md) |
110
189
  | CLI commands and flags | [docs/cli-reference.md](docs/cli-reference.md) |
111
- | Background jobs, feed, result, stop | [docs/background-jobs.md](docs/background-jobs.md) |
112
- | Write or edit a `.specialist.yaml` | [docs/authoring.md](docs/authoring.md) |
113
- | Current built-in specialists | [docs/specialists-catalog.md](docs/specialists-catalog.md) |
114
- | MCP registration details | [docs/mcp-servers.md](docs/mcp-servers.md) |
115
- | Hook behavior | [docs/hooks.md](docs/hooks.md) |
116
- | Skills shipped in this repo | [docs/skills.md](docs/skills.md) |
117
- | xtrm / worktree integration | [docs/worktree.md](docs/worktree.md) |
118
- | RPC mode notes | [docs/pi-rpc.md](docs/pi-rpc.md) |
119
- | Pi subprocess isolation and extensions | [docs/pi-session.md](docs/pi-session.md) |
120
- | NodeSupervisor architecture, node lifecycle, and `sp node` CLI | [docs/nodes.md](docs/nodes.md) |
121
-
122
- ## Ownership model
123
-
124
- 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.
190
+ | Background jobs / `ps` / `feed` / `result` | [docs/background-jobs.md](docs/background-jobs.md) |
191
+ | Specialist JSON authoring | [docs/authoring.md](docs/authoring.md) |
192
+ | Built-in specialists | [docs/specialists-catalog.md](docs/specialists-catalog.md) |
193
+ | Tool catalog and permission resolver | [docs/manifest.md](docs/manifest.md) |
194
+ | MCP registration and tool surface | [docs/mcp-servers.md](docs/mcp-servers.md), [docs/mcp-tools.md](docs/mcp-tools.md) |
195
+ | Hooks | [docs/hooks.md](docs/hooks.md) |
196
+ | Skills | [docs/skills.md](docs/skills.md) |
197
+ | Worktrees and session close | [docs/worktrees.md](docs/worktrees.md), [docs/worktree.md](docs/worktree.md) |
198
+ | Runtime architecture | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) |
199
+ | Pi subprocess isolation / RPC boundary | [docs/pi-session.md](docs/pi-session.md), [docs/pi-rpc-boundary.md](docs/pi-rpc-boundary.md) |
200
+ | NodeSupervisor | [docs/nodes.md](docs/nodes.md) |
201
+ | Service sidecar / HTTP contract | [docs/specialists-service.md](docs/specialists-service.md) |
202
+ | Compose deployment recipe | [docs/deploying-alongside.md](docs/deploying-alongside.md) |
203
+ | Release notes | [CHANGELOG.md](CHANGELOG.md) |
125
204
 
126
205
  ## Project structure
127
206
 
128
207
  ```text
129
208
  config/
130
- ├── specialists/ canonical specialist definitions (.specialist.json)
131
- ├── mandatory-rules/ canonical rule sets injected into specialist prompts (+ README)
132
- ├── nodes/ canonical node configs
209
+ ├── specialists/ package-canonical specialist definitions (.specialist.json)
210
+ ├── mandatory-rules/ package-canonical rule sets injected into specialist prompts
211
+ ├── catalog/ package-canonical tool catalog
212
+ ├── nodes/ package-canonical node configs
133
213
  ├── hooks/ bundled hook scripts
134
- ├── skills/ repo-local skills used by specialists
135
- └── extensions/ pi extensions (future)
214
+ └── skills/ repo-local skills shipped by this package
215
+
136
216
  .specialists/
137
- ├── default/ managed mirror of canonical (from sp init --sync-defaults)
138
- ├── specialists/
139
- ├── mandatory-rules/
140
- ├── nodes/
141
- ├── hooks/
142
- └── skills/
143
- ├── user/ repo-owned customizations (overrides default + canonical)
144
- │ ├── specialists/
145
- ├── hooks/
146
- └── skills/
147
- ├── mandatory-rules/ repo-specific rule overlay (wins on set-id conflict)
148
- ├── jobs/ runtime gitignored
149
- └── ready/ runtime — gitignored
150
- src/ CLI, server, loader, runner, tools
217
+ ├── user/ repo-owned specialists and overrides (highest precedence)
218
+ ├── default/ optional pins / compatibility snapshots; prune stale files
219
+ ├── mandatory-rules/ legacy/repo rule overlay compatibility
220
+ ├── db/ runtime SQLite state (gitignored)
221
+ ├── jobs/ legacy runtime mirror (gitignored)
222
+ └── ready/ legacy ready markers (gitignored)
223
+
224
+ .xtrm/
225
+ ├── skills/ xtrm-managed skill snapshots and active links
226
+ └── hooks/ xtrm-managed hook snapshots
227
+
228
+ src/ CLI, server, loader, runner, supervisor, MCP tool
151
229
  ```
152
230
 
153
- ## Core workflow rules
231
+ ## Core rules
154
232
 
155
- - **Use `--bead` for tracked work.** The bead is the prompt source.
156
- - **Use `--prompt` for ad-hoc work only.**
157
- - `--context-depth` controls how many completed blocker levels are injected.
158
- - `--no-beads` does **not** disable bead reading.
159
- - specialists are **project-only**. User-scope specialist discovery is deprecated.
233
+ - Use `--bead` for tracked work; use `--prompt` only for quick untracked work.
234
+ - `--context-depth` controls completed dependency context injection; default is 3 for bead runs.
235
+ - `--no-beads` disables tracking bead creation/updates, but it does not disable reading the input bead when `--bead` is provided.
236
+ - Edit-capable specialists run in isolated worktrees. Review/fix passes should use `--job <exec-job>` to reuse the same workspace.
237
+ - Reviewer PASS is the publish gate. Code-sanity/security/test-runner outputs are advisory evidence, not merge approval.
238
+ - Specialists are project-scoped. User-scope specialist discovery is deprecated.
160
239
 
161
240
  ## Deprecated commands
162
241
 
@@ -164,8 +243,9 @@ These commands are still recognized for migration guidance but are no longer onb
164
243
 
165
244
  - `specialists setup`
166
245
  - `specialists install`
246
+ - `sp release prepare` / `sp release publish` (deprecated aliases; release flow is skill-driven)
167
247
 
168
- Use `specialists init` instead.
248
+ Use `sp init`, `xt update`, and the release skill flow instead.
169
249
 
170
250
  ## Development
171
251
 
@@ -173,12 +253,10 @@ Use `specialists init` instead.
173
253
  bun run build
174
254
  bun test # bun vitest run (default)
175
255
  bun run test:node # node vitest run (subprocess-safe alternative)
176
- specialists help
177
- specialists quickstart
256
+ sp help
257
+ sp quickstart
178
258
  ```
179
259
 
180
- `test:node` uses plain `node vitest run` as an alternative to `bun --bun vitest`. Useful for executor/codex subprocess chains that may trigger stall detection during vitest's tinypool worker initialization silence.
181
-
182
260
  ## License
183
261
 
184
- MIT
262
+ MIT — see [LICENSE](LICENSE).
@@ -7,7 +7,7 @@ description: >
7
7
  security checks, multi-step chains, integration-phase reconciliation,
8
8
  debugger-restitch on conflicting chains, pre-dispatch conflict-cluster
9
9
  mapping, test-failure-map epics, and questions about specialist workflow.
10
- version: 3.3
10
+ version: 3.4
11
11
  ---
12
12
 
13
13
  # Using Specialists v3
@@ -279,24 +279,75 @@ Fix three bad smells fast:
279
279
 
280
280
  What differs: orchestrator writes contract before dispatch, so specialist does less guessing and more useful work.
281
281
 
282
- ## Dependency Linking
282
+ ## Dependency Linking And Relationship Vocabulary
283
283
 
284
- Link beads with correct edge shape. The edge tells orchestrator what blocks what, what is only related, and what should auto-nest.
284
+ Link beads with correct edge shape. The edge tells orchestrator what blocks execution, what only preserves context, which bead verifies another, and which issue has been replaced. Do not overload `blocks` for follow-ups, root-cause links, verification pairs, duplicates, or restitch replacements.
285
285
 
286
- - `bd dep add <issue> <depends-on>`: issue depends on depends-on; depends-on blocks issue. Use this for hard sequencing. [source: bd dep --help]
287
- - `bd dep <blocker> --blocks <blocked>`: reverse phrasing of same edge; blocker-first reads better when thinking in blockers. [source: bd dep --help; CLAUDE.md lines 62-64]
288
- - `bd dep relate <a> <b>`: non-blocking `relates_to` link. Use for context, not order. [source: bd dep --help; CLAUDE.md lines 64, 200-204]
289
- - `bd create --parent <epic-id>`: epic-child edge; auto-names child `.1`, `.2`, and adds parent edge. Use for chain members that must live under epic. [source: CLAUDE.md lines 49-50, 154-156; bd create --help]
290
- - `bd create --deps discovered-from:<id>`: follow-up work discovered from source bead. Use when one bead reveals new tracked work. [source: CLAUDE.md lines 50, 62-65; bd create --help]
286
+ Core commands:
287
+
288
+ - `bd dep add <issue> <depends-on>`: issue depends on depends-on; depends-on blocks issue. Default type is `blocks`. Use only for hard sequencing. [source: bd dep add --help]
289
+ - `bd dep <blocker> --blocks <blocked>`: reverse phrasing of the same hard sequencing edge. [source: bd dep --help]
290
+ - `bd dep add <issue> <other> --type <type>`: store a typed relationship. Supported types: `blocks`, `tracks`, `related`, `parent-child`, `discovered-from`, `until`, `caused-by`, `validates`, `relates-to`, `supersedes`. [source: bd dep add --help]
291
+ - `bd dep relate <a> <b>` / `bd dep unrelate <a> <b>`: bidirectional non-blocking `relates_to` link. Use for context, not order. [source: bd dep --help]
292
+ - `bd create --parent <epic-id>`: epic-child edge; auto-names child `.1`, `.2`, … and adds parent ownership. Use for chain members that must live under an epic. [source: bd create --help]
293
+ - `bd create --deps discovered-from:<id>` or `bd dep add <new> <source> --type discovered-from`: follow-up work discovered from a source bead.
294
+ - `bd duplicate <new> --of <canonical>`: close duplicate issue and point at canonical. Use when two beads describe the same required work.
295
+ - `bd duplicates` / `bd find-duplicates --status open --method ai --json`: find exact or semantic duplicates before dispatching parallel chains.
296
+ - `bd supersede <old> --with <new>` or `bd dep add <new> <old> --type supersedes`: mark a replacement when a better-scoped fix bead replaces an obsolete/abandoned one.
297
+ - `bd dep cycles`, `bd dep tree <id>`, and `bd graph <id>`: sanity-check the execution graph before merge/publication.
298
+
299
+ Relationship vocabulary for specialist chains:
300
+
301
+ | Relationship | Reach for it when | Example command |
302
+ | --- | --- | --- |
303
+ | `blocks` | Hard must-happen-before sequencing: planner before executor, implementation before reviewer, restitch before publish. | `bd dep add <impl> <plan> --type blocks` |
304
+ | `tracks` | A local bead mirrors upstream or cross-project work whose status matters but is not owned here. | `bd dep add <local> external:xtrm-tools:<capability> --type tracks` |
305
+ | `related` | Loose topical association when no direction or scheduling effect is intended. Prefer `bd dep relate` for bidirectional relation. | `bd dep add <a> <b> --type related` |
306
+ | `parent-child` | Epic owns child chains. Prefer `bd create --parent <epic>` so IDs and parentage stay canonical. | `bd create --parent <epic> --title "Impl auth retry" ...` |
307
+ | `discovered-from` | Reviewer, debugger, explorer, or test-runner surfaces new follow-up work from a run. | `bd dep add <follow-up> <reviewer-bead> --type discovered-from` |
308
+ | `until` | Time-bounded or event-bounded precondition that blocks only until a stated condition lands. | `bd dep add <chain> <precondition> --type until` |
309
+ | `caused-by` | Failure bead points to the root-cause bead/cluster that explains it. Makes test-failure-map epics navigable. | `bd dep add <failing-test> <root-cause> --type caused-by` |
310
+ | `validates` | Reviewer, test-runner, code-sanity, or security-auditor bead verifies an implementation/debugger bead. | `bd dep add <review> <impl> --type validates` |
311
+ | `relates-to` | Bidirectional context edge for conflict clusters, sibling designs, or rebuttal patterns. Prefer dedicated relate command. | `bd dep relate <chain-a> <chain-b>` |
312
+ | `supersedes` | New fix/design/restitch bead replaces an older bead that should no longer be executed or merged. Prefer `bd supersede`. | `bd supersede <old> --with <new>` |
313
+
314
+ Worked high-value patterns:
315
+
316
+ ```bash
317
+ # Reviewer discovers a separate follow-up during review. Do not block the impl.
318
+ bd create --title "Follow up: tighten retry metrics" --type task --priority 3 --description "..."
319
+ bd dep add <follow-up> <review> --type discovered-from
320
+
321
+ # Test-failure-map root cause: many failures point at one underlying issue.
322
+ bd create --title "Root cause: stale fixture factory" --type bug --priority 2 --description "..."
323
+ bd dep add <failing-test-bead> <root-cause> --type caused-by
324
+
325
+ # Verification bead validates implementation. This is not a hard prerequisite edge.
326
+ bd dep add <test-runner-bead> <impl> --type validates
327
+ bd dep add <reviewer-bead> <impl> --type validates
328
+
329
+ # Replacement bead supersedes an abandoned or wrongly scoped implementation.
330
+ bd create --title "Restitch auth retry onto integration state" --type task --priority 2 --description "..."
331
+ bd supersede <old-impl> --with <restitch>
332
+
333
+ # Before merging an epic or integration branch, prove the graph is sane.
334
+ bd dep cycles
335
+ bd graph <epic> --compact
336
+ ```
291
337
 
292
338
  Use each form for a different reason:
293
339
 
294
- - `add` / `--blocks` for must-happen-before dependency.
295
- - `relate` for soft linkage with no schedule effect.
296
- - `--parent` for epic ownership and child naming.
297
- - `discovered-from:` for spawned follow-up beads.
340
+ - `blocks` / `--blocks` for must-happen-before dependency only.
341
+ - `validates` for review, test, sanity, and security evidence.
342
+ - `discovered-from` for spawned follow-up beads.
343
+ - `caused-by` for failure-to-root-cause attribution.
344
+ - `relates-to` / `bd dep relate` for soft linkage with no schedule effect.
345
+ - `parent-child` / `--parent` for epic ownership and child naming.
346
+ - `supersedes` / `bd supersede` for replacement work; `duplicate` for same-work issues.
347
+
348
+ Cross-repo consistency: keep this vocabulary aligned with the xtrm-tools triaging skill and sibling triage bead `xtrm-drkk`; both should use the same relationship names when rewiring issue graphs.
298
349
 
299
- What differs: orchestrator chooses edge type deliberately, so graph stays correct for chain execution, epic publish, and follow-up traceability.
350
+ What differs: orchestrator chooses edge type deliberately, so graph stays correct for chain execution, epic publish, duplicate cleanup, root-cause navigation, verification evidence, and follow-up traceability.
300
351
 
301
352
  ## Bead Contract By Bead Type
302
353
 
@@ -537,11 +588,25 @@ Before dispatching N parallel chains, build the file-overlap matrix:
537
588
 
538
589
  For each cluster of overlapping chains, choose **one** of:
539
590
 
540
- 1. **Serial dispatch** — execute chains in dependency order, each waits for previous to land. Slowest but cleanest.
541
- 2. **Unified bead** — collapse all chains into one bead/executor pass. Larger reviewer scope but no merge conflicts.
542
- 3. **Parallel dispatch + debugger restitch at integration** — dispatch in parallel, plan for ~40% conflict rate (empirical), budget debugger-restitch passes during integration.
591
+ 1. **Serial dispatch** — execute chains in dependency order, each waits for previous to land. Slowest but cleanest. Encode the order with `blocks`, not notes.
592
+ 2. **Unified bead** — collapse all chains into one bead/executor pass. Larger reviewer scope but no merge conflicts. Mark obsolete split beads with `bd supersede <old> --with <unified>`.
593
+ 3. **Parallel dispatch + debugger restitch at integration** — dispatch in parallel, plan for ~40% conflict rate (empirical), budget debugger-restitch passes during integration. Link overlapping siblings with `bd dep relate <chain-a> <chain-b>` so the future restitch has visible context without creating fake blockers.
594
+
595
+ Example graph rewiring:
596
+
597
+ ```bash
598
+ # soft conflict-cluster context; does not change schedule
599
+ bd dep relate <chain-a> <chain-b>
600
+
601
+ # serializing because both chains edit src/cli/update.ts
602
+ bd dep add <chain-b> <chain-a> --type blocks
603
+
604
+ # replacing scattered duplicate/split beads with one unified implementation
605
+ bd supersede <old-chain-a> --with <unified-chain>
606
+ bd supersede <old-chain-b> --with <unified-chain>
607
+ ```
543
608
 
544
- Default heuristic: if 3+ chains touch the same file, **serial-dispatch them**. Conflict-resolution time at integration usually exceeds the time saved by parallel dispatch.
609
+ Default heuristic: if 3+ chains touch the same file, **serial-dispatch them**. Conflict-resolution time at integration usually exceeds the time saved by parallel dispatch. Run `bd find-duplicates --status open --method ai --json` before launching a large wave; merge or supersede duplicate work before specialists spend tokens on it.
545
610
 
546
611
  ## Pre-Epic: Test-Failure-Map Pattern
547
612
 
@@ -560,11 +625,17 @@ Use when:
560
625
  - `CONSTRAINTS:` READ_ONLY, no source/test edits, no fix attempts.
561
626
  3. **Dispatch test-runner / explorer / debugger** for this bead READ_ONLY (or fill inline by reading the log).
562
627
  4. **Build the cluster table**: cluster name | files (counts) | representative error | root-cause hypothesis | likely-owner area | targeted validation command. Save in bead notes.
563
- 5. **Plan fix chains** off the cluster table:
628
+ 5. **Wire root-cause relationships** so the graph is navigable:
629
+ ```bash
630
+ bd dep add <failure-cluster-bead> <root-cause-bead> --type caused-by
631
+ bd dep add <test-runner-bead> <fix-bead> --type validates
632
+ ```
633
+ Use `caused-by` for attribution, not `blocks`; use `validates` for the evidence-producing test bead.
634
+ 6. **Plan fix chains** off the cluster table:
564
635
  - One chain per cluster, file scopes disjoint where possible.
565
636
  - Order by leverage (largest cluster first), then by simplicity.
566
637
  - Debugger when root cause unclear; executor when bead constraint is concrete.
567
- 6. **Save the topology insight as `bd remember`** — patterns about where a codebase's test fragility concentrates are reusable.
638
+ 7. **Save the topology insight as `bd remember`** — patterns about where a codebase's test fragility concentrates are reusable.
568
639
 
569
640
  ### Why this beats dispatch-blind
570
641
 
@@ -587,26 +658,28 @@ bd update <task> --claim
587
658
 
588
659
  # 2. Optional discovery when path is unknown
589
660
  bd create --title "Explore auth refresh path" --type task --priority 2 --description "PROBLEM: token refresh retry path is undocumented and likely drifts on failure handling. SUCCESS: evidence-backed plan names exact files, symbols, and risk. SCOPE: src/auth/refresh.ts, src/cli/login.ts, tests/unit/auth/*.test.ts. NON_GOALS: no implementation, no broad audit. CONSTRAINTS: READ_ONLY, cite files/symbols/flows, stay within live repo evidence. VALIDATION: findings cite code path and recommended sequence. OUTPUT: tracked discovery plan with stop condition."
590
- bd dep add <explore> <task>
661
+ bd dep add <explore> <task> --type discovered-from
591
662
  specialists run explorer --bead <explore> --context-depth 3
592
663
  specialists result <explore-job>
593
664
 
594
665
  # 3. Implementation
595
666
  bd create --title "Implement token refresh retry" --type task --priority 2 --description "PROBLEM: login fails after transient token refresh error because retry path returns before backoff and clear error state. SUCCESS: retry waits once, preserves session on success, and surfaces final failure clearly. SCOPE: src/auth/refresh.ts, src/cli/login.ts, tests/unit/auth/refresh.test.ts. NON_GOALS: no auth redesign, no storage migration, no UI refresh. CONSTRAINTS: preserve existing token format, keep backward-compatible error text, avoid broad retry changes elsewhere. VALIDATION: add regression test for transient failure then success; run targeted auth tests. OUTPUT: changed files, test evidence, residual risks."
596
- bd dep add <impl> <explore-or-task>
667
+ bd dep add <impl> <explore-or-task> --type blocks
597
668
  specialists run executor --bead <impl> --context-depth 3
598
669
  specialists result <exec-job>
599
670
 
600
671
  # 4. Advisory passes when diff smells risky
601
672
  bd create --title "Sanity check token retry diff" --type task --priority 2 --description "PROBLEM: auth retry diff has control-flow and state-handling smell that could hide bug. SUCCESS: findings identify concrete simplification or confirm clean shape. SCOPE: executor diff in auth refresh and login flow. NON_GOALS: no edits, no merge gate decision. CONSTRAINTS: READ_ONLY, keep feedback cheap, cite exact lines or symbols. VALIDATION: findings name concrete improvement or say OK. OUTPUT: FINDINGS with severity or OK with caveats."
673
+ bd dep add <sanity-bead> <impl> --type validates
602
674
  specialists run code-sanity --bead <sanity-bead> --job <exec-job> --context-depth 3
603
675
 
604
676
  bd create --title "Security scan token retry diff" --type task --priority 2 --description "PROBLEM: auth refresh code touches secrets and session handling, so security regression is possible. SUCCESS: findings isolate real risk surface or confirm no obvious issue. SCOPE: executor diff in auth, token storage, and login path. NON_GOALS: no edits, no package updates, no destructive scans, no live exploit tests. CONSTRAINTS: LOW permissions, scan-only, recommendations only. VALIDATION: findings cite auth/secrets/input surface and why it matters. OUTPUT: recommendations for executor to apply in separate bead."
677
+ bd dep add <security-bead> <impl> --type validates
605
678
  specialists run security-auditor --bead <security-bead> --job <exec-job> --context-depth 3
606
679
 
607
680
  # 5. Final review
608
681
  bd create --title "Review token refresh retry" --type task --priority 2 --description "PROBLEM: verify executor output against auth retry requirements. SUCCESS: PASS only if retry behavior, error handling, and tests satisfy contract. SCOPE: executor job, diff, acceptance criteria, and target auth files. NON_GOALS: do not rewrite unless explicitly asked. CONSTRAINTS: code-review mindset; findings first; verify security and sanity findings were handled. VALIDATION: inspect targeted checks and regression coverage. OUTPUT: PASS/PARTIAL/FAIL with file/line findings."
609
- bd dep add <review> <impl>
682
+ bd dep add <review> <impl> --type validates
610
683
  specialists run reviewer --bead <review> --job <exec-job> --context-depth 3
611
684
  specialists result <review-job>
612
685
 
@@ -621,7 +694,7 @@ sp merge <impl>
621
694
  bd close <task> --reason "Reviewer PASS; merged."
622
695
  ```
623
696
 
624
- Edit-capable specialists with `--bead` auto-provision a worktree. `--worktree` is accepted for clarity but usually unnecessary. Use `--job <exec-job>` for reviewer/fix passes that must enter existing executor workspace.
697
+ Edit-capable specialists with `--bead` auto-provision a clean git worktree. This does **not** provision ignored project dependency artifacts (`node_modules/`, `.venv/`, build caches). If validation tools are missing inside that worktree, have the specialist run the repo's standard bootstrap command (`make bootstrap`, `just setup`, `npm ci`, `uv sync`, etc.) or report that bootstrap is required; do not solve it by tracking dependency directories. `--worktree` is accepted for clarity but usually unnecessary. Use `--job <exec-job>` for reviewer/fix passes that must enter existing executor workspace.
625
698
 
626
699
  What differs: orchestrator carries full bead contract inline, so downstream specialists inherit the actual job shape, not a title.
627
700
 
@@ -634,8 +707,7 @@ Use epic when multiple implementation chains publish together.
634
707
  bd create --title "Epic: auth refresh hardening" --type epic --priority 2 --description "PROBLEM: login and refresh flow have retry drift, weak error surfacing, and unclear follow-up ownership. SUCCESS: epic closes with stable retry behavior, tests, docs, and clean publish. SCOPE: src/auth/*, src/cli/login.ts, tests/unit/auth/*, docs/auth-refresh.md. NON_GOALS: no auth provider swap, no storage migration, no unrelated session revamp. CONSTRAINTS: preserve token format, keep login compatible, sequence risky fixes before merge, use child beads for parallelizable slices. VALIDATION: targeted tests, code-sanity or security pass if risk appears, final reviewer PASS. OUTPUT: merged chain set with notes on remaining gaps."
635
708
 
636
709
  # Planner bead
637
- bd create --title "Plan auth refresh split" --type task --priority 2 --description "PROBLEM: epic needs disjoint chains before executor starts. SUCCESS: child beads, dependency edges, and file ownership split are explicit. SCOPE: auth refresh epic area. NON_GOALS: no code changes. CONSTRAINTS: keep chains disjoint, identify security-sensitive slice, name review order. VALIDATION: plan names beads and edges. OUTPUT: parallel-ready plan with risk notes."
638
- bd dep add <plan> <epic>
710
+ bd create --parent <epic> --title "Plan auth refresh split" --type task --priority 2 --description "PROBLEM: epic needs disjoint chains before executor starts. SUCCESS: child beads, dependency edges, and file ownership split are explicit. SCOPE: auth refresh epic area. NON_GOALS: no code changes. CONSTRAINTS: keep chains disjoint, identify security-sensitive slice, name review order. VALIDATION: plan names beads and edges. OUTPUT: parallel-ready plan with risk notes."
639
711
  specialists run planner --bead <plan> --context-depth 3
640
712
 
641
713
  # Parallel impl beads
@@ -646,6 +718,8 @@ specialists run executor --bead <impl-a> --context-depth 3
646
718
  specialists run executor --bead <impl-b> --context-depth 3
647
719
 
648
720
  # Per-chain review
721
+ bd dep add <review-a> <impl-a> --type validates
722
+ bd dep add <review-b> <impl-b> --type validates
649
723
  specialists run reviewer --bead <review-a> --job <exec-a-job> --context-depth 3
650
724
  specialists run reviewer --bead <review-b> --job <exec-b-job> --context-depth 3
651
725
 
@@ -655,6 +729,7 @@ sp finalize <review-a-job>
655
729
  sp finalize <review-b-job>
656
730
 
657
731
  # Publish
732
+ bd dep cycles # stop if relationship rewiring introduced a cycle
658
733
  sp epic status <epic> # verify derived state shows merge_ready
659
734
  sp epic merge <epic> # batch publish all chains in dependency order with tsc gate per merge
660
735
  ```
@@ -675,7 +750,7 @@ reviewer -> PASS | PARTIAL | FAIL
675
750
 
676
751
  - `PASS`: verify expected commit/diff. If reviewer's PASS appeared in its streaming output, auto-finalize already closed the chain — go straight to `sp merge` / `sp epic merge`. If PASS arrived via `sp resume`, run `sp finalize <any-chain-job-id>` first to cascade-close any waiting keep-alive members, then publish.
677
752
  - `PARTIAL`: resume same executor/debugger with exact findings, then re-review (`sp resume <reviewer-job>`).
678
- - `FAIL`: stop and decide whether to replace chain, re-scope bead, or ask operator if judgment is required.
753
+ - `FAIL`: stop and decide whether to replace chain, re-scope bead, or ask operator if judgment is required. If replacing a bad chain with a narrower one, use `bd supersede <failed-impl> --with <replacement>`; if reviewer discovered separate follow-up work, use `bd dep add <follow-up> <reviewer-bead> --type discovered-from`.
679
754
 
680
755
  Prefer resume over new fix executor when original job is waiting and context is healthy:
681
756
 
@@ -764,8 +839,8 @@ Several specialists default to over-cautious verdicts when an evidence gate look
764
839
  ### Overthinker
765
840
 
766
841
  - "Hold for operator decision" without specifying what decision is needed → push: "Cite file/line evidence for why this is a product decision rather than a mechanical resolution."
767
- - "Close as superseded by X" without verification → push: "Read the current state of `<file>` and check whether feature Y from this bead is actually present."
768
- - "Run separate small beads" or "run one big bead" without rationale → push: "Pick one and explain operationally — cost difference, conflict expectations, reviewer scope."
842
+ - "Close as superseded by X" without verification → push: "Read the current state of `<file>` and check whether feature Y from this bead is actually present." If verified, record it structurally with `bd supersede <old> --with <new>` instead of burying the replacement in notes.
843
+ - "Run separate small beads" or "run one big bead" without rationale → push: "Pick one and explain operationally — cost difference, conflict expectations, reviewer scope." If one big bead wins, mark replaced split beads with `bd supersede`; if the small beads remain parallel siblings, link overlap with `bd dep relate`, not `blocks`.
769
844
 
770
845
  ### Reviewer
771
846
 
@@ -891,6 +966,7 @@ sp merge <chain-root-bead>
891
966
  Batch publish all chains in an epic in dependency order with tsc gate between each:
892
967
 
893
968
  ```bash
969
+ bd dep cycles
894
970
  sp epic status <epic-id>
895
971
  sp epic merge <epic-id>
896
972
  ```
@@ -922,11 +998,12 @@ Use when `sp merge` / `sp epic merge` is not the right path: chains forked from
922
998
  3. For each non-overlapping chain (security/critical first, then test-baseline, then features):
923
999
  - `git merge --squash <chain-branch>`
924
1000
  - Restore noise files (see "Chain noise filter checklist" below)
925
- - **Advisory passes** before commit: if the staged diff smells overcomplicated/duplicative/type-risky, dispatch `code-sanity --job <last-exec-job-of-chain>`; if it touches auth/secrets/input/agent-config, dispatch `security-auditor --job <last-exec-job-of-chain>`. Apply findings or document why skipped.
1001
+ - **Advisory passes** before commit: if the staged diff smells overcomplicated/duplicative/type-risky, dispatch `code-sanity --job <last-exec-job-of-chain>`; if it touches auth/secrets/input/agent-config, dispatch `security-auditor --job <last-exec-job-of-chain>`. Link those beads with `bd dep add <advisory-bead> <chain-bead> --type validates`. Apply findings or document why skipped.
926
1002
  - `git commit -m "<type>(<scope>): <summary> (<bead-id>)"` — one squash commit per chain.
927
- 4. For each overlapping chain, switch to the **debugger-restitch** pattern (next section).
928
- 5. After all chains land, run E2E smoke phase (below) before declaring done.
929
- 6. Operator FF-merges integration main when satisfied.
1003
+ 4. For each overlapping chain, add `bd dep relate <overlap-a> <overlap-b>` if not already linked, then switch to the **debugger-restitch** pattern (next section).
1004
+ 5. Before publication, run `bd dep cycles`; fix any accidental cycle before `sp epic merge` or operator FF-merge.
1005
+ 6. After all chains land, run E2E smoke phase (below) before declaring done.
1006
+ 7. Operator FF-merges integration → main when satisfied.
930
1007
 
931
1008
  ### Chain noise filter checklist
932
1009
 
@@ -950,7 +1027,7 @@ If a chain commits its own `.beads` symlink (older bd-in-worktree behavior), `rm
950
1027
 
951
1028
  When chain X conflicts with already-landed chain Y on shared files, raw `git cherry-pick` will revert Y's work. The debugger-restitch pattern preserves both, but only when the debugger gets an explicit "preserve already-landed work" contract.
952
1029
 
953
- 1. **Reopen X**: `bd reopen <X> --reason="integration stitch onto post-Y state"`.
1030
+ 1. **Reopen X**: `bd reopen <X> --reason="integration stitch onto post-Y state"`. If the old X chain is no longer publishable, create a restitch bead and mark replacement explicitly: `bd supersede <X> --with <X-restitch>`. Link X and Y with `bd dep relate <X-restitch> <Y>` for conflict context; use `caused-by` only when a concrete failure bead is attributable to Y's already-landed change.
954
1031
  2. **Strengthen the bead contract** with these fields:
955
1032
  - `## CRITICAL CONSTRAINTS:` heading at the top.
956
1033
  - "Fork off `integration/<date>-orchestrator`. Verify with `git log integration/...$..HEAD` empty before any commits."
@@ -969,13 +1046,13 @@ When chain X conflicts with already-landed chain Y on shared files, raw `git che
969
1046
  git diff integration/<date>...feature/<X>-debugger -- <key-files>
970
1047
  ```
971
1048
  Confirm the debugger's diff is **additive** — no reverts of Y's lines.
972
- 5. **Advisory passes**: before landing the restitch, dispatch `code-sanity --job <debugger-job>` if the restitch added control-flow complexity, and `security-auditor --job <debugger-job>` if it touched a sensitive surface. Restitched diffs are higher-risk than fresh executor diffs because the debugger had to thread around already-landed work.
1049
+ 5. **Advisory passes**: before landing the restitch, dispatch `code-sanity --job <debugger-job>` if the restitch added control-flow complexity, and `security-auditor --job <debugger-job>` if it touched a sensitive surface. Link each advisory bead back with `bd dep add <advisory> <X-restitch-or-X> --type validates`. Restitched diffs are higher-risk than fresh executor diffs because the debugger had to thread around already-landed work.
973
1050
  6. **Land via FF or cherry-pick the named commit** (NOT the checkpoint commit). Look for the commit with the proper `<type>(<scope>):` message; ignore `checkpoint(debugger):` commits above it.
974
1051
  7. **Verify tests** before marking done.
975
1052
 
976
1053
  ### Failure mode to watch for
977
1054
 
978
- If the debugger forks off the OLD baseline (pre-Y) instead of integration, its commit will revert Y. Symptom: `git diff integration..feature/<X>-debugger -- <Y's-file>` shows DELETIONS of Y's symbols. Fix: resume the debugger with explicit "cd to a fresh worktree forked from `integration/<date>-orchestrator`" instruction. Re-verify with `git log integration..HEAD` empty.
1055
+ If the debugger forks off the OLD baseline (pre-Y) instead of integration, its commit will revert Y. Symptom: `git diff integration..feature/<X>-debugger -- <Y's-file>` shows DELETIONS of Y's symbols. Fix: resume the debugger with explicit "cd to a fresh worktree forked from `integration/<date>-orchestrator`" instruction. Re-verify with `git log integration..HEAD` empty. If the bad restitch became a tracked bead, supersede it with the corrected restitch bead so nobody merges the obsolete chain.
979
1056
 
980
1057
  ## E2E Smoke Phase
981
1058
 
@@ -1049,6 +1126,7 @@ Then choose one action:
1049
1126
  | `sp merge` refuses with "non-terminal chain jobs" after reviewer PASS | Auto-finalize did not fire (PASS arrived via `sp resume`, not streaming) | `sp finalize <any-chain-job-id>` — cascades to close every waiting keep-alive member |
1050
1127
  | `sp epic merge` says epic is "in terminal state 'failed'" | Prior `sp epic merge` hit a transient error (rebase conflict, dirty worktree) and persisted a soft `failed` marker | Clear the original conflict source, then re-run `sp epic merge` — it retries fresh, only `merged`/`abandoned` truly block |
1051
1128
  | `sp epic merge` says "rebase failed: unstaged changes" in a worktree | bd auto-export or other tooling left uncommitted changes inside the worktree | `cd .worktrees/<bead>/<bead>-executor && git stash push -u -m epic-merge-prep`, then re-run from main repo |
1129
+ | Validation fails with `command not found`, `vitest: not found`, missing Python tools, or `ERR_MODULE_NOT_FOUND` in a fresh worktree | Normal git worktree behavior: ignored dependency dirs (`node_modules/`, `.venv/`) are not copied into new worktrees | Run the repo's standard bootstrap inside that worktree (`make bootstrap`, `just setup`, `npm ci`, `uv sync`, etc.) or report bootstrap-required. Do not track dependency artifacts. |
1052
1130
  | `sp ps` shows old terminal jobs after a session | Default dashboard keeps unresolved terminal problems visible until acknowledged | `sp clean --ps --dry-run`, then `sp clean --ps` to soft-hide from default ps; use `sp ps --include-cleaned`/`--all` for audit history |
1053
1131
  | Reviewer keeps returning PARTIAL on functional contracts already met | Reviewer demanding tool-event evidence — typically obsoleted after the gate relaxation, but if it persists check the executor's `gitnexus_detect_changes` ran and use the rebuttal pattern (see Specialist Rebuttal As Routine) | Rebut with cited evidence; second FAIL = escalate |
1054
1132
  | Multiple `sp run` background launches drop silently under shell parallelism | Known launch-ceremony race | Re-check `sp ps` after each dispatch and retry the missing one; serialize when reliability matters |
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema_version": "1.0.0",
3
- "package_version": "3.15.1",
3
+ "package_version": "3.15.3",
4
4
  "shipped_skills": [
5
5
  {
6
6
  "path": "config/skills/memory-audit-transaction/SKILL.md",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  {
42
42
  "path": "config/skills/using-specialists-v3/SKILL.md",
43
- "sha256": "c8aa5dccd55fe6a461e66202a6829a3f1b9f9b4dc8c54f5c4f2847eb585b30f1"
43
+ "sha256": "643264862dfa758f85cff41a150be13d6a0c2c8ed42ded6186a3b9b16a87852b"
44
44
  },
45
45
  {
46
46
  "path": "config/skills/using-specialists/SKILL.md",
package/dist/index.js CHANGED
@@ -7013,6 +7013,9 @@ var require_data = __commonJS((exports, module) => {
7013
7013
  var require_utils = __commonJS((exports, module) => {
7014
7014
  var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
7015
7015
  var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
7016
+ var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
7017
+ var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
7018
+ var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
7016
7019
  function stringArrayToHexStripped(input) {
7017
7020
  let acc = "";
7018
7021
  let code = 0;
@@ -7206,27 +7209,77 @@ var require_utils = __commonJS((exports, module) => {
7206
7209
  }
7207
7210
  return output.join("");
7208
7211
  }
7209
- function normalizeComponentEncoding(component, esc2) {
7210
- const func = esc2 !== true ? escape : unescape;
7211
- if (component.scheme !== undefined) {
7212
- component.scheme = func(component.scheme);
7213
- }
7214
- if (component.userinfo !== undefined) {
7215
- component.userinfo = func(component.userinfo);
7216
- }
7217
- if (component.host !== undefined) {
7218
- component.host = func(component.host);
7212
+ var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
7213
+ var HOST_DELIM_RE = /[@/?#:]/g;
7214
+ var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
7215
+ function reescapeHostDelimiters(host, isIP) {
7216
+ const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
7217
+ re.lastIndex = 0;
7218
+ return host.replace(re, (ch) => HOST_DELIMS[ch]);
7219
+ }
7220
+ function normalizePercentEncoding(input, decodeUnreserved = false) {
7221
+ if (input.indexOf("%") === -1) {
7222
+ return input;
7219
7223
  }
7220
- if (component.path !== undefined) {
7221
- component.path = func(component.path);
7224
+ let output = "";
7225
+ for (let i = 0;i < input.length; i++) {
7226
+ if (input[i] === "%" && i + 2 < input.length) {
7227
+ const hex = input.slice(i + 1, i + 3);
7228
+ if (isHexPair(hex)) {
7229
+ const normalizedHex = hex.toUpperCase();
7230
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
7231
+ if (decodeUnreserved && isUnreserved(decoded)) {
7232
+ output += decoded;
7233
+ } else {
7234
+ output += "%" + normalizedHex;
7235
+ }
7236
+ i += 2;
7237
+ continue;
7238
+ }
7239
+ }
7240
+ output += input[i];
7222
7241
  }
7223
- if (component.query !== undefined) {
7224
- component.query = func(component.query);
7242
+ return output;
7243
+ }
7244
+ function normalizePathEncoding(input) {
7245
+ let output = "";
7246
+ for (let i = 0;i < input.length; i++) {
7247
+ if (input[i] === "%" && i + 2 < input.length) {
7248
+ const hex = input.slice(i + 1, i + 3);
7249
+ if (isHexPair(hex)) {
7250
+ const normalizedHex = hex.toUpperCase();
7251
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
7252
+ if (decoded !== "." && isUnreserved(decoded)) {
7253
+ output += decoded;
7254
+ } else {
7255
+ output += "%" + normalizedHex;
7256
+ }
7257
+ i += 2;
7258
+ continue;
7259
+ }
7260
+ }
7261
+ if (isPathCharacter(input[i])) {
7262
+ output += input[i];
7263
+ } else {
7264
+ output += escape(input[i]);
7265
+ }
7225
7266
  }
7226
- if (component.fragment !== undefined) {
7227
- component.fragment = func(component.fragment);
7267
+ return output;
7268
+ }
7269
+ function escapePreservingEscapes(input) {
7270
+ let output = "";
7271
+ for (let i = 0;i < input.length; i++) {
7272
+ if (input[i] === "%" && i + 2 < input.length) {
7273
+ const hex = input.slice(i + 1, i + 3);
7274
+ if (isHexPair(hex)) {
7275
+ output += "%" + hex.toUpperCase();
7276
+ i += 2;
7277
+ continue;
7278
+ }
7279
+ }
7280
+ output += escape(input[i]);
7228
7281
  }
7229
- return component;
7282
+ return output;
7230
7283
  }
7231
7284
  function recomposeAuthority(component) {
7232
7285
  const uriTokens = [];
@@ -7241,7 +7294,7 @@ var require_utils = __commonJS((exports, module) => {
7241
7294
  if (ipV6res.isIPV6 === true) {
7242
7295
  host = `[${ipV6res.escapedHost}]`;
7243
7296
  } else {
7244
- host = component.host;
7297
+ host = reescapeHostDelimiters(host, false);
7245
7298
  }
7246
7299
  }
7247
7300
  uriTokens.push(host);
@@ -7255,7 +7308,10 @@ var require_utils = __commonJS((exports, module) => {
7255
7308
  module.exports = {
7256
7309
  nonSimpleDomain,
7257
7310
  recomposeAuthority,
7258
- normalizeComponentEncoding,
7311
+ reescapeHostDelimiters,
7312
+ normalizePercentEncoding,
7313
+ normalizePathEncoding,
7314
+ escapePreservingEscapes,
7259
7315
  removeDotSegments,
7260
7316
  isIPv4,
7261
7317
  isUUID,
@@ -7440,11 +7496,11 @@ var require_schemes = __commonJS((exports, module) => {
7440
7496
 
7441
7497
  // node_modules/fast-uri/index.js
7442
7498
  var require_fast_uri = __commonJS((exports, module) => {
7443
- var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
7499
+ var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
7444
7500
  var { SCHEMES, getSchemeHandler } = require_schemes();
7445
7501
  function normalize(uri, options) {
7446
7502
  if (typeof uri === "string") {
7447
- uri = serialize(parse5(uri, options), options);
7503
+ uri = normalizeString(uri, options);
7448
7504
  } else if (typeof uri === "object") {
7449
7505
  uri = parse5(serialize(uri, options), options);
7450
7506
  }
@@ -7510,19 +7566,9 @@ var require_fast_uri = __commonJS((exports, module) => {
7510
7566
  return target;
7511
7567
  }
7512
7568
  function equal(uriA, uriB, options) {
7513
- if (typeof uriA === "string") {
7514
- uriA = unescape(uriA);
7515
- uriA = serialize(normalizeComponentEncoding(parse5(uriA, options), true), { ...options, skipEscape: true });
7516
- } else if (typeof uriA === "object") {
7517
- uriA = serialize(normalizeComponentEncoding(uriA, true), { ...options, skipEscape: true });
7518
- }
7519
- if (typeof uriB === "string") {
7520
- uriB = unescape(uriB);
7521
- uriB = serialize(normalizeComponentEncoding(parse5(uriB, options), true), { ...options, skipEscape: true });
7522
- } else if (typeof uriB === "object") {
7523
- uriB = serialize(normalizeComponentEncoding(uriB, true), { ...options, skipEscape: true });
7524
- }
7525
- return uriA.toLowerCase() === uriB.toLowerCase();
7569
+ const normalizedA = normalizeComparableURI(uriA, options);
7570
+ const normalizedB = normalizeComparableURI(uriB, options);
7571
+ return normalizedA !== undefined && normalizedB !== undefined && normalizedA.toLowerCase() === normalizedB.toLowerCase();
7526
7572
  }
7527
7573
  function serialize(cmpts, opts) {
7528
7574
  const component = {
@@ -7548,12 +7594,12 @@ var require_fast_uri = __commonJS((exports, module) => {
7548
7594
  schemeHandler.serialize(component, options);
7549
7595
  if (component.path !== undefined) {
7550
7596
  if (!options.skipEscape) {
7551
- component.path = escape(component.path);
7597
+ component.path = escapePreservingEscapes(component.path);
7552
7598
  if (component.scheme !== undefined) {
7553
7599
  component.path = component.path.split("%3A").join(":");
7554
7600
  }
7555
7601
  } else {
7556
- component.path = unescape(component.path);
7602
+ component.path = normalizePercentEncoding(component.path);
7557
7603
  }
7558
7604
  }
7559
7605
  if (options.reference !== "suffix" && component.scheme) {
@@ -7588,7 +7634,16 @@ var require_fast_uri = __commonJS((exports, module) => {
7588
7634
  return uriTokens.join("");
7589
7635
  }
7590
7636
  var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
7591
- function parse5(uri, opts) {
7637
+ function getParseError(parsed, matches) {
7638
+ if (matches[2] !== undefined && parsed.path && parsed.path[0] !== "/") {
7639
+ return 'URI path must start with "/" when authority is present.';
7640
+ }
7641
+ if (typeof parsed.port === "number" && (parsed.port < 0 || parsed.port > 65535)) {
7642
+ return "URI port is malformed.";
7643
+ }
7644
+ return;
7645
+ }
7646
+ function parseWithStatus(uri, opts) {
7592
7647
  const options = Object.assign({}, opts);
7593
7648
  const parsed = {
7594
7649
  scheme: undefined,
@@ -7599,6 +7654,7 @@ var require_fast_uri = __commonJS((exports, module) => {
7599
7654
  query: undefined,
7600
7655
  fragment: undefined
7601
7656
  };
7657
+ let malformedAuthorityOrPort = false;
7602
7658
  let isIP = false;
7603
7659
  if (options.reference === "suffix") {
7604
7660
  if (options.scheme) {
@@ -7619,6 +7675,11 @@ var require_fast_uri = __commonJS((exports, module) => {
7619
7675
  if (isNaN(parsed.port)) {
7620
7676
  parsed.port = matches[5];
7621
7677
  }
7678
+ const parseError = getParseError(parsed, matches);
7679
+ if (parseError !== undefined) {
7680
+ parsed.error = parsed.error || parseError;
7681
+ malformedAuthorityOrPort = true;
7682
+ }
7622
7683
  if (parsed.host) {
7623
7684
  const ipv4result = isIPv4(parsed.host);
7624
7685
  if (ipv4result === false) {
@@ -7657,14 +7718,18 @@ var require_fast_uri = __commonJS((exports, module) => {
7657
7718
  parsed.scheme = unescape(parsed.scheme);
7658
7719
  }
7659
7720
  if (parsed.host !== undefined) {
7660
- parsed.host = unescape(parsed.host);
7721
+ parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
7661
7722
  }
7662
7723
  }
7663
7724
  if (parsed.path) {
7664
- parsed.path = escape(unescape(parsed.path));
7725
+ parsed.path = normalizePathEncoding(parsed.path);
7665
7726
  }
7666
7727
  if (parsed.fragment) {
7667
- parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
7728
+ try {
7729
+ parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
7730
+ } catch {
7731
+ parsed.error = parsed.error || "URI malformed";
7732
+ }
7668
7733
  }
7669
7734
  }
7670
7735
  if (schemeHandler && schemeHandler.parse) {
@@ -7673,7 +7738,29 @@ var require_fast_uri = __commonJS((exports, module) => {
7673
7738
  } else {
7674
7739
  parsed.error = parsed.error || "URI can not be parsed.";
7675
7740
  }
7676
- return parsed;
7741
+ return { parsed, malformedAuthorityOrPort };
7742
+ }
7743
+ function parse5(uri, opts) {
7744
+ return parseWithStatus(uri, opts).parsed;
7745
+ }
7746
+ function normalizeString(uri, opts) {
7747
+ return normalizeStringWithStatus(uri, opts).normalized;
7748
+ }
7749
+ function normalizeStringWithStatus(uri, opts) {
7750
+ const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
7751
+ return {
7752
+ normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
7753
+ malformedAuthorityOrPort
7754
+ };
7755
+ }
7756
+ function normalizeComparableURI(uri, opts) {
7757
+ if (typeof uri === "string") {
7758
+ const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts);
7759
+ return malformedAuthorityOrPort ? undefined : normalized;
7760
+ }
7761
+ if (typeof uri === "object") {
7762
+ return serialize(uri, opts);
7763
+ }
7677
7764
  }
7678
7765
  var fastUri = {
7679
7766
  SCHEMES,
@@ -7808,7 +7895,7 @@ var require_core = __commonJS((exports) => {
7808
7895
  constructor(opts = {}) {
7809
7896
  this.schemas = {};
7810
7897
  this.refs = {};
7811
- this.formats = {};
7898
+ this.formats = Object.create(null);
7812
7899
  this._compilations = new Set;
7813
7900
  this._loading = {};
7814
7901
  this._cache = new Map;
@@ -15332,8 +15419,10 @@ ${cb}` : comment;
15332
15419
  }
15333
15420
  }
15334
15421
  if (afterDoc) {
15335
- Array.prototype.push.apply(doc2.errors, this.errors);
15336
- Array.prototype.push.apply(doc2.warnings, this.warnings);
15422
+ for (let i = 0;i < this.errors.length; ++i)
15423
+ doc2.errors.push(this.errors[i]);
15424
+ for (let i = 0;i < this.warnings.length; ++i)
15425
+ doc2.warnings.push(this.warnings[i]);
15337
15426
  } else {
15338
15427
  doc2.errors = this.errors;
15339
15428
  doc2.warnings = this.warnings;
@@ -16045,7 +16134,7 @@ var require_lexer = __commonJS((exports) => {
16045
16134
  const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true));
16046
16135
  this.indentNext = this.indentValue + 1;
16047
16136
  this.indentValue += n;
16048
- return yield* this.parseBlockStart();
16137
+ return "block-start";
16049
16138
  }
16050
16139
  return "doc";
16051
16140
  }
@@ -16352,26 +16441,37 @@ var require_lexer = __commonJS((exports) => {
16352
16441
  return 0;
16353
16442
  }
16354
16443
  *pushIndicators() {
16355
- switch (this.charAt(0)) {
16356
- case "!":
16357
- return (yield* this.pushTag()) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators());
16358
- case "&":
16359
- return (yield* this.pushUntil(isNotAnchorChar)) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators());
16360
- case "-":
16361
- case "?":
16362
- case ":": {
16363
- const inFlow = this.flowLevel > 0;
16364
- const ch1 = this.charAt(1);
16365
- if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) {
16366
- if (!inFlow)
16367
- this.indentNext = this.indentValue + 1;
16368
- else if (this.flowKey)
16369
- this.flowKey = false;
16370
- return (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators());
16444
+ let n = 0;
16445
+ loop:
16446
+ while (true) {
16447
+ switch (this.charAt(0)) {
16448
+ case "!":
16449
+ n += yield* this.pushTag();
16450
+ n += yield* this.pushSpaces(true);
16451
+ continue loop;
16452
+ case "&":
16453
+ n += yield* this.pushUntil(isNotAnchorChar);
16454
+ n += yield* this.pushSpaces(true);
16455
+ continue loop;
16456
+ case "-":
16457
+ case "?":
16458
+ case ":": {
16459
+ const inFlow = this.flowLevel > 0;
16460
+ const ch1 = this.charAt(1);
16461
+ if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) {
16462
+ if (!inFlow)
16463
+ this.indentNext = this.indentValue + 1;
16464
+ else if (this.flowKey)
16465
+ this.flowKey = false;
16466
+ n += yield* this.pushCount(1);
16467
+ n += yield* this.pushSpaces(true);
16468
+ continue loop;
16469
+ }
16470
+ }
16371
16471
  }
16472
+ break loop;
16372
16473
  }
16373
- }
16374
- return 0;
16474
+ return n;
16375
16475
  }
16376
16476
  *pushTag() {
16377
16477
  if (this.charAt(1) === "<") {
@@ -16525,6 +16625,13 @@ var require_parser = __commonJS((exports) => {
16525
16625
  while (prev[++i]?.type === "space") {}
16526
16626
  return prev.splice(i, prev.length);
16527
16627
  }
16628
+ function arrayPushArray(target, source) {
16629
+ if (source.length < 1e5)
16630
+ Array.prototype.push.apply(target, source);
16631
+ else
16632
+ for (let i = 0;i < source.length; ++i)
16633
+ target.push(source[i]);
16634
+ }
16528
16635
  function fixFlowSeqItems(fc) {
16529
16636
  if (fc.start.type === "flow-seq-start") {
16530
16637
  for (const it of fc.items) {
@@ -16534,11 +16641,11 @@ var require_parser = __commonJS((exports) => {
16534
16641
  delete it.key;
16535
16642
  if (isFlowToken(it.value)) {
16536
16643
  if (it.value.end)
16537
- Array.prototype.push.apply(it.value.end, it.sep);
16644
+ arrayPushArray(it.value.end, it.sep);
16538
16645
  else
16539
16646
  it.value.end = it.sep;
16540
16647
  } else
16541
- Array.prototype.push.apply(it.start, it.sep);
16648
+ arrayPushArray(it.start, it.sep);
16542
16649
  delete it.sep;
16543
16650
  }
16544
16651
  }
@@ -16878,7 +16985,7 @@ var require_parser = __commonJS((exports) => {
16878
16985
  const prev = map2.items[map2.items.length - 2];
16879
16986
  const end = prev?.value?.end;
16880
16987
  if (Array.isArray(end)) {
16881
- Array.prototype.push.apply(end, it.start);
16988
+ arrayPushArray(end, it.start);
16882
16989
  end.push(this.sourceToken);
16883
16990
  map2.items.pop();
16884
16991
  return;
@@ -17066,7 +17173,7 @@ var require_parser = __commonJS((exports) => {
17066
17173
  const prev = seq.items[seq.items.length - 2];
17067
17174
  const end = prev?.value?.end;
17068
17175
  if (Array.isArray(end)) {
17069
- Array.prototype.push.apply(end, it.start);
17176
+ arrayPushArray(end, it.start);
17070
17177
  end.push(this.sourceToken);
17071
17178
  seq.items.pop();
17072
17179
  return;
@@ -31951,7 +32058,7 @@ async function run14() {
31951
32058
  const launchStartedAt = Date.now();
31952
32059
  const innerArgs = process.argv.slice(2).filter((a) => a !== "--background");
31953
32060
  const cmd = `${process.execPath} ${process.argv[1]} ${innerArgs.map(shellQuote2).join(" ")}`;
31954
- const tmuxCmd = `/bin/bash -lc ${shellQuote2(`cd ${shellQuote2(cwd)} && exec ${cmd}`)}`;
32061
+ const tmuxCmd = `/bin/bash -c ${shellQuote2(`cd ${shellQuote2(cwd)} && exec ${cmd}`)}`;
31955
32062
  let childPid;
31956
32063
  let childExitCode;
31957
32064
  let childExitPromise;
package/dist/lib.js CHANGED
@@ -4855,8 +4855,10 @@ ${cb}` : comment;
4855
4855
  }
4856
4856
  }
4857
4857
  if (afterDoc) {
4858
- Array.prototype.push.apply(doc.errors, this.errors);
4859
- Array.prototype.push.apply(doc.warnings, this.warnings);
4858
+ for (let i = 0;i < this.errors.length; ++i)
4859
+ doc.errors.push(this.errors[i]);
4860
+ for (let i = 0;i < this.warnings.length; ++i)
4861
+ doc.warnings.push(this.warnings[i]);
4860
4862
  } else {
4861
4863
  doc.errors = this.errors;
4862
4864
  doc.warnings = this.warnings;
@@ -5568,7 +5570,7 @@ var require_lexer = __commonJS((exports) => {
5568
5570
  const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true));
5569
5571
  this.indentNext = this.indentValue + 1;
5570
5572
  this.indentValue += n;
5571
- return yield* this.parseBlockStart();
5573
+ return "block-start";
5572
5574
  }
5573
5575
  return "doc";
5574
5576
  }
@@ -5875,26 +5877,37 @@ var require_lexer = __commonJS((exports) => {
5875
5877
  return 0;
5876
5878
  }
5877
5879
  *pushIndicators() {
5878
- switch (this.charAt(0)) {
5879
- case "!":
5880
- return (yield* this.pushTag()) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators());
5881
- case "&":
5882
- return (yield* this.pushUntil(isNotAnchorChar)) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators());
5883
- case "-":
5884
- case "?":
5885
- case ":": {
5886
- const inFlow = this.flowLevel > 0;
5887
- const ch1 = this.charAt(1);
5888
- if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) {
5889
- if (!inFlow)
5890
- this.indentNext = this.indentValue + 1;
5891
- else if (this.flowKey)
5892
- this.flowKey = false;
5893
- return (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)) + (yield* this.pushIndicators());
5880
+ let n = 0;
5881
+ loop:
5882
+ while (true) {
5883
+ switch (this.charAt(0)) {
5884
+ case "!":
5885
+ n += yield* this.pushTag();
5886
+ n += yield* this.pushSpaces(true);
5887
+ continue loop;
5888
+ case "&":
5889
+ n += yield* this.pushUntil(isNotAnchorChar);
5890
+ n += yield* this.pushSpaces(true);
5891
+ continue loop;
5892
+ case "-":
5893
+ case "?":
5894
+ case ":": {
5895
+ const inFlow = this.flowLevel > 0;
5896
+ const ch1 = this.charAt(1);
5897
+ if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) {
5898
+ if (!inFlow)
5899
+ this.indentNext = this.indentValue + 1;
5900
+ else if (this.flowKey)
5901
+ this.flowKey = false;
5902
+ n += yield* this.pushCount(1);
5903
+ n += yield* this.pushSpaces(true);
5904
+ continue loop;
5905
+ }
5906
+ }
5894
5907
  }
5908
+ break loop;
5895
5909
  }
5896
- }
5897
- return 0;
5910
+ return n;
5898
5911
  }
5899
5912
  *pushTag() {
5900
5913
  if (this.charAt(1) === "<") {
@@ -6048,6 +6061,13 @@ var require_parser = __commonJS((exports) => {
6048
6061
  while (prev[++i]?.type === "space") {}
6049
6062
  return prev.splice(i, prev.length);
6050
6063
  }
6064
+ function arrayPushArray(target, source) {
6065
+ if (source.length < 1e5)
6066
+ Array.prototype.push.apply(target, source);
6067
+ else
6068
+ for (let i = 0;i < source.length; ++i)
6069
+ target.push(source[i]);
6070
+ }
6051
6071
  function fixFlowSeqItems(fc) {
6052
6072
  if (fc.start.type === "flow-seq-start") {
6053
6073
  for (const it of fc.items) {
@@ -6057,11 +6077,11 @@ var require_parser = __commonJS((exports) => {
6057
6077
  delete it.key;
6058
6078
  if (isFlowToken(it.value)) {
6059
6079
  if (it.value.end)
6060
- Array.prototype.push.apply(it.value.end, it.sep);
6080
+ arrayPushArray(it.value.end, it.sep);
6061
6081
  else
6062
6082
  it.value.end = it.sep;
6063
6083
  } else
6064
- Array.prototype.push.apply(it.start, it.sep);
6084
+ arrayPushArray(it.start, it.sep);
6065
6085
  delete it.sep;
6066
6086
  }
6067
6087
  }
@@ -6401,7 +6421,7 @@ var require_parser = __commonJS((exports) => {
6401
6421
  const prev = map.items[map.items.length - 2];
6402
6422
  const end = prev?.value?.end;
6403
6423
  if (Array.isArray(end)) {
6404
- Array.prototype.push.apply(end, it.start);
6424
+ arrayPushArray(end, it.start);
6405
6425
  end.push(this.sourceToken);
6406
6426
  map.items.pop();
6407
6427
  return;
@@ -6589,7 +6609,7 @@ var require_parser = __commonJS((exports) => {
6589
6609
  const prev = seq.items[seq.items.length - 2];
6590
6610
  const end = prev?.value?.end;
6591
6611
  if (Array.isArray(end)) {
6592
- Array.prototype.push.apply(end, it.start);
6612
+ arrayPushArray(end, it.start);
6593
6613
  end.push(this.sourceToken);
6594
6614
  seq.items.pop();
6595
6615
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/run.ts"],"names":[],"mappings":"AA6gBA,wBAAgB,kCAAkC,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,SAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA+FrG;AAGD,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAmXzC"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/run.ts"],"names":[],"mappings":"AA6gBA,wBAAgB,kCAAkC,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,SAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA+FrG;AAGD,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAsXzC"}
@@ -44,7 +44,7 @@ export declare function evaluateReadiness(opts: ReadinessCheckOptions): Promise<
44
44
  }>;
45
45
  export declare function checkPiHelpForFlags(flags?: string[]): ReadinessReason | undefined;
46
46
  export declare function startServe(argv?: string[]): Promise<{
47
- server: import("http").Server<typeof IncomingMessage, typeof ServerResponse>;
47
+ server: import("node:http").Server<typeof IncomingMessage, typeof ServerResponse>;
48
48
  args: ServeArgs;
49
49
  db: import("../specialist/observability-sqlite.js").ObservabilitySqliteClient | null;
50
50
  readinessState: ReadinessState;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaggerxtrm/specialists",
3
- "version": "3.15.1",
3
+ "version": "3.15.3",
4
4
  "description": "OmniSpecialist — 7-tool MCP orchestration layer powered by the Specialist System. Discover and execute .specialist.yaml files across project/user/system scopes via pi.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/types/lib.d.ts",
@@ -74,13 +74,13 @@
74
74
  },
75
75
  "dependencies": {
76
76
  "@modelcontextprotocol/sdk": "^1.29.0",
77
- "yaml": "2.8.4",
77
+ "yaml": "2.9.0",
78
78
  "zod": "^3.25.76",
79
79
  "zod-to-json-schema": "^3.24.6"
80
80
  },
81
81
  "devDependencies": {
82
- "@types/bun": "1.3.10",
83
- "@types/node": "^24.0.0",
82
+ "@types/bun": "1.3.14",
83
+ "@types/node": "^25.8.0",
84
84
  "@vitest/coverage-v8": "^4.1.6",
85
85
  "tsx": "^4.20.6",
86
86
  "typescript": "^5.0.0",
@@ -90,8 +90,10 @@
90
90
  "bun": ">=1.0.0"
91
91
  },
92
92
  "overrides": {
93
+ "@hono/node-server": "^1.19.13",
93
94
  "fast-uri": "^3.1.2",
95
+ "hono": "^4.12.18",
94
96
  "ip-address": "^10.2.0",
95
- "hono": "^4.12.18"
97
+ "path-to-regexp": "^8.4.0"
96
98
  }
97
99
  }