@dzhechkov/harness-core 0.8.2 → 0.8.6
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 +146 -58
- package/README.md +72 -2
- package/dist/cmd-usage.d.ts +148 -0
- package/dist/cmd-usage.d.ts.map +1 -0
- package/dist/cmd-usage.js +548 -0
- package/dist/cmd-usage.js.map +1 -0
- package/dist/compounding.d.ts +4 -0
- package/dist/compounding.d.ts.map +1 -1
- package/dist/compounding.js +6 -0
- package/dist/compounding.js.map +1 -1
- package/dist/contract-checklist.d.ts +123 -0
- package/dist/contract-checklist.d.ts.map +1 -0
- package/dist/contract-checklist.js +700 -0
- package/dist/contract-checklist.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 +37 -2
- package/dist/feature-adr-checkpoints.js.map +1 -1
- 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 +208 -59
- package/dist/feature-adr-routing.js.map +1 -1
- package/dist/guard.d.ts +25 -0
- package/dist/guard.d.ts.map +1 -1
- package/dist/guard.js +59 -1
- package/dist/guard.js.map +1 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- 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/qe-bridge.d.ts.map +1 -1
- package/dist/qe-bridge.js +9 -0
- package/dist/qe-bridge.js.map +1 -1
- package/dist/restart-advisor.d.ts +103 -0
- package/dist/restart-advisor.d.ts.map +1 -0
- package/dist/restart-advisor.js +445 -0
- package/dist/restart-advisor.js.map +1 -0
- package/dist/slop-lint.d.ts +128 -0
- package/dist/slop-lint.d.ts.map +1 -0
- package/dist/slop-lint.js +607 -0
- package/dist/slop-lint.js.map +1 -0
- 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 +277 -57
- package/src/cmd-usage.ts +720 -0
- package/src/compounding.ts +13 -0
- package/src/contract-checklist.ts +973 -0
- package/src/deadwood-allowlist.json +80 -0
- package/src/feature-adr-checkpoints.ts +38 -2
- package/src/feature-adr-routing.ts +238 -55
- package/src/guard.ts +79 -1
- package/src/index.ts +81 -1
- 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/qe-bridge.ts +9 -0
- package/src/restart-advisor.ts +579 -0
- package/src/slop-lint.ts +762 -0
- package/src/slop-markers.json +71 -0
- 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,7 +422,16 @@ export function codexEffortHint(opts) {
|
|
|
297
422
|
return '';
|
|
298
423
|
}
|
|
299
424
|
// ── Step-7.5 CODE landed barrier (Codex out-of-band write flush) ────────────
|
|
300
|
-
|
|
425
|
+
// Widened 2026-08-28 (MEASURED, not a guess): the dz-deadwood coder job ran 46m11s end to end, and
|
|
426
|
+
// the slop-lint coder was independently confirmed STILL RUNNING (codex-companion status "running",
|
|
427
|
+
// pid alive, mid-TDD-cycle, actively applying file changes) at 16m38s elapsed — a full 8x past the
|
|
428
|
+
// old 120s window. The old window declared "genuinely not landed" and fed Step-8 QE a false empty
|
|
429
|
+
// tree, producing a grade-D report entirely about an absence that was never real. This does not fix
|
|
430
|
+
// the underlying conflation of TIMEOUT with CONFIRMED-ABSENCE (filed as a backlog item — the barrier
|
|
431
|
+
// still cannot see the codex-companion job's own liveness) — it only narrows how often the false
|
|
432
|
+
// negative fires for a realistic M-tier build, at the cost of a longer worst-case wait when the
|
|
433
|
+
// coder truly produced nothing.
|
|
434
|
+
export const DEFAULT_CODE_LANDING_MAX_WAIT_MS = 1_020_000;
|
|
301
435
|
export const DEFAULT_CODE_LANDING_BACKOFF_MS = [
|
|
302
436
|
1_000,
|
|
303
437
|
2_000,
|
|
@@ -310,6 +444,17 @@ export const DEFAULT_CODE_LANDING_BACKOFF_MS = [
|
|
|
310
444
|
20_000,
|
|
311
445
|
25_000,
|
|
312
446
|
25_000,
|
|
447
|
+
30_000,
|
|
448
|
+
30_000,
|
|
449
|
+
60_000,
|
|
450
|
+
60_000,
|
|
451
|
+
60_000,
|
|
452
|
+
90_000,
|
|
453
|
+
90_000,
|
|
454
|
+
120_000,
|
|
455
|
+
120_000,
|
|
456
|
+
120_000,
|
|
457
|
+
120_000,
|
|
313
458
|
];
|
|
314
459
|
export const CODE_LANDED_BARRIER_SLEEPS_SECONDS = DEFAULT_CODE_LANDING_BACKOFF_MS.map((ms) => ms / 1000);
|
|
315
460
|
const CODE_LANDING_PIPELINE_PREFIXES = ['features/', '.dz/', '.agentic-qe/', 'roam/'];
|
|
@@ -1205,12 +1350,16 @@ export function codexExecCommand(input) {
|
|
|
1205
1350
|
const modelId = String(o.modelId === undefined || o.modelId === null ? '' : o.modelId);
|
|
1206
1351
|
if (!isSafeCodexId(modelId))
|
|
1207
1352
|
return null;
|
|
1353
|
+
const reasoning = o.reasoning === undefined || o.reasoning === null || o.reasoning === '' ? 'high' : String(o.reasoning);
|
|
1354
|
+
if (!VALID_REASONING[reasoning])
|
|
1355
|
+
return null;
|
|
1208
1356
|
const raw = Number(o.timeoutSeconds);
|
|
1209
1357
|
const seconds = raw === raw && raw !== Infinity && raw > 0 ? Math.floor(raw) : CODEX_EXEC_TIMEOUT_SECONDS;
|
|
1210
1358
|
const repo = String(o.repo === undefined || o.repo === null ? '' : o.repo);
|
|
1211
1359
|
const cd = repo === '' ? '' : ' -C ' + codexSq(repo);
|
|
1212
1360
|
return timeoutBinOrDefault(o.timeoutBin) + ' ' + seconds + ' codex exec' + cd
|
|
1213
|
-
+ ' -m ' + codexSq(modelId) + ' ' + codexSq(
|
|
1361
|
+
+ ' -m ' + codexSq(modelId) + ' -c model_reasoning_effort=' + codexSq(reasoning)
|
|
1362
|
+
+ ' ' + codexSq(String(o.prompt === undefined || o.prompt === null ? '' : o.prompt)) + ' < /dev/null';
|
|
1214
1363
|
}
|
|
1215
1364
|
export function codexReviewCommand(input) {
|
|
1216
1365
|
const o = input || {};
|