dorfl 0.11.0 → 0.11.1

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/src/do-config.ts CHANGED
@@ -17,6 +17,8 @@ import type {
17
17
  export interface HarnessFlags {
18
18
  agentCmd?: string;
19
19
  model?: string;
20
+ /** `--build-model <id>` — the builder-specific model override (falls back to `--model`). */
21
+ buildModel?: string;
20
22
  harness?: string;
21
23
  piBin?: string;
22
24
  /**
@@ -43,6 +45,9 @@ export function harnessFlagOverrides(flags: HarnessFlags): PartialConfig {
43
45
  if (flags.model !== undefined) {
44
46
  overrides.model = flags.model;
45
47
  }
48
+ if (flags.buildModel !== undefined) {
49
+ overrides.buildModel = flags.buildModel;
50
+ }
46
51
  if (flags.harness === 'null' || flags.harness === 'pi') {
47
52
  overrides.harness = flags.harness;
48
53
  }
@@ -69,6 +74,7 @@ export function doFlagOverrides(
69
74
  flags: HarnessFlags &
70
75
  ReviewFlags &
71
76
  TaskerLoopFlags &
77
+ TriageModelFlags &
72
78
  FreshWorktreeGateFlags &
73
79
  SelectionOrderFlags &
74
80
  ObservationTriageFlags &
@@ -115,6 +121,10 @@ export function doFlagOverrides(
115
121
  // family from the gate's `--review*`, never sharing a flag/key/field name.
116
122
  // The flag fields bridge onto the `taskerLoop*` config keys.
117
123
  ...taskerLoopFlagOverrides(flags),
124
+ // `--triage-model <id>` (the advance lifecycle gates' model — surface, apply,
125
+ // triage) rides the SAME chain. DISTINCT from `--review-model`/
126
+ // `--tasker-loop-model`; falls back to `model` when unset at all levels.
127
+ ...triageModelFlagOverrides(flags),
118
128
  // `--fresh-worktree-gate`/`--no-fresh-worktree-gate` (run the acceptance gate
119
129
  // against the REBASED tip in a clean throwaway worktree, ON by default) rides
120
130
  // the SAME chain — a DISTINCT family from `--review*`/`--tasker-loop*`.
@@ -232,6 +242,34 @@ export function taskerLoopFlagOverrides(flags: TaskerLoopFlags): PartialConfig {
232
242
  return overrides;
233
243
  }
234
244
 
245
+ /**
246
+ * The `--triage-model` flag — the model the advance lifecycle gates (surface,
247
+ * apply, triage) run on. A single string flag, distinct from the harness
248
+ * `--model`/`--build-model` family and from `--review-model`/
249
+ * `--tasker-loop-model`. Resolved through the SAME `flag > env > per-repo >
250
+ * global > default` chain; falls back to `model` when unset at all levels (via
251
+ * `applyModelFallbacks`).
252
+ */
253
+ export interface TriageModelFlags {
254
+ /** `--triage-model <id>` — the advance lifecycle gates' model (routing intent). */
255
+ triageModel?: string;
256
+ }
257
+
258
+ /**
259
+ * Map the `--triage-model` flag into a {@link PartialConfig} override. Only
260
+ * contributes when the flag is actually present (absent flag => absent key), so
261
+ * the override layer never clobbers a lower-precedence source with `undefined`.
262
+ */
263
+ export function triageModelFlagOverrides(
264
+ flags: TriageModelFlags,
265
+ ): PartialConfig {
266
+ const overrides: PartialConfig = {};
267
+ if (flags.triageModel !== undefined) {
268
+ overrides.triageModel = flags.triageModel;
269
+ }
270
+ return overrides;
271
+ }
272
+
235
273
  /**
236
274
  * The fresh-worktree acceptance-gate CLI flag (`do`/`complete`/`run`):
237
275
  * `--fresh-worktree-gate` / `--no-fresh-worktree-gate`, the POSITIVE boolean
package/src/do.ts CHANGED
@@ -549,7 +549,9 @@ export interface DoOptions {
549
549
  watch?: boolean;
550
550
  /** The configured agent command the harness shells out to (null adapter). */
551
551
  agentCmd?: string;
552
- /** The model routing intent forwarded to the harness (ADR §13). */
552
+ /** The model routing intent forwarded to the harness (ADR §13). Sourced
553
+ * from `config.buildModel` (which inherits the general `config.model` when
554
+ * unset via `applyModelFallbacks`). */
553
555
  model?: string;
554
556
  /**
555
557
  * The HOST-ONLY root folder under which this run's pi session FILE is generated
package/src/env-config.ts CHANGED
@@ -152,6 +152,9 @@ const KEY_COERCIONS: {[K in keyof Config]?: Coercion} = {
152
152
  // A non-string / empty value is normalised at load (`validateDorflCmdConfig`).
153
153
  dorflCmd: 'string',
154
154
  model: 'string',
155
+ buildModel: 'string',
156
+ triageModel: 'string',
157
+ intakeModel: 'string',
155
158
  harness: {enum: ['null', 'pi']},
156
159
  piBin: 'string',
157
160
  sessionsDir: 'string',
@@ -2,6 +2,7 @@ import {readFileSync, existsSync} from 'node:fs';
2
2
  import {join} from 'node:path';
3
3
  import {
4
4
  mergeConfig,
5
+ applyModelFallbacks,
5
6
  validateDeadlineConfig,
6
7
  validateDorflCmdConfig,
7
8
  warnDeprecatedConfigKeys,
@@ -209,8 +210,12 @@ export const REPO_ALLOWED_KEYS = [
209
210
  // `model` (which model this repo's work runs on) and `harness` (which adapter)
210
211
  // are legitimate repo properties (ADR §13) — model is routing intent, not auth,
211
212
  // and a repo may prefer a given harness. `piBin`/`agentCmd` stay host-only
212
- // (machine paths/commands), so they are rejected below.
213
+ // (machine paths/commands), so they are rejected below. `buildModel` is the
214
+ // builder-specific override of the general `model` (falls back to `model` when
215
+ // unset at all levels — see `applyModelFallbacks`); like `model` it is routing
216
+ // intent (not auth), so it is repo-appropriate.
213
217
  'model',
218
+ 'buildModel',
214
219
  'harness',
215
220
  // Gate 2 (PR/code review) policy is a genuine repo property (GATES spec
216
221
  // `work/specs/tasked/review.md`), resolved per-repo like `integration`/`autoBuild`:
@@ -230,6 +235,14 @@ export const REPO_ALLOWED_KEYS = [
230
235
  'taskerLoop',
231
236
  'taskerLoopMax',
232
237
  'taskerLoopModel',
238
+ // `triageModel` (the model the advance lifecycle gates — surface, apply,
239
+ // triage — run on) is a genuine repo property like `reviewModel`/
240
+ // `taskerLoopModel`: routing intent (not auth), resolved per-repo through the
241
+ // same chain. `intakeModel` (the front-door intake decision agent's model) is
242
+ // likewise repo-appropriate. Each falls back to `model` when unset at all
243
+ // levels (see `applyModelFallbacks`).
244
+ 'triageModel',
245
+ 'intakeModel',
233
246
  // `freshWorktreeGate` (run the acceptance gate against the REBASED tip in a
234
247
  // clean throwaway worktree, ON by default) is a genuine repo property exactly
235
248
  // like `verify`/`prepare`/`review`: whether this repo's gate tests the merged
@@ -646,6 +659,11 @@ export function resolveRepoConfigFromLoaded(
646
659
  // (flag / env / per-repo / global) surfaces the same clear error (ADR
647
660
  // `dorfl-cmd-repo-settable-exception-to-host-only`).
648
661
  validateDorflCmdConfig(config);
662
+ // Apply the per-role model fallbacks: `buildModel`, `reviewModel`, and
663
+ // `taskerLoopModel` each inherit the general `model` when unset at EVERY level
664
+ // (flag / env / per-repo / global). Applied HERE — the resolution FINAL point —
665
+ // so the fallback uses the FULLY resolved `model`, never an intermediate layer.
666
+ applyModelFallbacks(config);
649
667
  return {
650
668
  config,
651
669
  rejected: repo.rejected,
package/src/run.ts CHANGED
@@ -834,8 +834,9 @@ async function runOneItem(
834
834
 
835
835
  // 4. Run the agent — via the injected runner (tests) or the harness seam
836
836
  // (null adapter by default), shelling out to the configured agentCmd. The
837
- // resolved per-repo `model` (ADR §13) flows through the seam to the adapter;
838
- // a `{model}`-in-agentCmd misconfiguration surfaces as agent-failed.
837
+ // resolved per-repo `buildModel` (ADR §13, which inherits the general
838
+ // `model` when unset) flows through the seam to the adapter; a
839
+ // `{model}`-in-agentCmd misconfiguration surfaces as agent-failed.
839
840
  let agent: {ok: boolean; detail?: string; output?: string};
840
841
  try {
841
842
  agent = runAgent(
@@ -844,7 +845,7 @@ async function runOneItem(
844
845
  prompt,
845
846
  slug,
846
847
  config.agentCmd,
847
- config.model,
848
+ config.buildModel,
848
849
  config.sessionsDir,
849
850
  );
850
851
  } catch (err) {