sortie-dogs 0.2.6 → 0.2.8

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
@@ -60,8 +60,9 @@ to the `plugin` array of the OpenCode configuration the agents run under —
60
60
  Restart OpenCode afterwards. A `plugin` entry must name the package, not a
61
61
  subpath: `sortie-dogs/plugin` is an import specifier, not a plugin specifier.
62
62
 
63
- `dog-coordinator` and `dog-scout` default to `openai/gpt-5.6-luna`. To use a
64
- different model for both roles, save this as `.opencode/sortie-dogs.json`:
63
+ `dog-coordinator` keeps whichever model you select for the session. `dog-scout`
64
+ defaults to `openai/gpt-5.6-luna`. To pin either role to another model, save this
65
+ as `.opencode/sortie-dogs.json`:
65
66
 
66
67
  ```json
67
68
  {
@@ -160,7 +161,7 @@ Optional settings in `.opencode/sortie-dogs.json`:
160
161
  - `readOnlyTools` adds host-specific tool names that never change files, such as
161
162
  MCP tools. Unknown tools are denied for a bound session by default.
162
163
  - `dedicatedWorkerModel` selects the single model every worker role resolves to.
163
- It defaults to `openai/gpt-5.6-sol` with variant `medium`; declare your own when
164
+ It defaults to `openai/gpt-5.6-luna` with variant `max`; declare your own when
164
165
  that model is unavailable or when you want a different worker effort. Worker
165
166
  roles always resolve to this one target and cannot be routed per role.
166
167
  - `continuation` bounds the batch loop. After a terminal unit and its checkpoint,
@@ -180,7 +181,7 @@ Optional settings in `.opencode/sortie-dogs.json`:
180
181
  exactly three bounded scouts before implementation begins.
181
182
  - **Writes stay inside the assignment.** Exact source or operation manifests
182
183
  gate edits and handoffs.
183
- - **One accountable implementation path.** A dedicated Sol worker handles
184
+ - **One accountable implementation path.** One dedicated worker handles
184
185
  implementation, remediation, and blocker resolution.
185
186
  - **Evidence before completion.** Canonical validation, risk-based review, and
186
187
  terminal evidence gate coordinator-owned completion and commits.
@@ -207,8 +208,8 @@ dog-coordinator: completion evidence accepted
207
208
  criteria, a write manifest, and validation requirements.
208
209
  2. **Exactly three scouts** — bounded, read-only investigation collects
209
210
  complementary evidence without expanding the write scope.
210
- 3. **Dedicated worker** — the Sol worker implements only the approved manifest
211
- and also owns scoped remediation or blocker resolution.
211
+ 3. **Dedicated worker** — the dedicated worker implements only the approved
212
+ manifest and also owns scoped remediation or blocker resolution.
212
213
  4. **Canonical validation** — the declared test or build command must produce
213
214
  acceptable evidence.
214
215
  5. **Risk-based review** — high-risk candidates receive independent review;
@@ -258,19 +259,30 @@ untouched.
258
259
 
259
260
  ## Model routing
260
261
 
261
- `dog-coordinator` and `dog-scout` default to `openai/gpt-5.6-luna` with the
262
- `xhigh` variant. This is the recommended balance: bounded prompts, concise
263
- scout evidence, and fewer unnecessary context or tool turns can reduce token
264
- use while preserving quality. Project-local routing can override either
265
- default.
262
+ Every default route is one model at a different reasoning effort, because
263
+ published cost curves put a cheap model at high effort above an expensive model
264
+ at mid effort on both solve rate and price. Sortie-dogs therefore buys effort,
265
+ not model tiers, wherever the work allows it.
266
+
267
+ `dog-coordinator` has no built-in route. It is the one agent you drive directly
268
+ and pick a model for in the session, so it keeps the model you selected; a
269
+ shipped default there would silently revert your choice instead of filling an
270
+ absent one. Declare `modelRouting` for `dog-coordinator` if you do want a fixed
271
+ coordinator model.
272
+
273
+ `dog-scout` defaults to `openai/gpt-5.6-luna` with the `high` variant, since
274
+ gathering bounded evidence is retrieval rather than reasoning and that tier is
275
+ where the curve gives the most per unit of cost. Nobody selects a model for a
276
+ session the loop spawns, which is why delegated roles carry defaults and the
277
+ coordinator does not. Project-local routing can override this default.
266
278
 
267
279
  The `implementation`, `remediation`, `blocker-resolution`, and `dog-worker`
268
- roles always use the dedicated worker target, `openai/gpt-5.6-sol` with the
269
- `medium` variant. Worker effort is deliberately below review effort: source
270
- review is mandatory for risky candidates and returns findings the worker must
271
- remediate, so the loop already re-runs weak implementation work and top-of-range
272
- first-attempt effort mostly buys accuracy the reviewer supplies anyway. Raise
273
- `dedicatedWorkerModel` when you would rather pay for it up front. `modelRouting`
280
+ roles always use the dedicated worker target, `openai/gpt-5.6-luna` with the
281
+ `max` variant. Worker effort stays at the top of that model's range while review
282
+ effort stays above it on a stronger model, which is what mandatory source review
283
+ is for. Declare `dedicatedWorkerModel` as `openai/gpt-5.6-sol` when you would
284
+ rather pay for the stronger worker model up front; that target stays in the
285
+ built-in catalog for exactly this reason. `modelRouting`
274
286
  cannot replace those routes, and only `dedicatedWorkerModel` moves them. For other explicitly
275
287
  routed roles, resolution is deterministic: Sortie-dogs tries the preferred
276
288
  target, then ordered fallbacks. Roles without either a built-in default or an
@@ -280,7 +292,9 @@ explicit route keep OpenCode's already selected model.
280
292
  review and strategy lose their value when they run on the model that produced
281
293
  the candidate. Both default to `anthropic/claude-opus-5` when the catalog
282
294
  declares it, and otherwise fall back to `openai/gpt-5.6-sol` with the `xhigh`
283
- variant, one effort step above the worker target. A host that redeclares
295
+ variant. That fallback stays on the stronger model rather than matching the
296
+ worker target, because review has to be able to reject work the worker just
297
+ produced. A host that redeclares
284
298
  `dedicatedWorkerModel` keeps that target as its first fallback, since such a
285
299
  host may not serve the shipped model at all. Nothing here requires a particular
286
300
  vendor: both roles stay fully configurable, so declare whichever model you can
@@ -290,10 +304,10 @@ actually serve.
290
304
  {
291
305
  "modelRouting": {
292
306
  "dog-coordinator": {
293
- "preferred": { "model": "openai/gpt-5.6-luna", "variant": "xhigh" }
307
+ "preferred": { "model": "openai/gpt-5.6-luna", "variant": "max" }
294
308
  },
295
309
  "dog-scout": {
296
- "preferred": { "model": "openai/gpt-5.6-luna", "variant": "xhigh" }
310
+ "preferred": { "model": "openai/gpt-5.6-luna", "variant": "high" }
297
311
  },
298
312
  "dog-reviewer": {
299
313
  "preferred": { "model": "anthropic/claude-opus-5" },
@@ -306,7 +320,7 @@ actually serve.
306
320
  "modelCatalog": {
307
321
  "project": [
308
322
  { "model": "openai/gpt-5.6-sol", "variants": ["medium", "xhigh"] },
309
- { "model": "openai/gpt-5.6-luna", "variants": ["xhigh"] },
323
+ { "model": "openai/gpt-5.6-luna", "variants": ["max", "high"] },
310
324
  { "model": "anthropic/claude-opus-5" }
311
325
  ]
312
326
  }
@@ -7,28 +7,52 @@ export interface RoleModelRoute {
7
7
  readonly fallback?: readonly ModelTarget[];
8
8
  }
9
9
  export type ModelRoutingConfig = Readonly<Record<string, RoleModelRoute>>;
10
- export declare const DEDICATED_SOL_MODEL = "openai/gpt-5.6-sol";
11
10
  /**
12
- * Worker reasoning effort is deliberately below the review effort. Source review is mandatory and
13
- * returns findings the worker must remediate, so the loop already re-runs weak implementation work;
14
- * paying top-of-range effort on the first attempt buys accuracy the reviewer would otherwise supply.
11
+ * The published DeepSWE cost curve settles the worker target: the cheap model at top reasoning effort
12
+ * solves more than the expensive model at mid effort while costing a fraction of it, so paying for the
13
+ * expensive model by default bought a lower solve rate. Worker effort therefore sits at the top of the
14
+ * cheap model's range, and review effort stays above it on a stronger model, which is what the
15
+ * mandatory source review is for.
15
16
  */
16
- export declare const DEDICATED_SOL_VARIANT = "medium";
17
- export declare const DEDICATED_SOL_ROLES: readonly ["implementation", "remediation", "blocker-resolution", "sol-worker-mk2a2", "dog-worker"];
17
+ export declare const DEDICATED_WORKER_MODEL = "openai/gpt-5.6-luna";
18
+ export declare const DEDICATED_WORKER_VARIANT = "max";
19
+ /**
20
+ * The stronger, far more expensive worker target a host may still select deliberately. It is no longer
21
+ * a default route: it stays declared so an explicit dedicatedWorkerModel resolves against the catalog
22
+ * without extra host configuration.
23
+ */
24
+ export declare const ESCALATION_WORKER_MODEL = "openai/gpt-5.6-sol";
25
+ export declare const ESCALATION_WORKER_VARIANT = "medium";
26
+ export declare const DEDICATED_WORKER_ROLES: readonly ["implementation", "remediation", "blocker-resolution", "sol-worker-mk2a2", "dog-worker"];
18
27
  /** Ordered last-resort targets used only when the host proves a policy target unavailable. */
19
28
  export declare const DEFAULT_FREE_TIER_FALLBACK_MODELS: readonly string[];
20
29
  /** The dedicated worker target this build ships with when a host declares no target of its own. */
21
30
  export declare const DEFAULT_DEDICATED_WORKER_TARGET: ModelTarget;
31
+ /** The declared escalation target for a host that chooses to pay for the stronger worker model. */
32
+ export declare const ESCALATION_WORKER_TARGET: ModelTarget;
22
33
  /**
23
34
  * Fixed worker routes for one dedicated target. Which target is dedicated is a host decision, but
24
35
  * every worker role resolves to that single target and never to a fallback.
25
36
  */
26
37
  export declare function dedicatedWorkerRouting(target?: ModelTarget): ModelRoutingConfig;
27
- export declare const DEDICATED_SOL_ROUTING: ModelRoutingConfig;
38
+ export declare const DEDICATED_WORKER_ROUTING: ModelRoutingConfig;
28
39
  export declare const FIXED_MODEL_ROUTING: ModelRoutingConfig;
29
40
  export declare const RECOMMENDED_LUNA_MODEL = "openai/gpt-5.6-luna";
30
- export declare const RECOMMENDED_LUNA_VARIANT = "xhigh";
31
- export declare const RECOMMENDED_LUNA_ROLES: readonly ["dog-coordinator", "dog-scout"];
41
+ /**
42
+ * The coordinator has no built-in route on purpose. It is the one agent the user drives directly and
43
+ * picks a model for in the session, so a shipped default here does not choose between models for an
44
+ * undecided user: it silently discards a choice the user already made and cannot see being reverted.
45
+ * Delegated roles are the opposite, because nobody selects a model for a session the loop spawns.
46
+ * A host that does want a fixed coordinator model still declares one through modelRouting.
47
+ *
48
+ * Evidence gathering is retrieval rather than reasoning, so the scout sits one effort tier below the
49
+ * worker, where the published cost curve returns the most per unit of cost.
50
+ */
51
+ export declare const RECOMMENDED_SCOUT_VARIANT = "high";
52
+ export declare const RECOMMENDED_LUNA_ROLE_VARIANTS: Readonly<{
53
+ readonly "dog-scout": "high";
54
+ }>;
55
+ export declare const RECOMMENDED_LUNA_ROLES: readonly string[];
32
56
  /** Configurable MkII defaults. Project-local and global configuration may override these routes. */
33
57
  export declare const RECOMMENDED_LUNA_ROUTING: ModelRoutingConfig;
34
58
  /**
@@ -39,8 +63,12 @@ export declare const RECOMMENDED_LUNA_ROUTING: ModelRoutingConfig;
39
63
  export declare const RECOMMENDED_CONSULTATION_MODEL = "anthropic/claude-opus-5";
40
64
  export declare const RECOMMENDED_CONSULTATION_ROLES: readonly string[];
41
65
  /** Review and advice carry the reasoning effort the worker target intentionally does not spend. */
42
- export declare const CONSULTATION_SOL_VARIANT = "xhigh";
43
- /** The shipped consultation fallback; higher effort than the worker target on the same model. */
66
+ export declare const CONSULTATION_FALLBACK_VARIANT = "xhigh";
67
+ /**
68
+ * The shipped consultation fallback. Review has to be able to reject work the worker just produced, so
69
+ * it stays on the stronger model even though the worker no longer defaults to it; a fallback equal to
70
+ * the worker target would review that work with exactly the capability that produced it.
71
+ */
44
72
  export declare const DEFAULT_CONSULTATION_FALLBACK_TARGET: ModelTarget;
45
73
  /**
46
74
  * Consultation prefers the strongest declared reasoning model. A host that relocated the dedicated
@@ -55,7 +83,7 @@ export declare const RECOMMENDED_CONSULTATION_ROUTING: ModelRoutingConfig;
55
83
  /** Every configurable role route this build recommends before host configuration is applied. */
56
84
  export declare function recommendedRoleRouting(fallbackTarget?: ModelTarget): ModelRoutingConfig;
57
85
  export declare const RECOMMENDED_ROLE_ROUTING: ModelRoutingConfig;
58
- export declare function isDedicatedSolRole(role: string): boolean;
86
+ export declare function isDedicatedWorkerRole(role: string): boolean;
59
87
  export declare function isFixedModelRole(role: string): boolean;
60
88
  export interface CatalogModel {
61
89
  readonly model: string;
@@ -1,12 +1,21 @@
1
1
  import { CONSULTATION_ROLE_POLICY } from "../core/consultation.js";
2
- export const DEDICATED_SOL_MODEL = "openai/gpt-5.6-sol";
3
2
  /**
4
- * Worker reasoning effort is deliberately below the review effort. Source review is mandatory and
5
- * returns findings the worker must remediate, so the loop already re-runs weak implementation work;
6
- * paying top-of-range effort on the first attempt buys accuracy the reviewer would otherwise supply.
3
+ * The published DeepSWE cost curve settles the worker target: the cheap model at top reasoning effort
4
+ * solves more than the expensive model at mid effort while costing a fraction of it, so paying for the
5
+ * expensive model by default bought a lower solve rate. Worker effort therefore sits at the top of the
6
+ * cheap model's range, and review effort stays above it on a stronger model, which is what the
7
+ * mandatory source review is for.
7
8
  */
8
- export const DEDICATED_SOL_VARIANT = "medium";
9
- export const DEDICATED_SOL_ROLES = [
9
+ export const DEDICATED_WORKER_MODEL = "openai/gpt-5.6-luna";
10
+ export const DEDICATED_WORKER_VARIANT = "max";
11
+ /**
12
+ * The stronger, far more expensive worker target a host may still select deliberately. It is no longer
13
+ * a default route: it stays declared so an explicit dedicatedWorkerModel resolves against the catalog
14
+ * without extra host configuration.
15
+ */
16
+ export const ESCALATION_WORKER_MODEL = "openai/gpt-5.6-sol";
17
+ export const ESCALATION_WORKER_VARIANT = "medium";
18
+ export const DEDICATED_WORKER_ROLES = [
10
19
  "implementation",
11
20
  "remediation",
12
21
  "blocker-resolution",
@@ -17,34 +26,52 @@ export const DEDICATED_SOL_ROLES = [
17
26
  export const DEFAULT_FREE_TIER_FALLBACK_MODELS = Object.freeze([
18
27
  "opencode/deepseek-v4-flash-free",
19
28
  ]);
20
- const dedicatedSolRoleSet = new Set(DEDICATED_SOL_ROLES);
29
+ const dedicatedWorkerRoleSet = new Set(DEDICATED_WORKER_ROLES);
21
30
  /** The dedicated worker target this build ships with when a host declares no target of its own. */
22
31
  export const DEFAULT_DEDICATED_WORKER_TARGET = Object.freeze({
23
- model: DEDICATED_SOL_MODEL,
24
- variant: DEDICATED_SOL_VARIANT,
32
+ model: DEDICATED_WORKER_MODEL,
33
+ variant: DEDICATED_WORKER_VARIANT,
34
+ });
35
+ /** The declared escalation target for a host that chooses to pay for the stronger worker model. */
36
+ export const ESCALATION_WORKER_TARGET = Object.freeze({
37
+ model: ESCALATION_WORKER_MODEL,
38
+ variant: ESCALATION_WORKER_VARIANT,
25
39
  });
26
40
  /**
27
41
  * Fixed worker routes for one dedicated target. Which target is dedicated is a host decision, but
28
42
  * every worker role resolves to that single target and never to a fallback.
29
43
  */
30
44
  export function dedicatedWorkerRouting(target = DEFAULT_DEDICATED_WORKER_TARGET) {
31
- return Object.freeze(Object.fromEntries(DEDICATED_SOL_ROLES.map((role) => [role, Object.freeze({
45
+ return Object.freeze(Object.fromEntries(DEDICATED_WORKER_ROLES.map((role) => [role, Object.freeze({
32
46
  preferred: Object.freeze(target.variant === undefined
33
47
  ? { model: target.model }
34
48
  : { model: target.model, variant: target.variant }),
35
49
  })])));
36
50
  }
37
- export const DEDICATED_SOL_ROUTING = dedicatedWorkerRouting();
38
- export const FIXED_MODEL_ROUTING = DEDICATED_SOL_ROUTING;
51
+ export const DEDICATED_WORKER_ROUTING = dedicatedWorkerRouting();
52
+ export const FIXED_MODEL_ROUTING = DEDICATED_WORKER_ROUTING;
39
53
  const fixedModelRoleSet = new Set(Object.keys(FIXED_MODEL_ROUTING));
40
54
  export const RECOMMENDED_LUNA_MODEL = "openai/gpt-5.6-luna";
41
- export const RECOMMENDED_LUNA_VARIANT = "xhigh";
42
- export const RECOMMENDED_LUNA_ROLES = ["dog-coordinator", "dog-scout"];
55
+ /**
56
+ * The coordinator has no built-in route on purpose. It is the one agent the user drives directly and
57
+ * picks a model for in the session, so a shipped default here does not choose between models for an
58
+ * undecided user: it silently discards a choice the user already made and cannot see being reverted.
59
+ * Delegated roles are the opposite, because nobody selects a model for a session the loop spawns.
60
+ * A host that does want a fixed coordinator model still declares one through modelRouting.
61
+ *
62
+ * Evidence gathering is retrieval rather than reasoning, so the scout sits one effort tier below the
63
+ * worker, where the published cost curve returns the most per unit of cost.
64
+ */
65
+ export const RECOMMENDED_SCOUT_VARIANT = "high";
66
+ export const RECOMMENDED_LUNA_ROLE_VARIANTS = Object.freeze({
67
+ "dog-scout": RECOMMENDED_SCOUT_VARIANT,
68
+ });
69
+ export const RECOMMENDED_LUNA_ROLES = Object.freeze(Object.keys(RECOMMENDED_LUNA_ROLE_VARIANTS));
43
70
  /** Configurable MkII defaults. Project-local and global configuration may override these routes. */
44
- export const RECOMMENDED_LUNA_ROUTING = Object.freeze(Object.fromEntries(RECOMMENDED_LUNA_ROLES.map((role) => [role, Object.freeze({
71
+ export const RECOMMENDED_LUNA_ROUTING = Object.freeze(Object.fromEntries(Object.entries(RECOMMENDED_LUNA_ROLE_VARIANTS).map(([role, variant]) => [role, Object.freeze({
45
72
  preferred: Object.freeze({
46
73
  model: RECOMMENDED_LUNA_MODEL,
47
- variant: RECOMMENDED_LUNA_VARIANT,
74
+ variant,
48
75
  }),
49
76
  })])));
50
77
  /**
@@ -55,11 +82,15 @@ export const RECOMMENDED_LUNA_ROUTING = Object.freeze(Object.fromEntries(RECOMME
55
82
  export const RECOMMENDED_CONSULTATION_MODEL = "anthropic/claude-opus-5";
56
83
  export const RECOMMENDED_CONSULTATION_ROLES = Object.freeze(Object.values(CONSULTATION_ROLE_POLICY));
57
84
  /** Review and advice carry the reasoning effort the worker target intentionally does not spend. */
58
- export const CONSULTATION_SOL_VARIANT = "xhigh";
59
- /** The shipped consultation fallback; higher effort than the worker target on the same model. */
85
+ export const CONSULTATION_FALLBACK_VARIANT = "xhigh";
86
+ /**
87
+ * The shipped consultation fallback. Review has to be able to reject work the worker just produced, so
88
+ * it stays on the stronger model even though the worker no longer defaults to it; a fallback equal to
89
+ * the worker target would review that work with exactly the capability that produced it.
90
+ */
60
91
  export const DEFAULT_CONSULTATION_FALLBACK_TARGET = Object.freeze({
61
- model: DEDICATED_SOL_MODEL,
62
- variant: CONSULTATION_SOL_VARIANT,
92
+ model: ESCALATION_WORKER_MODEL,
93
+ variant: CONSULTATION_FALLBACK_VARIANT,
63
94
  });
64
95
  function frozenTarget(target) {
65
96
  return Object.freeze(target.variant === undefined
@@ -97,8 +128,8 @@ export function recommendedRoleRouting(fallbackTarget = DEFAULT_DEDICATED_WORKER
97
128
  });
98
129
  }
99
130
  export const RECOMMENDED_ROLE_ROUTING = recommendedRoleRouting();
100
- export function isDedicatedSolRole(role) {
101
- return dedicatedSolRoleSet.has(role);
131
+ export function isDedicatedWorkerRole(role) {
132
+ return dedicatedWorkerRoleSet.has(role);
102
133
  }
103
134
  export function isFixedModelRole(role) {
104
135
  return fixedModelRoleSet.has(role);
@@ -107,12 +138,13 @@ export function isFixedModelRole(role) {
107
138
  export const BUILT_IN_MODEL_CATALOG = Object.freeze({
108
139
  global: Object.freeze([
109
140
  Object.freeze({
110
- model: DEDICATED_SOL_MODEL,
111
- variants: Object.freeze([DEDICATED_SOL_VARIANT, CONSULTATION_SOL_VARIANT]),
141
+ model: DEDICATED_WORKER_MODEL,
142
+ variants: Object.freeze([DEDICATED_WORKER_VARIANT, RECOMMENDED_SCOUT_VARIANT]
143
+ .filter((variant, index, all) => all.indexOf(variant) === index)),
112
144
  }),
113
145
  Object.freeze({
114
- model: RECOMMENDED_LUNA_MODEL,
115
- variants: Object.freeze([RECOMMENDED_LUNA_VARIANT]),
146
+ model: ESCALATION_WORKER_MODEL,
147
+ variants: Object.freeze([ESCALATION_WORKER_VARIANT, CONSULTATION_FALLBACK_VARIANT]),
116
148
  }),
117
149
  ]),
118
150
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sortie-dogs",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "Bounded, validated orchestration loop plugin for OpenCode",
5
5
  "keywords": [
6
6
  "opencode",