sortie-dogs 0.9.9 → 0.9.11

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,13 +1,13 @@
1
1
  # Sortie-dogs
2
2
 
3
- **Add a bounded, cost-aware execution loop to OpenCode without taking OpenCode over.**
4
-
5
- Sortie-dogs is an opt-in overlay, not a replacement. It stays passive until you
6
- invoke `/sortie` or select `dog-coordinator`. Standard OpenCode agents and
7
- unrelated sessions remain available and unchanged.
8
-
9
- > **Project status: Beta.** v0.9.x is under active stabilization. Runtime
10
- > behavior, configuration, and runtime assets may still change before 1.0.
3
+ **Add a bounded, cost-aware execution loop to OpenCode without taking OpenCode over.**
4
+
5
+ Sortie-dogs is an opt-in overlay, not a replacement. It stays passive until you
6
+ invoke `/sortie` or select `dog-coordinator`. Standard OpenCode agents and
7
+ unrelated sessions remain available and unchanged.
8
+
9
+ > **Project status: Beta.** v0.9.x is under active stabilization. Runtime
10
+ > behavior, configuration, and runtime assets may still change before 1.0.
11
11
 
12
12
  [![npm](https://img.shields.io/npm/v/sortie-dogs)](https://www.npmjs.com/package/sortie-dogs)
13
13
  [![license](https://img.shields.io/npm/l/sortie-dogs)](LICENSE)
@@ -15,63 +15,96 @@ unrelated sessions remain available and unchanged.
15
15
 
16
16
  ![Sortie-dogs coordinating a bounded implementation workflow](https://raw.githubusercontent.com/zufall-upon/Sortie-dogs/main/docs/assets/sortie-workflow.gif)
17
17
 
18
- Sortie-dogs turns selected work into a scoped plan, optional evidence gathering,
19
- bounded implementation, canonical validation, and evidence-backed completion.
18
+ Sortie-dogs turns selected work into a scoped plan, optional evidence gathering,
19
+ bounded implementation, canonical validation, and evidence-backed completion.
20
20
 
21
21
  Requirements: Node.js 22.6 or newer, npm, and OpenCode.
22
22
 
23
- Guides: [日本語](docs/guide-ja.md) · [简体中文](docs/guide-zh-CN.md) · [CLI testing](docs/cli-testing.md)
24
-
25
- Release: [v0.9.9](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.9.9)
26
-
27
- ## Why Sortie-dogs?
28
-
29
- ### Invisible until invited
30
-
31
- Use normal OpenCode normally. Sortie activates only for `/sortie` or
32
- `dog-coordinator`; it does not disable or replace OpenCode's standard agents.
33
-
34
- ### Spend strong models only where they matter
35
-
36
- Lower-cost models handle bounded retrieval and parallel volume work. Stronger
37
- models are reserved for implementation, escalation, and independent review.
38
-
39
- ### Return with proof
40
-
41
- Writes stay scoped, and completion requires validation evidence. Every completed
42
- run can return a concise Speed / Cost / Proof debrief.
43
-
44
- ## Designed to coexist with OpenCode
45
-
46
- Sortie-dogs adds a workflow to your existing setup rather than replacing it.
47
-
48
- - It does not disable OpenCode's native agents or replace standard roles such as
49
- `build`, `plan`, `explore`, or `general`.
50
- - Ordinary sessions are not automatically converted into Sortie workflows.
51
- - Project-local initialization is the recommended setup and does not change
52
- user settings.
53
- - Global runtime availability and cross-project reflection require separate,
54
- explicit opt-in. Reflection is disabled by default.
55
- - Unknown or user-owned runtime files are preserved rather than overwritten.
56
- - Manual removal targets only known Sortie-owned runtime assets.
57
-
58
- Use OpenCode normally. Invoke the pack only when you want it.
59
-
60
- ## Quick start
61
-
62
- Install the public npm package in the project and generate the project-local
63
- OpenCode runtime files:
23
+ Guides: [日本語](docs/guide-ja.md) · [简体中文](docs/guide-zh-CN.md) · [テスト実行](docs/testing.md) · [CLI testing](docs/cli-testing.md)
24
+
25
+ Release: [v0.9.11](https://github.com/zufall-upon/Sortie-dogs/releases/tag/v0.9.11)
26
+
27
+ ## Provisional quality–cost position
28
+
29
+ **Reference values, not a successful benchmark claim.** Quality and end-to-end completion
30
+ problems remain. The latest completed qualification attempt ended at `IN_PROGRESS`, so its official
31
+ verifier was not run. Further benchmarks are frozen while completion defects are repaired.
32
+
33
+ The last complete measured **Bare OpenCode vs Sortie** pair below used one frozen task,
34
+ `datacurve/anko-typed-variable-bindings`, on 2026-09-10. It used Sortie **v0.9.5**, not the
35
+ current release. Both candidates failed the official verifier.
36
+
37
+ | Metric · one task, one trial per arm | Bare OpenCode | Sortie v0.9.5 |
38
+ | --- | ---: | ---: |
39
+ | Verified PASS | 0/1 | 0/1 |
40
+ | Task-check completion · F2P | 55.6% · 5/9 | 88.9% · 8/9 |
41
+ | Retained checks · P2P | 94/94 | 93/94 |
42
+ | Estimated API-equivalent total cost | $5.42 | $1.46 |
43
+ | Median agent wall · n=1 | 28.7 min | 10.0 min |
44
+ | Premium-model token share · Sol | 100% | 20.1% |
45
+
46
+ Later Sortie-only evidence is weaker: the v0.9.9 recovery candidate passed **5/9** task
47
+ checks with **0/1 Verified PASS**; `0.9.11-bench.2` did not reach a gradeable completion.
48
+ Those attempts are not pooled into the historical pair above.
49
+
50
+ ![Historical quality–cost reference: Bare at $5.42 and 55.6% task-check completion; Sortie v0.9.5 at $1.46 and 88.9%. Neither achieved Verified PASS.](docs/assets/quality-cost-reference.svg)
51
+
52
+ The goal is **higher OpenCode task success with selective use of premium models**.
53
+ These reference observations do not yet establish that success-rate claim: Sortie missed
54
+ one task check and regressed one retained check. Codex, Pi, and Oh My OpenCode belong to
55
+ separate methodologies and are not assigned comparable positions on this chart.
56
+
57
+ [Definitions, frozen inputs, current failure status, and limitations](docs/benchmark-reference.md)
58
+ · [Machine-readable reference values](docs/benchmarks/provisional-reference.json)
59
+
60
+ ## Why Sortie-dogs?
61
+
62
+ ### Invisible until invited
63
+
64
+ Use normal OpenCode normally. Sortie activates only for `/sortie` or
65
+ `dog-coordinator`; it does not disable or replace OpenCode's standard agents.
66
+
67
+ ### Spend strong models only where they matter
68
+
69
+ Lower-cost models handle bounded retrieval and parallel volume work. Stronger
70
+ models are reserved for implementation, escalation, and independent review.
71
+
72
+ ### Return with proof
73
+
74
+ Writes stay scoped, and completion requires validation evidence. Every completed
75
+ run can return a concise Speed / Cost / Proof debrief.
76
+
77
+ ## Designed to coexist with OpenCode
78
+
79
+ Sortie-dogs adds a workflow to your existing setup rather than replacing it.
80
+
81
+ - It does not disable OpenCode's native agents or replace standard roles such as
82
+ `build`, `plan`, `explore`, or `general`.
83
+ - Ordinary sessions are not automatically converted into Sortie workflows.
84
+ - Project-local initialization is the recommended setup and does not change
85
+ user settings.
86
+ - Global runtime availability and cross-project reflection require separate,
87
+ explicit opt-in. Reflection is disabled by default.
88
+ - Unknown or user-owned runtime files are preserved rather than overwritten.
89
+ - Manual removal targets only known Sortie-owned runtime assets.
90
+
91
+ Use OpenCode normally. Invoke the pack only when you want it.
92
+
93
+ ## Quick start
94
+
95
+ Install the public npm package in the project and generate the project-local
96
+ OpenCode runtime files:
64
97
 
65
98
  ```sh
66
99
  npm install --save-dev sortie-dogs
67
- npx sortie-dogs init .
68
- ```
69
-
70
- This is the recommended setup. Runtime assets stay project-local.
71
-
72
- Installing the runtime assets does not load the plugin, and without the plugin
73
- every role runs on whichever model the caller happened to use. Add the package
74
- to the `plugin` array in the project's `.opencode/opencode.json`:
100
+ npx sortie-dogs init .
101
+ ```
102
+
103
+ This is the recommended setup. Runtime assets stay project-local.
104
+
105
+ Installing the runtime assets does not load the plugin, and without the plugin
106
+ every role runs on whichever model the caller happened to use. Add the package
107
+ to the `plugin` array in the project's `.opencode/opencode.json`:
75
108
 
76
109
  ```json
77
110
  {
@@ -82,8 +115,8 @@ to the `plugin` array in the project's `.opencode/opencode.json`:
82
115
  Restart OpenCode afterwards. A `plugin` entry must name the package, not a
83
116
  subpath: `sortie-dogs/plugin` is an import specifier, not a plugin specifier.
84
117
 
85
- `dog-coordinator` defaults to `openai/gpt-5.6-terra` with the `high` variant; `dog-scout` defaults to
86
- `openai/gpt-5.6-luna`. To pin either role to another model, save this
118
+ `dog-coordinator` defaults to `openai/gpt-5.6-terra` with the `high` variant; `dog-scout` defaults to
119
+ `openai/gpt-5.6-luna`. To pin either role to another model, save this
87
120
  as `.opencode/sortie-dogs.json`:
88
121
 
89
122
  ```json
@@ -166,83 +199,83 @@ Optional settings in `.opencode/sortie-dogs.json`:
166
199
  {
167
200
  "operationManifestPath": "operation-manifest.json",
168
201
  "handoffPaths": ["handoff.json"],
169
- "readOnlyTools": ["my_mcp_search"],
170
- "dedicatedWorkerModel": { "model": "provider/model", "variant": "deep" },
171
- "continuation": { "enabled": true, "maxAutoContinues": 10 },
172
- "reflection": {
173
- "enabled": false,
174
- "layers": { "run": true, "project": true, "global": false },
175
- "maxInjectedTokens": 500
176
- }
177
- }
178
- ```
179
-
180
- The same schema may be saved globally as
181
- `~/.config/opencode/sortie-dogs.json` (on Windows,
182
- `%USERPROFILE%\.config\opencode\sortie-dogs.json`). Precedence is built-in
183
- defaults, global file, project file, `SORTIE_DOGS_CONFIG`, then plugin factory
184
- options. OpenCode plugin normalization may omit factory options, so use the
185
- global file for durable global settings.
202
+ "readOnlyTools": ["my_mcp_search"],
203
+ "dedicatedWorkerModel": { "model": "provider/model", "variant": "deep" },
204
+ "continuation": { "enabled": true, "maxAutoContinues": 10 },
205
+ "reflection": {
206
+ "enabled": false,
207
+ "layers": { "run": true, "project": true, "global": false },
208
+ "maxInjectedTokens": 500
209
+ }
210
+ }
211
+ ```
212
+
213
+ The same schema may be saved globally as
214
+ `~/.config/opencode/sortie-dogs.json` (on Windows,
215
+ `%USERPROFILE%\.config\opencode\sortie-dogs.json`). Precedence is built-in
216
+ defaults, global file, project file, `SORTIE_DOGS_CONFIG`, then plugin factory
217
+ options. OpenCode plugin normalization may omit factory options, so use the
218
+ global file for durable global settings.
186
219
 
187
220
  - `operationManifestPath` moves the manifest; the path is project-relative.
188
- - `handoffPaths` lists the handoff files the plugin inspects. A worker can only
221
+ - `handoffPaths` lists the handoff files the plugin inspects. A worker can only
189
222
  bind after one of these files passes inspection, so an empty list disables
190
223
  binding entirely. Relative entries are also candidate-relative in a nested
191
224
  repository: a child candidate may use its own `handoff.json` while OpenCode is
192
225
  opened at the parent workspace. For operational work the coordinator creates
193
226
  that valid handoff before dispatch and sends its exact absolute path; the
194
- binding child must use the built-in Read tool on it immediately before bind.
195
- New coordinator contracts are emitted under the candidate-relative
196
- `.sortie-dogs/contracts/` directory as `handoff.<id>.json` and
197
- `<id>.operation-manifest.json`. The directory is ignored by this repository's
198
- `.gitignore`; legacy root/scoped paths and configured custom paths remain
199
- readable and preflight-compatible, but are never moved or deleted.
200
- Remove the directory only when no Sortie run is active.
227
+ binding child must use the built-in Read tool on it immediately before bind.
228
+ New coordinator contracts are emitted under the candidate-relative
229
+ `.sortie-dogs/contracts/` directory as `handoff.<id>.json` and
230
+ `<id>.operation-manifest.json`. The directory is ignored by this repository's
231
+ `.gitignore`; legacy root/scoped paths and configured custom paths remain
232
+ readable and preflight-compatible, but are never moved or deleted.
233
+ Remove the directory only when no Sortie run is active.
201
234
  - `readOnlyTools` adds host-specific tool names that never change files, such as
202
235
  MCP tools. Unknown tools are denied for a bound session by default.
203
- - `dedicatedWorkerModel` selects the serial implementation target used by
204
- `implementation`, `remediation`, `blocker-resolution`, `sol-worker-mk2a2`, and
205
- `dog-worker`. It defaults to `openai/gpt-5.6-sol` with variant `medium`.
206
- The installed `dog-luna-worker` fabric route remains fixed to
207
- `openai/gpt-5.6-luna` with variant `max`; pointing the serial target at that
236
+ - `dedicatedWorkerModel` selects the serial implementation target used by
237
+ `implementation`, `remediation`, `blocker-resolution`, `sol-worker-mk2a2`, and
238
+ `dog-worker`. It defaults to `openai/gpt-5.6-sol` with variant `medium`.
239
+ The installed `dog-luna-worker` fabric route remains fixed to
240
+ `openai/gpt-5.6-luna` with variant `max`; pointing the serial target at that
208
241
  Luna model is invalid because it would collapse the two route identities. The
209
242
  coordinator dispatches this role only for a ready descriptor of a prepared
210
- `luna-fabric` run; a `sol-serial` run keeps `dog-worker`.
243
+ `luna-fabric` run; a `sol-serial` run keeps `dog-worker`.
211
244
  - `continuation` bounds the batch loop. After a terminal unit and its checkpoint,
212
245
  `dog-coordinator` calls `sortie_compact_and_continue`, which compacts the run
213
246
  and resumes the same root session on the next independent unit. Only a root
214
247
  `dog-coordinator` session is ever resumed: a child session is never promoted and
215
248
  another coordinator is never adopted. Set `enabled` to `false` to keep every
216
- batch manual, lower `maxAutoContinues` (default and maximum `10`) to change the
217
- ceiling, and set `summarizeModel` to override the latest coordinator
218
- model used for compaction. Normal OpenCode auto-compaction keeps the
219
- host's auto-continue behavior; Sortie suppresses it only while its own
220
- explicitly queued rollover owns the resume.
221
- Every terminal root-coordinator response that does not resume another unit
222
- compacts without auto-continuing, so completed tool output is not carried into
223
- the next user request.
224
- - `reflection` is an opt-in process-prevention companion for an activated root
225
- `dog-coordinator`. It is disabled by default. Run and project layers default
226
- to enabled after opt-in; the cross-project global storage layer remains
227
- disabled unless explicitly enabled. Child and non-coordinator sessions fail
228
- closed, and `SORTIE_REFLECTION=0` is an immediate kill switch. The coordinator
229
- injects the governing `REFLECTION_POLICY` only while reflection is enabled.
230
- `maxInjectedTokens` budgets the dynamic `SORTIE_PROCESS_REFLECTIONS` heading
231
- and persisted entry lines; the policy is outside that entry budget.
232
- The coordinator
233
- evaluates it only after a resolved blocker/review defect and at a terminal
234
- unit, with a maximum of three records per run; routine bugs and external
235
- failures are never journaled.
236
-
237
- ## Example run
249
+ batch manual, lower `maxAutoContinues` (default and maximum `10`) to change the
250
+ ceiling, and set `summarizeModel` to override the latest coordinator
251
+ model used for compaction. Normal OpenCode auto-compaction keeps the
252
+ host's auto-continue behavior; Sortie suppresses it only while its own
253
+ explicitly queued rollover owns the resume.
254
+ Every terminal root-coordinator response that does not resume another unit
255
+ compacts without auto-continuing, so completed tool output is not carried into
256
+ the next user request.
257
+ - `reflection` is an opt-in process-prevention companion for an activated root
258
+ `dog-coordinator`. It is disabled by default. Run and project layers default
259
+ to enabled after opt-in; the cross-project global storage layer remains
260
+ disabled unless explicitly enabled. Child and non-coordinator sessions fail
261
+ closed, and `SORTIE_REFLECTION=0` is an immediate kill switch. The coordinator
262
+ injects the governing `REFLECTION_POLICY` only while reflection is enabled.
263
+ `maxInjectedTokens` budgets the dynamic `SORTIE_PROCESS_REFLECTIONS` heading
264
+ and persisted entry lines; the policy is outside that entry budget.
265
+ The coordinator
266
+ evaluates it only after a resolved blocker/review defect and at a terminal
267
+ unit, with a maximum of three records per run; routine bugs and external
268
+ failures are never journaled.
269
+
270
+ ## Example run
238
271
 
239
272
  An illustrative low-risk run stays bounded and reports its gates:
240
273
 
241
274
  ```text
242
275
  You: /sortie Add the requested behavior
243
276
  dog-coordinator: manifest confirmed
244
- dog-scout: skipped — no concrete evidence gap
245
- dog-worker: implementation complete
277
+ dog-scout: skipped — no concrete evidence gap
278
+ dog-worker: implementation complete
246
279
  validation: npm test — PASS
247
280
  review: skipped — low risk
248
281
  dog-coordinator: completion evidence accepted
@@ -252,8 +285,8 @@ dog-coordinator: completion evidence accepted
252
285
 
253
286
  1. **Brief and plan** — `dog-coordinator` turns the request into acceptance
254
287
  criteria, a write manifest, and validation requirements.
255
- 2. **Optional scout** — one bounded, read-only investigation runs only for a
256
- concrete pre-worker evidence gap.
288
+ 2. **Optional scout** — one bounded, read-only investigation runs only for a
289
+ concrete pre-worker evidence gap.
257
290
  3. **Dedicated worker** — the dedicated worker implements only the approved
258
291
  manifest and also owns scoped remediation or blocker resolution.
259
292
  4. **Canonical validation** — the declared test or build command must produce
@@ -305,16 +338,16 @@ untouched.
305
338
 
306
339
  ## Model routing
307
340
 
308
- Default routes split work by required capability and repeated-context cost.
309
- Sortie-dogs keeps retrieval on Luna, coordinator routing on Terra, and independent
310
- review on Sol unless the host declares another target.
341
+ Default routes split work by required capability and repeated-context cost.
342
+ Sortie-dogs keeps retrieval on Luna, coordinator routing on Terra, and independent
343
+ review on Sol unless the host declares another target.
311
344
 
312
- `dog-coordinator` defaults to `openai/gpt-5.6-terra` with the `high` variant. Coordinator quality controls
313
- planning and forward progress, so Terra High is the default balance between capability and cost.
314
- Project or global `modelRouting` can override
315
- this default. If the host proves Terra unavailable,
316
- the existing availability policy uses a configured free-tier fallback when present
317
- and otherwise preserves the session model.
345
+ `dog-coordinator` defaults to `openai/gpt-5.6-terra` with the `high` variant. Coordinator quality controls
346
+ planning and forward progress, so Terra High is the default balance between capability and cost.
347
+ Project or global `modelRouting` can override
348
+ this default. If the host proves Terra unavailable,
349
+ the existing availability policy uses a configured free-tier fallback when present
350
+ and otherwise preserves the session model.
318
351
 
319
352
  `dog-scout` defaults to `openai/gpt-5.6-luna` with the `high` variant, since
320
353
  gathering bounded evidence is retrieval rather than reasoning and that tier is
@@ -322,46 +355,46 @@ where the curve gives the most per unit of cost. Nobody selects a model for a
322
355
  session the loop spawns, which is why delegated roles carry defaults and the
323
356
  coordinator does not. Project-local routing can override this default.
324
357
 
325
- The `implementation`, `remediation`, `blocker-resolution`, `sol-worker-mk2a2`,
326
- and `dog-worker` roles always use the stable serial target,
327
- `openai/gpt-5.6-sol` with the `medium` variant. `dedicatedWorkerModel` may move
328
- that serial target when a host cannot serve it. The installed `dog-luna-worker`
329
- route is separately fixed to `openai/gpt-5.6-luna` with the `max` variant. Its
358
+ The `implementation`, `remediation`, `blocker-resolution`, `sol-worker-mk2a2`,
359
+ and `dog-worker` roles always use the stable serial target,
360
+ `openai/gpt-5.6-sol` with the `medium` variant. `dedicatedWorkerModel` may move
361
+ that serial target when a host cannot serve it. The installed `dog-luna-worker`
362
+ route is separately fixed to `openai/gpt-5.6-luna` with the `max` variant. Its
330
363
  shared worker contract requires one validated fabric descriptor: the coordinator
331
- admits a v0.8 DAG contract with `sortie_admit_luna_fabric`, prepares it with
332
- `sortie_prepare_luna_fabric`, and materializes only the current ready wave, with
333
- at most five distinct Luna units. The complete DAG may contain up to 64 units.
334
- After every active artifact is verified, `sortie_advance_luna_fabric_wave`
335
- integrates them into a runtime-owned hidden candidate, cleans those worktrees,
336
- and creates fresh worktrees from that exact snapshot. After the final wave,
337
- `sortie_validate_luna_fabric_candidate` runs canonical validation once and
338
- `sortie_accept_luna_fabric_candidate` records review before one target CAS.
339
- Declared shared-path ownership serializes overlapping units
340
- across waves; unowned overlap or any admission defect routes the whole job back
341
- to one `dog-worker`. The fabric never duplicates one unit across lanes.
342
- `modelRouting` cannot replace either fixed
343
- route, and a serial override naming the Luna fabric model is invalid rather than
344
- silently collapsing both identities. Version 0.7.0 routed `dog-worker` to Luna
345
- Max; v0.8 intentionally preserves that history while splitting stable Sol and
346
- fabric Luna roles. Other explicit routes try the preferred target, then ordered
347
- fallbacks. Roles without a built-in default or explicit route keep OpenCode's
348
- already selected model.
364
+ admits a v0.8 DAG contract with `sortie_admit_luna_fabric`, prepares it with
365
+ `sortie_prepare_luna_fabric`, and materializes only the current ready wave, with
366
+ at most five distinct Luna units. The complete DAG may contain up to 64 units.
367
+ After every active artifact is verified, `sortie_advance_luna_fabric_wave`
368
+ integrates them into a runtime-owned hidden candidate, cleans those worktrees,
369
+ and creates fresh worktrees from that exact snapshot. After the final wave,
370
+ `sortie_validate_luna_fabric_candidate` runs canonical validation once and
371
+ `sortie_accept_luna_fabric_candidate` records review before one target CAS.
372
+ Declared shared-path ownership serializes overlapping units
373
+ across waves; unowned overlap or any admission defect routes the whole job back
374
+ to one `dog-worker`. The fabric never duplicates one unit across lanes.
375
+ `modelRouting` cannot replace either fixed
376
+ route, and a serial override naming the Luna fabric model is invalid rather than
377
+ silently collapsing both identities. Version 0.7.0 routed `dog-worker` to Luna
378
+ Max; v0.8 intentionally preserves that history while splitting stable Sol and
379
+ fabric Luna roles. Other explicit routes try the preferred target, then ordered
380
+ fallbacks. Roles without a built-in default or explicit route keep OpenCode's
381
+ already selected model.
349
382
 
350
383
  `dog-reviewer` and `dog-advisor` must never inherit the caller's model, because
351
384
  review and strategy lose their value when they run on the model that produced
352
385
  the candidate. Both default to `anthropic/claude-opus-5` when the catalog
353
386
  declares it, and otherwise fall back to `openai/gpt-5.6-sol` with the `xhigh`
354
- variant. That fallback uses higher effort than the Sol Medium worker because
355
- review has to be able to reject work the worker just produced. Moving
356
- `dedicatedWorkerModel` does not change consultation policy. Nothing here requires
357
- a particular vendor: both roles stay fully configurable, so declare whichever
358
- model you can actually serve.
387
+ variant. That fallback uses higher effort than the Sol Medium worker because
388
+ review has to be able to reject work the worker just produced. Moving
389
+ `dedicatedWorkerModel` does not change consultation policy. Nothing here requires
390
+ a particular vendor: both roles stay fully configurable, so declare whichever
391
+ model you can actually serve.
359
392
 
360
393
  ```json
361
394
  {
362
395
  "modelRouting": {
363
- "dog-coordinator": {
364
- "preferred": { "model": "openai/gpt-5.6-luna", "variant": "max" }
396
+ "dog-coordinator": {
397
+ "preferred": { "model": "openai/gpt-5.6-luna", "variant": "max" }
365
398
  },
366
399
  "dog-scout": {
367
400
  "preferred": { "model": "openai/gpt-5.6-luna", "variant": "high" }
@@ -375,8 +408,8 @@ model you can actually serve.
375
408
  }
376
409
  },
377
410
  "modelCatalog": {
378
- "project": [
379
- { "model": "openai/gpt-5.6-sol", "variants": ["medium", "xhigh"] },
411
+ "project": [
412
+ { "model": "openai/gpt-5.6-sol", "variants": ["medium", "xhigh"] },
380
413
  { "model": "openai/gpt-5.6-luna", "variants": ["max", "high"] },
381
414
  { "model": "anthropic/claude-opus-5" }
382
415
  ]
@@ -397,52 +430,52 @@ coordinator. `dog-reviewer` independently checks high-risk candidates after
397
430
  canonical validation. Neither role implements, stages, commits, or acts as a
398
431
  user-facing worker.
399
432
 
400
- ## Optional global availability
401
-
402
- If you intentionally want the Sortie roles available across projects, install
403
- the CLI and runtime assets globally:
404
-
405
- ```sh
406
- npm install --global sortie-dogs
407
- sortie-dogs init --global
408
- ```
409
-
410
- This writes canonical Sortie runtime assets to OpenCode's global configuration;
411
- it does not make project-local initialization global. Load the plugin from the
412
- global `~/.config/opencode/opencode.json` when using these assets:
413
-
414
- ```json
415
- {
416
- "plugin": ["sortie-dogs"]
417
- }
418
- ```
419
-
420
- Project-local configuration and plugin loading remain available when a project
421
- needs its own settings or dependency.
422
-
423
- ## Updates and migration
433
+ ## Optional global availability
434
+
435
+ If you intentionally want the Sortie roles available across projects, install
436
+ the CLI and runtime assets globally:
437
+
438
+ ```sh
439
+ npm install --global sortie-dogs
440
+ sortie-dogs init --global
441
+ ```
442
+
443
+ This writes canonical Sortie runtime assets to OpenCode's global configuration;
444
+ it does not make project-local initialization global. Load the plugin from the
445
+ global `~/.config/opencode/opencode.json` when using these assets:
446
+
447
+ ```json
448
+ {
449
+ "plugin": ["sortie-dogs"]
450
+ }
451
+ ```
452
+
453
+ Project-local configuration and plugin loading remain available when a project
454
+ needs its own settings or dependency.
455
+
456
+ ## Updates and migration
424
457
 
425
458
  After replacing the dependency with a newer release asset, run:
426
459
 
427
460
  ```sh
428
- npx sortie-dogs init .
429
- ```
430
-
431
- After initialization, fully restart the OpenCode host before resuming a coordinator or opening a
432
- fresh session. Plugin modules are process-scoped, so a new session alone cannot load the update.
433
-
434
- `init` is idempotent. It updates files owned by Sortie-dogs, migrates recognized
461
+ npx sortie-dogs init .
462
+ ```
463
+
464
+ After initialization, fully restart the OpenCode host before resuming a coordinator or opening a
465
+ fresh session. Plugin modules are process-scoped, so a new session alone cannot load the update.
466
+
467
+ `init` is idempotent. It updates files owned by Sortie-dogs, migrates recognized
435
468
  older runtime files, and records the installed version in
436
469
  `.opencode/sortie-dogs.version`. Conflicting or unrecognized files remain
437
470
  untouched and initialization stops safely. User-owned configuration—including
438
471
  `.opencode/sortie-dogs.json`—and standard OpenCode files are preserved.
439
472
 
440
- ## Maintainer releases
441
-
442
- The [release batch guide](docs/release-batch.md) covers fixed-tarball CLI verification,
443
- global application, resumable GitHub publication, and manual npm publication checks.
444
-
445
- ## Safe manual removal
473
+ ## Maintainer releases
474
+
475
+ The [release batch guide](docs/release-batch.md) covers fixed-tarball CLI verification,
476
+ global application, resumable GitHub publication, and manual npm publication checks.
477
+
478
+ ## Safe manual removal
446
479
 
447
480
  There is no supported Sortie-dogs uninstall command. Remove the npm dependency
448
481
  separately, then follow the [safe manual removal guide](docs/uninstall.md) to
@@ -2,5 +2,5 @@
2
2
  * Version of the installable runtime assets. Kept in its own module so the plugin can compare an
3
3
  * installed project marker without importing every asset body.
4
4
  */
5
- export declare const RUNTIME_ASSET_VERSION = "0.3.86-codegen-proof-v1";
5
+ export declare const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
6
6
  export type RuntimeAssetVersion = typeof RUNTIME_ASSET_VERSION;
@@ -2,4 +2,4 @@
2
2
  * Version of the installable runtime assets. Kept in its own module so the plugin can compare an
3
3
  * installed project marker without importing every asset body.
4
4
  */
5
- export const RUNTIME_ASSET_VERSION = "0.3.86-codegen-proof-v1";
5
+ export const RUNTIME_ASSET_VERSION = "0.3.89-completion-proof-v1";
@@ -119,7 +119,7 @@ export function reduceGoalFlight(records) {
119
119
  continue;
120
120
  requireState(instant(event.at), "invalid", "Goal event timestamp is invalid.");
121
121
  if (event.kind === "goal.accepted") {
122
- requireState(state.goal_id === null || state.phase === "terminal", "transition", "An active goal already owns this root.");
122
+ requireState(state.goal_id === null || state.phase === "terminal" || state.phase === "stopped", "transition", "An active goal already owns this root.");
123
123
  requireState(text(event.goal_id) && HASH.test(event.acceptance_fingerprint) && text(event.origin_user_message_id) &&
124
124
  text(event.origin_session_id) && text(event.selected_agent) && event.budget.max_units > 0 &&
125
125
  validAcceptanceContract(event.acceptance_contract), "invalid", "Accepted goal identity is incomplete.");
@@ -2,38 +2,38 @@ import { goalFingerprint } from "./goal-bound.js";
2
2
  /** Shared planner guidance and admission enums; no inferred acceptance or dispatch authority. */
3
3
  export const GOAL_DELIVERY_INTENTS = ["design", "registration", "implementation", "repair", "controlled-change"];
4
4
  export const GOAL_DELIVERY_MODES = ["planning-only", "mvp-first", "repair-first", "controlled-change"];
5
- export const GOAL_DECLARATION_FORMAT = `Declare a goal once, then reference it with goal_declaration_path in the Task prompt.
6
- The referenced JSON may contain shared defaults and a criteria array; the host expands them privately.
7
- An inline ext["sortie-dogs/goal-declaration"] in the registered handoff is also supported.
8
- Existing accepted goals need no repeated declaration; budget-only revisions retain their criteria.
9
- Legacy flat goal_* fields remain supported. No need to rewrite unrelated files or repeat common fields for each criterion.
10
- delivery_intent must be exactly one of: ${GOAL_DELIVERY_INTENTS.join(" | ")}. Never use prose or a boolean.
11
- delivery_mode is a separate optional enum: ${GOAL_DELIVERY_MODES.join(" | ")}.
12
- Use flat key: value lines. Each criterion begins with its own goal_criterion_id: line.
13
- For legacy flat input each criterion has a goal_criterion_id line. Prefer shared JSON defaults for multiple criteria.
14
- Replace every placeholder with the accepted task's actual value; preserve its acceptance and validation:
15
- goal_acceptance_fingerprint: <sha256: followed by exactly 64 lowercase hexadecimal characters>
16
- delivery_intent: implementation
17
- delivery_mode: mvp-first
18
- usable_path_established: false
19
- controlled_change: false
20
- goal_budget_units: <accepted positive integer>
21
- goal_criterion_id: <stable criterion id>
22
- goal_target: <requested behavior>
23
- goal_entrypoint: <real entrypoint>
24
- goal_workload: <requested workload>
25
- goal_oracle_coverage: ["<actual oracle>"]
26
- goal_build_boundary: <included | excluded | not-applicable>
27
- goal_source: <fixed source label>
28
- goal_candidate: <fixed candidate label>
29
- goal_source_binding: current-protected
30
- goal_candidate_binding: current-protected
31
- goal_fixture: <actual fixture identity>
32
- goal_proof_scope: requested-full
33
- goal_expected_outcome: pass
34
- goal_validation_command: <exact operation manifest validation command>
35
- The example's implementation/mvp-first selections apply only to an implementation without its usable path;
36
- select other enum values only from the accepted request. A declaration rejection launches no child.
5
+ export const GOAL_DECLARATION_FORMAT = `Declare a goal once, then reference it with goal_declaration_path in the Task prompt.
6
+ The referenced JSON may contain shared defaults and a criteria array; the host expands them privately.
7
+ An inline ext["sortie-dogs/goal-declaration"] in the registered handoff is also supported.
8
+ Existing accepted goals need no repeated declaration; budget-only revisions retain their criteria.
9
+ Legacy flat goal_* fields remain supported. No need to rewrite unrelated files or repeat common fields for each criterion.
10
+ delivery_intent must be exactly one of: ${GOAL_DELIVERY_INTENTS.join(" | ")}. Never use prose or a boolean.
11
+ delivery_mode is a separate optional enum: ${GOAL_DELIVERY_MODES.join(" | ")}.
12
+ Use flat key: value lines. Each criterion begins with its own goal_criterion_id: line.
13
+ For legacy flat input each criterion has a goal_criterion_id line. Prefer shared JSON defaults for multiple criteria.
14
+ Replace every placeholder with the accepted task's actual value; preserve its acceptance and validation:
15
+ goal_acceptance_fingerprint: <sha256: followed by exactly 64 lowercase hexadecimal characters>
16
+ delivery_intent: implementation
17
+ delivery_mode: mvp-first
18
+ usable_path_established: false
19
+ controlled_change: false
20
+ goal_budget_units: <accepted positive integer>
21
+ goal_criterion_id: <stable criterion id>
22
+ goal_target: <requested behavior>
23
+ goal_entrypoint: <real entrypoint>
24
+ goal_workload: <requested workload>
25
+ goal_oracle_coverage: ["<actual oracle>"]
26
+ goal_build_boundary: <included | excluded | not-applicable>
27
+ goal_source: <fixed source label>
28
+ goal_candidate: <fixed candidate label>
29
+ goal_source_binding: current-protected
30
+ goal_candidate_binding: current-protected
31
+ goal_fixture: <actual fixture identity>
32
+ goal_proof_scope: requested-full
33
+ goal_expected_outcome: pass
34
+ goal_validation_command: <exact operation manifest validation command>
35
+ The example's implementation/mvp-first selections apply only to an implementation without its usable path;
36
+ select other enum values only from the accepted request. A declaration rejection launches no child.
37
37
  Repair the Task prompt's named fields, not unrelated files; retry only with the corrected declaration.`;
38
38
  /** Normalize a shared declaration without guessing acceptance, coverage, delivery, or validation. */
39
39
  export function expandGoalDeclaration(value) {