@dzhechkov/harness-core 0.8.5 → 0.8.10
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/.dz-manifest.json +286 -82
- package/README.md +54 -2
- package/dist/amendment-trace.d.ts.map +1 -1
- package/dist/amendment-trace.js +12 -1
- package/dist/amendment-trace.js.map +1 -1
- package/dist/compounding.d.ts +54 -0
- package/dist/compounding.d.ts.map +1 -1
- package/dist/compounding.js +221 -1
- package/dist/compounding.js.map +1 -1
- package/dist/eta.d.ts +92 -0
- package/dist/eta.d.ts.map +1 -0
- package/dist/eta.js +488 -0
- package/dist/eta.js.map +1 -0
- package/dist/feature-adr-checkpoints.d.ts +11 -2
- package/dist/feature-adr-checkpoints.d.ts.map +1 -1
- package/dist/feature-adr-checkpoints.js +38 -3
- package/dist/feature-adr-checkpoints.js.map +1 -1
- package/dist/feature-adr-decision-recall.d.ts +167 -0
- package/dist/feature-adr-decision-recall.d.ts.map +1 -0
- package/dist/feature-adr-decision-recall.js +519 -0
- package/dist/feature-adr-decision-recall.js.map +1 -0
- package/dist/feature-adr-landing.d.ts +37 -0
- package/dist/feature-adr-landing.d.ts.map +1 -0
- package/dist/feature-adr-landing.js +59 -0
- package/dist/feature-adr-landing.js.map +1 -0
- package/dist/feature-adr-routing.d.ts +58 -23
- package/dist/feature-adr-routing.d.ts.map +1 -1
- package/dist/feature-adr-routing.js +194 -69
- package/dist/feature-adr-routing.js.map +1 -1
- package/dist/guard-promotion.d.ts +41 -0
- package/dist/guard-promotion.d.ts.map +1 -1
- package/dist/guard-promotion.js +218 -4
- package/dist/guard-promotion.js.map +1 -1
- package/dist/guard-volume.d.ts +108 -0
- package/dist/guard-volume.d.ts.map +1 -0
- package/dist/guard-volume.js +536 -0
- package/dist/guard-volume.js.map +1 -0
- package/dist/guard.d.ts +34 -0
- package/dist/guard.d.ts.map +1 -1
- package/dist/guard.js +113 -4
- package/dist/guard.js.map +1 -1
- package/dist/index.d.ts +18 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/integration-apply.d.ts +25 -0
- package/dist/integration-apply.d.ts.map +1 -0
- package/dist/integration-apply.js +299 -0
- package/dist/integration-apply.js.map +1 -0
- package/dist/integration-evidence.d.ts +46 -0
- package/dist/integration-evidence.d.ts.map +1 -0
- package/dist/integration-evidence.js +44 -0
- package/dist/integration-evidence.js.map +1 -0
- package/dist/integration-probe-worker.d.ts +22 -0
- package/dist/integration-probe-worker.d.ts.map +1 -0
- package/dist/integration-probe-worker.js +334 -0
- package/dist/integration-probe-worker.js.map +1 -0
- package/dist/integrations-verify.d.ts +60 -0
- package/dist/integrations-verify.d.ts.map +1 -0
- package/dist/integrations-verify.js +194 -0
- package/dist/integrations-verify.js.map +1 -0
- package/dist/lesson-generalization.d.ts +29 -0
- package/dist/lesson-generalization.d.ts.map +1 -0
- package/dist/lesson-generalization.js +84 -0
- package/dist/lesson-generalization.js.map +1 -0
- package/dist/loop-blobs.generated.js +8 -8
- package/dist/loop-blobs.generated.js.map +1 -1
- package/dist/loop-plan.d.ts +13 -1
- package/dist/loop-plan.d.ts.map +1 -1
- package/dist/loop-plan.js +15 -1
- package/dist/loop-plan.js.map +1 -1
- package/dist/loop-render.d.ts.map +1 -1
- package/dist/loop-render.js +51 -6
- package/dist/loop-render.js.map +1 -1
- package/dist/loop-trace.d.ts +20 -1
- package/dist/loop-trace.d.ts.map +1 -1
- package/dist/loop-trace.js +83 -1
- package/dist/loop-trace.js.map +1 -1
- package/dist/model-recommender.d.ts +8 -0
- package/dist/model-recommender.d.ts.map +1 -1
- package/dist/model-recommender.js +31 -4
- package/dist/model-recommender.js.map +1 -1
- package/dist/operations.d.ts +27 -0
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +178 -2
- package/dist/operations.js.map +1 -1
- package/dist/patterns.d.ts +27 -1
- package/dist/patterns.d.ts.map +1 -1
- package/dist/patterns.js +211 -45
- package/dist/patterns.js.map +1 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +20 -17
- package/dist/setup.js.map +1 -1
- package/dist/target-integrations.d.ts +65 -0
- package/dist/target-integrations.d.ts.map +1 -0
- package/dist/target-integrations.js +152 -0
- package/dist/target-integrations.js.map +1 -0
- package/dist/vector-tier.d.ts +6 -1
- package/dist/vector-tier.d.ts.map +1 -1
- package/dist/vector-tier.js +32 -7
- package/dist/vector-tier.js.map +1 -1
- package/dist/workflow-run.d.ts.map +1 -1
- package/dist/workflow-run.js +18 -12
- package/dist/workflow-run.js.map +1 -1
- package/package.json +19 -15
- package/sbom.json +591 -81
- package/src/amendment-trace.ts +12 -1
- package/src/compounding.ts +300 -1
- package/src/eta.ts +590 -0
- package/src/feature-adr-checkpoints.ts +39 -3
- package/src/feature-adr-decision-recall.ts +652 -0
- package/src/feature-adr-landing.ts +109 -0
- package/src/feature-adr-routing.ts +224 -65
- package/src/guard-promotion.ts +245 -4
- package/src/guard-volume.ts +752 -0
- package/src/guard.ts +148 -4
- package/src/index.ts +65 -3
- package/src/integration-apply.ts +332 -0
- package/src/integration-evidence.ts +89 -0
- package/src/integration-probe-worker.ts +310 -0
- package/src/integration-receipts/claude-code/mcp/2.1.235.json +35 -0
- package/src/integrations-verify.ts +258 -0
- package/src/lesson-generalization.ts +115 -0
- package/src/loop-blobs.generated.ts +8 -8
- package/src/loop-plan.ts +36 -3
- package/src/loop-render.ts +50 -6
- package/src/loop-trace.ts +91 -2
- package/src/model-recommender.ts +35 -4
- package/src/operations.ts +199 -2
- package/src/patterns.ts +252 -43
- package/src/setup.ts +20 -17
- package/src/target-integrations.ts +225 -0
- package/src/vector-tier.ts +44 -14
- package/src/workflow-run.ts +18 -11
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
*
|
|
15
15
|
* The load-bearing property: a model that WRITES code must not also SELF-QE.
|
|
16
16
|
* When `args.models.qe` is unset, the QE stage is auto-routed to the OTHER
|
|
17
|
-
* family than the resolved coder (codex-coder → Claude `
|
|
18
|
-
*
|
|
17
|
+
* family than the resolved coder (codex-coder → Claude `sonnet`; Claude-coder →
|
|
18
|
+
* the Codex-budget cell, or `opus` if codex is unavailable — never a block).
|
|
19
19
|
*
|
|
20
20
|
* DESIGN CONSTRAINT — the Workflow parser is STRICTER than `node --check`
|
|
21
21
|
* (no nested template literals, no inline `cond ? agent() : null` in arrays).
|
|
@@ -29,40 +29,27 @@
|
|
|
29
29
|
/** Blob version stamp read by scripts/gen-loop-blobs.mjs (feature loop-designer, ADR-004) — the
|
|
30
30
|
* ONLY loop-designer change to this canonical file; bump when any blob-exported semantic changes. */
|
|
31
31
|
export const BLOB_VERSION = '1.0.0';
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* The gpt-5.6-ready TOP codex-id pick — SHARED by the cross-model QE default and the usage
|
|
52
|
-
* override so adding an id to {@link KNOWN_CODEX} (e.g. `gpt-5.7`) retargets BOTH with zero
|
|
53
|
-
* control-flow diff (AC-3). Pinned `CODEX_MODEL` when ≠ `'auto'`; else the last non-`auto` key of
|
|
54
|
-
* `KNOWN_CODEX`.
|
|
55
|
-
*/
|
|
32
|
+
export const STAGE_EFFORT = { override: {
|
|
33
|
+
router: 'medium',
|
|
34
|
+
requirements: 'medium',
|
|
35
|
+
research: 'medium',
|
|
36
|
+
adr: 'high',
|
|
37
|
+
ideation: 'medium',
|
|
38
|
+
ddd: 'high',
|
|
39
|
+
architecture: 'high',
|
|
40
|
+
plan: 'high',
|
|
41
|
+
code: 'medium',
|
|
42
|
+
qe: 'high',
|
|
43
|
+
fleet: 'medium',
|
|
44
|
+
} };
|
|
45
|
+
/** One-release compatibility alias for existing public imports. */
|
|
46
|
+
// Frozen (QE round-1 F10): the alias exports the SAME object as the live effort table, so a
|
|
47
|
+
// consumer mutating the compatibility alias would silently mutate shipped routing. Freezing keeps
|
|
48
|
+
// the read-compatibility promise while making any such write throw loudly in strict mode.
|
|
49
|
+
export const OVERRIDE_REASONING = Object.freeze(STAGE_EFFORT.override);
|
|
50
|
+
/** The flagship Codex id, independent of the spellability allowlist's insertion order. */
|
|
56
51
|
export function topCodexId(env) {
|
|
57
|
-
|
|
58
|
-
if (top === 'auto') {
|
|
59
|
-
const ids = Object.keys(KNOWN_CODEX);
|
|
60
|
-
for (let i = 0; i < ids.length; i++) {
|
|
61
|
-
if (ids[i] !== 'auto')
|
|
62
|
-
top = ids[i] || top;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return top;
|
|
52
|
+
return env.CODEX_MODEL !== 'auto' ? env.CODEX_MODEL : CODEX_TIERS.flagship;
|
|
66
53
|
}
|
|
67
54
|
/**
|
|
68
55
|
* The PURE hysteresis core (the load-bearing safety property, AC-1). Given the previous override
|
|
@@ -104,10 +91,69 @@ export function decideUsageAction(prevOverride, signal, threshold) {
|
|
|
104
91
|
// ── Data tables (data-only extensibility — gpt-5.6-ready) ───────────────────
|
|
105
92
|
/** Known codex ids. Adding a new id (e.g. `'gpt-5.7'`) is a DATA-ONLY change. */
|
|
106
93
|
export const KNOWN_CODEX = { auto: 1, 'gpt-5.5': 1, 'gpt-5.6': 1, 'gpt-5.6-luna': 1, 'gpt-5.6-terra': 1, 'gpt-5.6-sol': 1 };
|
|
94
|
+
/**
|
|
95
|
+
* Capability tiers are routing data, separate from {@link KNOWN_CODEX}'s spellability role.
|
|
96
|
+
* The allowlist is not an availability check — probe every id before every run
|
|
97
|
+
* (`codex exec -m <id> "Reply with exactly: OK"`); ids drift in both directions on a
|
|
98
|
+
* timescale of days (probed 2026-08-18: 3 ids HTTP 400; probed 2026-08-29: all 3 exit 0).
|
|
99
|
+
*/
|
|
100
|
+
export const CODEX_TIERS = {
|
|
101
|
+
flagship: 'gpt-5.6-sol',
|
|
102
|
+
workhorse: 'gpt-5.6-terra',
|
|
103
|
+
'high-volume': 'gpt-5.6-luna',
|
|
104
|
+
};
|
|
105
|
+
export function codexIdForTier(tier, env) {
|
|
106
|
+
return env.CODEX_MODEL !== 'auto' ? env.CODEX_MODEL : CODEX_TIERS[tier];
|
|
107
|
+
}
|
|
108
|
+
export const BUDGET_PRESETS = {
|
|
109
|
+
normal: { claude: 'normal', codex: 'normal' },
|
|
110
|
+
eco: { claude: 'eco', codex: 'eco' },
|
|
111
|
+
hybrid: { claude: 'eco', codex: 'normal' },
|
|
112
|
+
};
|
|
113
|
+
export function resolveBudgetMode(raw) {
|
|
114
|
+
if (raw === undefined)
|
|
115
|
+
return BUDGET_PRESETS.normal;
|
|
116
|
+
if (typeof raw === 'string') {
|
|
117
|
+
const preset = BUDGET_PRESETS[raw];
|
|
118
|
+
if (!preset)
|
|
119
|
+
throw new RangeError('budget: unknown preset "' + raw + '" — valid: normal|eco|hybrid');
|
|
120
|
+
return preset;
|
|
121
|
+
}
|
|
122
|
+
if (raw && typeof raw === 'object') {
|
|
123
|
+
const value = raw;
|
|
124
|
+
for (const key of Object.keys(value)) {
|
|
125
|
+
if (key !== 'claude' && key !== 'codex') {
|
|
126
|
+
throw new RangeError('budget: unknown family key "' + key + '" — valid: claude|codex');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
for (const key of ['claude', 'codex']) {
|
|
130
|
+
const level = value[key];
|
|
131
|
+
if (level !== undefined && level !== 'normal' && level !== 'eco') {
|
|
132
|
+
throw new RangeError('budget.' + key + ': unknown level "' + level + '" — valid: normal|eco');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
claude: value.claude || 'normal',
|
|
137
|
+
codex: value.codex || 'normal',
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
throw new RangeError('budget: expected a preset name or {claude,codex} object, got ' + typeof raw);
|
|
141
|
+
}
|
|
142
|
+
export function budgetPresetName(axis) {
|
|
143
|
+
for (const name of ['normal', 'eco', 'hybrid']) {
|
|
144
|
+
const preset = BUDGET_PRESETS[name];
|
|
145
|
+
if (preset.claude === axis.claude && preset.codex === axis.codex)
|
|
146
|
+
return name;
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
107
150
|
/** The Claude model names the Workflow runtime accepts as `agent()` `model`. */
|
|
108
151
|
export const CLAUDE_NAMES = { fable: 1, opus: 1, sonnet: 1, haiku: 1 };
|
|
109
|
-
/**
|
|
110
|
-
|
|
152
|
+
/**
|
|
153
|
+
* The codex-companion `--effort` vocabulary. `minimal` remains temporarily for compatibility
|
|
154
|
+
* until a live companion probe establishes whether it can be retired; `max` is part of GPT-5.6's ladder.
|
|
155
|
+
*/
|
|
156
|
+
export const VALID_REASONING = { none: 1, minimal: 1, low: 1, medium: 1, high: 1, xhigh: 1, max: 1 };
|
|
111
157
|
/**
|
|
112
158
|
* The proven DEFAULT TABLE, applied only when the user opts into routing.
|
|
113
159
|
* `code`/`qe` are `null` SENTINELS: their defaults are DERIVED (the coder knob /
|
|
@@ -126,6 +172,68 @@ export const DEFAULT_MODELS = {
|
|
|
126
172
|
qe: null,
|
|
127
173
|
fleet: 'sonnet',
|
|
128
174
|
};
|
|
175
|
+
/** Four family half-tables; presets compose the Claude and Codex halves independently. */
|
|
176
|
+
export const ROUTING_TABLES = {
|
|
177
|
+
claude: {
|
|
178
|
+
claude: {
|
|
179
|
+
normal: { router: 'sonnet', requirements: 'sonnet', research: 'sonnet', adr: 'fable', ideation: 'sonnet', ddd: 'fable', architecture: 'fable', plan: 'opus', code: 'sonnet', fleet: 'sonnet' },
|
|
180
|
+
eco: { router: 'sonnet', requirements: 'sonnet', research: 'sonnet', adr: 'opus', ideation: 'sonnet', ddd: 'opus', architecture: 'opus', plan: 'sonnet', code: 'sonnet', fleet: 'sonnet' },
|
|
181
|
+
},
|
|
182
|
+
codex: { normal: {}, eco: {} },
|
|
183
|
+
},
|
|
184
|
+
codex: {
|
|
185
|
+
claude: {
|
|
186
|
+
normal: { router: 'sonnet', qe: 'sonnet', fleet: 'sonnet' },
|
|
187
|
+
eco: { router: 'sonnet', qe: 'sonnet', fleet: 'sonnet' },
|
|
188
|
+
},
|
|
189
|
+
codex: { normal: {}, eco: {} },
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
function codexCell(tier, effort, env) {
|
|
193
|
+
return 'codex:' + codexIdForTier(tier, env) + ':' + effort;
|
|
194
|
+
}
|
|
195
|
+
export function budgetTable(primary, mode, env) {
|
|
196
|
+
const claudeHalf = ROUTING_TABLES[primary].claude[mode.claude];
|
|
197
|
+
let codexHalf;
|
|
198
|
+
if (primary === 'claude') {
|
|
199
|
+
const qeSpec = mode.codex === 'normal'
|
|
200
|
+
? codexCell('flagship', 'high', env)
|
|
201
|
+
: codexCell('workhorse', 'medium', env);
|
|
202
|
+
codexHalf = { ...ROUTING_TABLES.claude.codex[mode.codex], qe: env.codexAvailable === false ? 'opus' : qeSpec };
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
const normal = mode.codex === 'normal';
|
|
206
|
+
const design = codexCell(normal ? 'flagship' : 'workhorse', normal ? 'high' : 'medium', env);
|
|
207
|
+
codexHalf = {
|
|
208
|
+
requirements: design,
|
|
209
|
+
research: design,
|
|
210
|
+
adr: design,
|
|
211
|
+
ideation: design,
|
|
212
|
+
ddd: design,
|
|
213
|
+
architecture: design,
|
|
214
|
+
plan: codexCell(normal ? 'flagship' : 'workhorse', normal ? 'high' : 'low', env),
|
|
215
|
+
code: codexCell(normal ? 'flagship' : 'workhorse', 'medium', env),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return { ...claudeHalf, ...codexHalf };
|
|
219
|
+
}
|
|
220
|
+
/** Optional Claude precision review after A-normal's recall-oriented Sonnet QE. */
|
|
221
|
+
export function qePrecisionPassSpec(primary, budget, tier, _env) {
|
|
222
|
+
if (primary !== 'codex')
|
|
223
|
+
return null;
|
|
224
|
+
if (budget.claude !== 'normal')
|
|
225
|
+
return null;
|
|
226
|
+
if (tier !== 'L' && tier !== 'XL')
|
|
227
|
+
return null;
|
|
228
|
+
return 'opus';
|
|
229
|
+
}
|
|
230
|
+
/** Build/test-time guard for the matrix's load-bearing cross-family property. */
|
|
231
|
+
export function assertCrossFamilyQe(codeSpec, qeSpec) {
|
|
232
|
+
const fam = (s) => (s && String(s).split(':')[0] === 'codex') ? 'codex' : 'claude';
|
|
233
|
+
if (fam(codeSpec) === fam(qeSpec)) {
|
|
234
|
+
throw new Error('cross-family QE violated: code=' + codeSpec + ' qe=' + qeSpec);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
129
237
|
// ── Pure resolvers (byte-equivalent to the workflow's inline block) ──────────
|
|
130
238
|
/**
|
|
131
239
|
* Turn a compact model SPEC into `agent()` opts.
|
|
@@ -157,11 +265,8 @@ export function specToOpts(spec, env) {
|
|
|
157
265
|
id = env.CODEX_MODEL;
|
|
158
266
|
}
|
|
159
267
|
let reasoning = parts[2] || 'high';
|
|
160
|
-
// Clamp to the codex-companion --effort vocabulary so a bad token (e.g. `codex:gpt-5.6:banana`)
|
|
161
|
-
// can't silently reach the runtime as `--effort banana` (QE LOW). Unknown ⇒ 'high'.
|
|
162
268
|
if (!VALID_REASONING[reasoning]) {
|
|
163
|
-
|
|
164
|
-
reasoning = 'high';
|
|
269
|
+
throw new RangeError('models: invalid reasoning "' + reasoning + '" — valid: ' + Object.keys(VALID_REASONING).join('|'));
|
|
165
270
|
}
|
|
166
271
|
return { agentType: 'codex:codex-rescue', codexModel: id, _reasoning: reasoning };
|
|
167
272
|
}
|
|
@@ -188,34 +293,43 @@ export function resolveCoderSpec(env) {
|
|
|
188
293
|
* `MODELS.code='codex'` still routes QE to Claude (never codex-self-QE).
|
|
189
294
|
*/
|
|
190
295
|
export function coderIsCodex(env) {
|
|
191
|
-
if (env.CODER === 'codex' || env.CODER === 'codex-fallback')
|
|
192
|
-
return true;
|
|
193
296
|
const codeSpec = env.MODELS.code;
|
|
194
|
-
if (codeSpec &&
|
|
297
|
+
if (codeSpec !== undefined && codeSpec !== null)
|
|
298
|
+
return String(codeSpec).split(':')[0] === 'codex';
|
|
299
|
+
if (env.CODER === 'codex' || env.CODER === 'codex-fallback')
|
|
195
300
|
return true;
|
|
196
|
-
return
|
|
301
|
+
return env.primary === 'codex';
|
|
197
302
|
}
|
|
198
303
|
/**
|
|
199
304
|
* The CROSS-MODEL QE default (load-bearing). Called only when `MODELS.qe` is
|
|
200
305
|
* unset. Resolves to the OTHER family than the coder:
|
|
201
|
-
* - coder is codex → `'
|
|
202
|
-
* - coder is Claude →
|
|
203
|
-
*
|
|
306
|
+
* - coder is codex → `'sonnet'` (the recall-oriented Claude reviewer)
|
|
307
|
+
* - coder is Claude → the Codex-budget cell (`sol:high` or `terra:medium`),
|
|
308
|
+
* or `'opus'` when Codex is unavailable (never block)
|
|
309
|
+
* A pinned `CODEX_MODEL` still outranks the tier selected by the budget table.
|
|
204
310
|
*/
|
|
205
|
-
export function
|
|
206
|
-
if (
|
|
207
|
-
return '
|
|
311
|
+
export function resolveQeSpecForCoder(coderCodex, env) {
|
|
312
|
+
if (coderCodex)
|
|
313
|
+
return 'sonnet';
|
|
208
314
|
const CODEX_AVAILABLE = env.codexAvailable !== false;
|
|
209
315
|
if (!CODEX_AVAILABLE)
|
|
210
316
|
return 'opus';
|
|
211
|
-
|
|
317
|
+
const budget = resolveBudgetMode(env.budget);
|
|
318
|
+
return budget.codex === 'eco'
|
|
319
|
+
? 'codex:' + codexIdForTier('workhorse', env) + ':medium'
|
|
320
|
+
: 'codex:' + codexIdForTier('flagship', env) + ':high';
|
|
321
|
+
}
|
|
322
|
+
export function resolveQeSpec(env) {
|
|
323
|
+
return resolveQeSpecForCoder(coderIsCodex(env), env);
|
|
212
324
|
}
|
|
213
325
|
/**
|
|
214
|
-
* Whether the caller opted into routing at all. When FALSE (no `args.models`,
|
|
215
|
-
*
|
|
326
|
+
* Whether the caller opted into routing at all. When FALSE (no axes, `args.models`,
|
|
327
|
+
* or Codex knobs), every Claude stage resolves to `{}` → byte-identical to today.
|
|
216
328
|
*/
|
|
217
329
|
export function routingRequested(env) {
|
|
218
330
|
return (Object.keys(env.MODELS).length > 0 ||
|
|
331
|
+
env.primary !== undefined ||
|
|
332
|
+
env.budget !== undefined ||
|
|
219
333
|
env.PLANNER === 'codex' ||
|
|
220
334
|
env.CODER === 'codex' ||
|
|
221
335
|
env.CODER === 'codex-fallback' ||
|
|
@@ -247,12 +361,12 @@ export function qeShouldUseCodex(env) {
|
|
|
247
361
|
* Resolve a stage to its `agent()` opts fragment.
|
|
248
362
|
* 1. explicit `MODELS[stage]` wins
|
|
249
363
|
* 2. else if the user did NOT opt into routing → `{}` (byte-identical BC path)
|
|
250
|
-
* 3. else the
|
|
364
|
+
* 3. else the primary × per-family-budget table fills the gap
|
|
251
365
|
* 4. `code`/`qe` `null` sentinels resolve via the coder / cross-model rules
|
|
252
366
|
*/
|
|
253
367
|
export function resolveStageModel(stage, env) {
|
|
254
368
|
if (env.usageOverride) {
|
|
255
|
-
const r = (env.usageReasoning && env.usageReasoning[stage]) ||
|
|
369
|
+
const r = (env.usageReasoning && env.usageReasoning[stage]) || STAGE_EFFORT.override[stage] || 'medium';
|
|
256
370
|
const o = specToOpts('codex:' + topCodexId(env) + ':' + r, env);
|
|
257
371
|
o._usageSwitched = true;
|
|
258
372
|
return o;
|
|
@@ -261,7 +375,18 @@ export function resolveStageModel(stage, env) {
|
|
|
261
375
|
if (spec === undefined) {
|
|
262
376
|
if (!routingRequested(env))
|
|
263
377
|
return {};
|
|
264
|
-
|
|
378
|
+
if (stage === 'code' && (env.CODER === 'codex' || env.CODER === 'codex-fallback')) {
|
|
379
|
+
return specToOpts(resolveCoderSpec(env), env);
|
|
380
|
+
}
|
|
381
|
+
if (stage === 'plan' && env.PLANNER === 'codex') {
|
|
382
|
+
return specToOpts('codex:' + env.CODEX_MODEL + ':high', env);
|
|
383
|
+
}
|
|
384
|
+
if (stage === 'qe') {
|
|
385
|
+
return specToOpts(resolveQeSpec(env), env);
|
|
386
|
+
}
|
|
387
|
+
const resolvedPrimary = env.primary || 'claude';
|
|
388
|
+
const cell = budgetTable(resolvedPrimary, resolveBudgetMode(env.budget), env)[stage];
|
|
389
|
+
spec = cell !== undefined ? cell : DEFAULT_MODELS[stage];
|
|
265
390
|
}
|
|
266
391
|
if (stage === 'code' && (spec === null || spec === undefined))
|
|
267
392
|
return specToOpts(resolveCoderSpec(env), env);
|
|
@@ -297,15 +422,11 @@ export function codexEffortHint(opts) {
|
|
|
297
422
|
return '';
|
|
298
423
|
}
|
|
299
424
|
// ── Step-7.5 CODE landed barrier (Codex out-of-band write flush) ────────────
|
|
300
|
-
// Widened 2026-08-28 (MEASURED
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
//
|
|
305
|
-
// the underlying conflation of TIMEOUT with CONFIRMED-ABSENCE (filed as a backlog item — the barrier
|
|
306
|
-
// still cannot see the codex-companion job's own liveness) — it only narrows how often the false
|
|
307
|
-
// negative fires for a realistic M-tier build, at the cost of a longer worst-case wait when the
|
|
308
|
-
// coder truly produced nothing.
|
|
425
|
+
// Widened 2026-08-28 (MEASURED): the dz-deadwood coder ran 46m11s, and slop-lint was still running
|
|
426
|
+
// with its recorded PID alive at 16m38s when the old 120s window had already declared absence.
|
|
427
|
+
// Changed 2026-08-30 (MEASURED: six false verdicts; plus task-mtfhglwk stayed status=running for
|
|
428
|
+
// 8h51m after recorded PID 3639268 disappeared): this is now one git-evidence/backoff window inside
|
|
429
|
+
// the liveness-driven loop, not the whole barrier. The separate hard ceiling bounds a live worker.
|
|
309
430
|
export const DEFAULT_CODE_LANDING_MAX_WAIT_MS = 1_020_000;
|
|
310
431
|
export const DEFAULT_CODE_LANDING_BACKOFF_MS = [
|
|
311
432
|
1_000,
|
|
@@ -335,10 +456,10 @@ export const CODE_LANDED_BARRIER_SLEEPS_SECONDS = DEFAULT_CODE_LANDING_BACKOFF_M
|
|
|
335
456
|
const CODE_LANDING_PIPELINE_PREFIXES = ['features/', '.dz/', '.agentic-qe/', 'roam/'];
|
|
336
457
|
/** Bumped whenever the landing protocol's meaning changes. Recorded on every code-stage result so a
|
|
337
458
|
* pre-protocol checkpoint reads as no-checkpoint instead of resuming into new semantics. */
|
|
338
|
-
export const LANDING_PROTOCOL_VERSION =
|
|
459
|
+
export const LANDING_PROTOCOL_VERSION = 3;
|
|
339
460
|
/** R6: salted into the code stage's checkpoint hash PARTS (not the schema version — ADR-003 keeps
|
|
340
461
|
* `CKPT_SCHEMA_VERSION` at 'fa-ckpt-2' deliberately, so ONLY the code stage re-runs). */
|
|
341
|
-
export const LANDING_HASH_TOKEN = 'landing-
|
|
462
|
+
export const LANDING_HASH_TOKEN = 'landing-v3';
|
|
342
463
|
/**
|
|
343
464
|
* The CLOSED landing-predicate vocabulary, as DATA (QE F6) — exhaustive-record derived, so `tsc`
|
|
344
465
|
* fails the build if the union gains or loses a member. `'expected-path'` and `'any-code-change'`
|
|
@@ -1225,12 +1346,16 @@ export function codexExecCommand(input) {
|
|
|
1225
1346
|
const modelId = String(o.modelId === undefined || o.modelId === null ? '' : o.modelId);
|
|
1226
1347
|
if (!isSafeCodexId(modelId))
|
|
1227
1348
|
return null;
|
|
1349
|
+
const reasoning = o.reasoning === undefined || o.reasoning === null || o.reasoning === '' ? 'high' : String(o.reasoning);
|
|
1350
|
+
if (!VALID_REASONING[reasoning])
|
|
1351
|
+
return null;
|
|
1228
1352
|
const raw = Number(o.timeoutSeconds);
|
|
1229
1353
|
const seconds = raw === raw && raw !== Infinity && raw > 0 ? Math.floor(raw) : CODEX_EXEC_TIMEOUT_SECONDS;
|
|
1230
1354
|
const repo = String(o.repo === undefined || o.repo === null ? '' : o.repo);
|
|
1231
1355
|
const cd = repo === '' ? '' : ' -C ' + codexSq(repo);
|
|
1232
1356
|
return timeoutBinOrDefault(o.timeoutBin) + ' ' + seconds + ' codex exec' + cd
|
|
1233
|
-
+ ' -m ' + codexSq(modelId) + ' ' + codexSq(
|
|
1357
|
+
+ ' -m ' + codexSq(modelId) + ' -c model_reasoning_effort=' + codexSq(reasoning)
|
|
1358
|
+
+ ' ' + codexSq(String(o.prompt === undefined || o.prompt === null ? '' : o.prompt)) + ' < /dev/null';
|
|
1234
1359
|
}
|
|
1235
1360
|
export function codexReviewCommand(input) {
|
|
1236
1361
|
const o = input || {};
|