dorfl 0.10.1 → 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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +99 -21
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +73 -12
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +28 -0
- package/dist/config.js.map +1 -1
- package/dist/do-config.d.ts +21 -1
- package/dist/do-config.d.ts.map +1 -1
- package/dist/do-config.js +19 -0
- package/dist/do-config.js.map +1 -1
- package/dist/do.d.ts +3 -1
- package/dist/do.d.ts.map +1 -1
- package/dist/do.js.map +1 -1
- package/dist/env-config.d.ts.map +1 -1
- package/dist/env-config.js +3 -0
- package/dist/env-config.js.map +1 -1
- package/dist/gc.d.ts +42 -0
- package/dist/gc.d.ts.map +1 -1
- package/dist/gc.js +71 -1
- package/dist/gc.js.map +1 -1
- package/dist/protocol/WORK-CONTRACT.md +11 -2
- package/dist/protocol/spec-template.md +1 -1
- package/dist/protocol/task-template.md +1 -1
- package/dist/repo-config.d.ts +1 -1
- package/dist/repo-config.d.ts.map +1 -1
- package/dist/repo-config.js +19 -2
- package/dist/repo-config.js.map +1 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +4 -3
- package/dist/run.js.map +1 -1
- package/dist/skills/drive-tasks/SKILL.md +21 -3
- package/dist/skills/setup/protocol/WORK-CONTRACT.md +11 -2
- package/dist/skills/setup/protocol/spec-template.md +1 -1
- package/dist/skills/setup/protocol/task-template.md +1 -1
- package/package.json +1 -1
- package/src/cli.ts +143 -21
- package/src/config.ts +90 -12
- package/src/do-config.ts +38 -0
- package/src/do.ts +3 -1
- package/src/env-config.ts +3 -0
- package/src/gc.ts +108 -1
- package/src/repo-config.ts +19 -1
- package/src/run.ts +4 -3
package/src/cli.ts
CHANGED
|
@@ -99,6 +99,7 @@ import {
|
|
|
99
99
|
freshWorktreeGateFlagOverrides,
|
|
100
100
|
mergeRetriesFlagOverrides,
|
|
101
101
|
noPRFlagOverrides,
|
|
102
|
+
triageModelFlagOverrides,
|
|
102
103
|
} from './do-config.js';
|
|
103
104
|
import {harnessReviewGate, harnessTaskAcceptanceGate} from './review-gate.js';
|
|
104
105
|
import {harnessSurfaceGate} from './surface-gate.js';
|
|
@@ -107,8 +108,13 @@ import {harnessApplyDecider} from './apply-decide.js';
|
|
|
107
108
|
import {harnessTaskReviewGate} from './tasker-review-loop.js';
|
|
108
109
|
import {runVerify} from './verify.js';
|
|
109
110
|
import {renderPrompt} from './prompt.js';
|
|
110
|
-
import {resolvePromptGuidance} from './config.js';
|
|
111
|
-
import {
|
|
111
|
+
import {resolvePromptGuidance, applyModelFallbacks} from './config.js';
|
|
112
|
+
import {
|
|
113
|
+
gc,
|
|
114
|
+
resolveArbiterKeyFromCwd,
|
|
115
|
+
enumerateWorkspaceArbiters,
|
|
116
|
+
RETAIN_REASON_TEXT,
|
|
117
|
+
} from './gc.js';
|
|
112
118
|
import {
|
|
113
119
|
runPrdToSpec,
|
|
114
120
|
type PrdToSpecResult,
|
|
@@ -211,7 +217,9 @@ function resolveGlobalConfig(
|
|
|
211
217
|
fileConfig: PartialConfig,
|
|
212
218
|
flags: PartialConfig,
|
|
213
219
|
): Config {
|
|
214
|
-
|
|
220
|
+
const config = mergeConfig({...fileConfig, ...envOverrides(), ...flags});
|
|
221
|
+
applyModelFallbacks(config);
|
|
222
|
+
return config;
|
|
215
223
|
}
|
|
216
224
|
|
|
217
225
|
/**
|
|
@@ -418,7 +426,7 @@ function buildRegistrySetAdvanceTick(options: {
|
|
|
418
426
|
noPR: config.noPR,
|
|
419
427
|
harness,
|
|
420
428
|
agentCmd: config.agentCmd,
|
|
421
|
-
model: config.
|
|
429
|
+
model: config.buildModel,
|
|
422
430
|
sessionsDir: config.sessionsDir,
|
|
423
431
|
review: config.review,
|
|
424
432
|
reviewModel: config.reviewModel,
|
|
@@ -443,12 +451,12 @@ function buildRegistrySetAdvanceTick(options: {
|
|
|
443
451
|
arbiter: arbiter ?? config.defaultArbiter,
|
|
444
452
|
doOptions,
|
|
445
453
|
surfaceGate: harnessSurfaceGate({harness, agentCmd: config.agentCmd}),
|
|
446
|
-
surfaceModel: config.
|
|
454
|
+
surfaceModel: config.triageModel,
|
|
447
455
|
applyDecide: harnessApplyDecider({harness, agentCmd: config.agentCmd}),
|
|
448
|
-
applyModel: config.
|
|
456
|
+
applyModel: config.triageModel,
|
|
449
457
|
observationTriage: config.observationTriage,
|
|
450
458
|
triageGate: harnessTriageGate({harness, agentCmd: config.agentCmd}),
|
|
451
|
-
triageModel: config.
|
|
459
|
+
triageModel: config.triageModel,
|
|
452
460
|
// The ANSWERED-MERGE LAND DISPATCH context (task
|
|
453
461
|
// `apply-rung-merge-disposition`, spec `land-time-reverify-and-parallel-
|
|
454
462
|
// merge-ceiling`): the dispatcher cuts a per-job worktree via
|
|
@@ -546,6 +554,8 @@ interface RunFlags extends ScanFlags {
|
|
|
546
554
|
pr?: boolean;
|
|
547
555
|
agentCmd?: string;
|
|
548
556
|
model?: string;
|
|
557
|
+
/** `--build-model <id>` — the builder-specific model override (falls back to `--model`). */
|
|
558
|
+
buildModel?: string;
|
|
549
559
|
harness?: string;
|
|
550
560
|
piBin?: string;
|
|
551
561
|
sessionsDir?: string;
|
|
@@ -553,6 +563,8 @@ interface RunFlags extends ScanFlags {
|
|
|
553
563
|
review?: boolean;
|
|
554
564
|
reviewModel?: string;
|
|
555
565
|
reviewMaxRounds?: string;
|
|
566
|
+
/** `--triage-model <id>` — the advance lifecycle gates' model (surface, apply, triage). */
|
|
567
|
+
triageModel?: string;
|
|
556
568
|
/** `--fresh-worktree-gate` / `--no-fresh-worktree-gate` — gate the REBASED tip in a clean throwaway worktree (ON by default). */
|
|
557
569
|
freshWorktreeGate?: boolean;
|
|
558
570
|
/** `--merge-retries <n>` — the cross-job merge-serialiser CAS-retry cap (spec `land-time-reverify-and-parallel-merge-ceiling` Story 5 / Applied Answer q1 (a)). */
|
|
@@ -588,6 +600,8 @@ function runFlagOverrides(flags: RunFlags, command?: Commander): PartialConfig {
|
|
|
588
600
|
// flag > env > per-repo > global > default — mirroring the `do` command (the
|
|
589
601
|
// fleet inherits the review gate via the converged `performIntegration` core).
|
|
590
602
|
Object.assign(overrides, reviewFlagOverrides(flags));
|
|
603
|
+
// `--triage-model` (the advance lifecycle gates' model) rides the SAME chain.
|
|
604
|
+
Object.assign(overrides, triageModelFlagOverrides(flags));
|
|
591
605
|
// `--fresh-worktree-gate`/`--no-fresh-worktree-gate` rides the SAME chain: gate
|
|
592
606
|
// the REBASED tip in a clean throwaway worktree (ON by default). The `run` fleet
|
|
593
607
|
// caller additionally downgrades it to today's gate at `perRepoMax > 1` (the
|
|
@@ -764,6 +778,8 @@ interface DoFlags {
|
|
|
764
778
|
allowBacklog?: boolean;
|
|
765
779
|
agentCmd?: string;
|
|
766
780
|
model?: string;
|
|
781
|
+
/** `--build-model <id>` — the builder-specific model override (falls back to `--model`). */
|
|
782
|
+
buildModel?: string;
|
|
767
783
|
harness?: string;
|
|
768
784
|
piBin?: string;
|
|
769
785
|
sessionsDir?: string;
|
|
@@ -771,6 +787,8 @@ interface DoFlags {
|
|
|
771
787
|
review?: boolean;
|
|
772
788
|
reviewModel?: string;
|
|
773
789
|
reviewMaxRounds?: string;
|
|
790
|
+
/** `--triage-model <id>` — the advance lifecycle gates' model (surface, apply, triage). */
|
|
791
|
+
triageModel?: string;
|
|
774
792
|
/** `--tasker-loop` / `--no-tasker-loop` — the tasker improver loop on/off toggle (`do spec:` path). Resolves into the `taskerLoop` config key. */
|
|
775
793
|
taskerLoop?: boolean;
|
|
776
794
|
/** `--tasker-loop-max <n>` — the tasker improver loop's in-context convergence cap (`do spec:` path). Resolves into the `taskerLoopMax` config key. */
|
|
@@ -808,6 +826,8 @@ interface IntakeFlags {
|
|
|
808
826
|
tasksLandIn?: string;
|
|
809
827
|
agentCmd?: string;
|
|
810
828
|
model?: string;
|
|
829
|
+
/** `--intake-model <id>` — the intake decision agent's model (falls back to `--model`). */
|
|
830
|
+
intakeModel?: string;
|
|
811
831
|
harness?: string;
|
|
812
832
|
piBin?: string;
|
|
813
833
|
sessionsDir?: string;
|
|
@@ -822,6 +842,7 @@ interface GcFlags {
|
|
|
822
842
|
ledger?: string;
|
|
823
843
|
remoteBranches?: boolean;
|
|
824
844
|
arbiter?: string;
|
|
845
|
+
allArbiters?: boolean;
|
|
825
846
|
cwd?: string;
|
|
826
847
|
dryRun?: boolean;
|
|
827
848
|
reapStaleLocks?: boolean;
|
|
@@ -1485,6 +1506,10 @@ export function buildProgram(): Command {
|
|
|
1485
1506
|
'--model <id>',
|
|
1486
1507
|
'model the agent runs on (routing intent; auth/keys stay the harness\u2019s job). pi: passed as --model; null/shell: substitutes a {model} placeholder in agentCmd. Resolved flag > env > per-repo > global > default (unset).',
|
|
1487
1508
|
)
|
|
1509
|
+
.option(
|
|
1510
|
+
'--build-model <id>',
|
|
1511
|
+
'model the BUILD agent runs on (the builder-specific override of --model). Unset at every level inherits --model. Resolved flag > env > per-repo > global > fallback to model > default (unset).',
|
|
1512
|
+
)
|
|
1488
1513
|
.option(
|
|
1489
1514
|
'--harness <adapter>',
|
|
1490
1515
|
'harness adapter that launches the agent + reports liveness: null (default, shells out to agentCmd) or pi (the pi CLI)',
|
|
@@ -1510,6 +1535,10 @@ export function buildProgram(): Command {
|
|
|
1510
1535
|
'--review-model <id>',
|
|
1511
1536
|
'model the Gate-2 review agent runs on (de-correlated from the builder; routing intent). Resolved flag > env > per-repo > global > default.',
|
|
1512
1537
|
)
|
|
1538
|
+
.option(
|
|
1539
|
+
'--triage-model <id>',
|
|
1540
|
+
'model the advance lifecycle gates (surface, apply, triage) run on. Unset at every level inherits --model. Resolved flag > env > per-repo > global > fallback to model > default (unset).',
|
|
1541
|
+
)
|
|
1513
1542
|
.option(
|
|
1514
1543
|
'--review-max-rounds <n>',
|
|
1515
1544
|
'bound the revise/review loop; on exhaustion force needs-attention (default 2)',
|
|
@@ -2317,6 +2346,10 @@ export function buildProgram(): Command {
|
|
|
2317
2346
|
'--model <id>',
|
|
2318
2347
|
'model the agent runs on (routing intent; resolved flag > env > per-repo > global > default)',
|
|
2319
2348
|
)
|
|
2349
|
+
.option(
|
|
2350
|
+
'--build-model <id>',
|
|
2351
|
+
'model the BUILD agent runs on (the builder-specific override of --model). Unset at every level inherits --model. Resolved flag > env > per-repo > global > fallback to model > default (unset).',
|
|
2352
|
+
)
|
|
2320
2353
|
.option(
|
|
2321
2354
|
'--harness <adapter>',
|
|
2322
2355
|
'harness adapter that launches the agent: null (default, shells out to agentCmd) or pi (the pi CLI)',
|
|
@@ -2345,6 +2378,10 @@ export function buildProgram(): Command {
|
|
|
2345
2378
|
'--review-model <id>',
|
|
2346
2379
|
'model the Gate-2 review agent runs on (de-correlated from the builder; routing intent). Resolved flag > env > per-repo > global > default.',
|
|
2347
2380
|
)
|
|
2381
|
+
.option(
|
|
2382
|
+
'--triage-model <id>',
|
|
2383
|
+
'model the advance lifecycle gates (surface, apply, triage) run on. Unset at every level inherits --model. Resolved flag > env > per-repo > global > fallback to model > default (unset).',
|
|
2384
|
+
)
|
|
2348
2385
|
.option(
|
|
2349
2386
|
'--review-max-rounds <n>',
|
|
2350
2387
|
'bound the revise/review loop; on exhaustion force needs-attention (default 2)',
|
|
@@ -2614,7 +2651,7 @@ export function buildProgram(): Command {
|
|
|
2614
2651
|
noPR: remoteConfig.noPR,
|
|
2615
2652
|
harness: remoteHarness,
|
|
2616
2653
|
agentCmd: remoteConfig.agentCmd,
|
|
2617
|
-
model: remoteConfig.
|
|
2654
|
+
model: remoteConfig.buildModel,
|
|
2618
2655
|
sessionsDir: remoteConfig.sessionsDir,
|
|
2619
2656
|
review: remoteConfig.review,
|
|
2620
2657
|
reviewModel: remoteConfig.reviewModel,
|
|
@@ -2793,7 +2830,7 @@ export function buildProgram(): Command {
|
|
|
2793
2830
|
noPR: config.noPR,
|
|
2794
2831
|
harness,
|
|
2795
2832
|
agentCmd: config.agentCmd,
|
|
2796
|
-
model: config.
|
|
2833
|
+
model: config.buildModel,
|
|
2797
2834
|
// The HOST-ONLY sessions root (resolved Config → DoOptions bridge, like
|
|
2798
2835
|
// model/agentCmd): the path generator turns it into
|
|
2799
2836
|
// `<sessionsDir>/<id>.jsonl` for `--session`. Without this map the key
|
|
@@ -3126,7 +3163,7 @@ export function buildProgram(): Command {
|
|
|
3126
3163
|
noPR: remoteConfig.noPR,
|
|
3127
3164
|
harness: isoHarness,
|
|
3128
3165
|
agentCmd: remoteConfig.agentCmd,
|
|
3129
|
-
model: remoteConfig.
|
|
3166
|
+
model: remoteConfig.buildModel,
|
|
3130
3167
|
sessionsDir: remoteConfig.sessionsDir,
|
|
3131
3168
|
review: remoteConfig.review,
|
|
3132
3169
|
reviewModel: remoteConfig.reviewModel,
|
|
@@ -3168,18 +3205,18 @@ export function buildProgram(): Command {
|
|
|
3168
3205
|
harness: isoHarness,
|
|
3169
3206
|
agentCmd: remoteConfig.agentCmd,
|
|
3170
3207
|
}),
|
|
3171
|
-
surfaceModel: remoteConfig.
|
|
3208
|
+
surfaceModel: remoteConfig.triageModel,
|
|
3172
3209
|
applyDecide: harnessApplyDecider({
|
|
3173
3210
|
harness: isoHarness,
|
|
3174
3211
|
agentCmd: remoteConfig.agentCmd,
|
|
3175
3212
|
}),
|
|
3176
|
-
applyModel: remoteConfig.
|
|
3213
|
+
applyModel: remoteConfig.triageModel,
|
|
3177
3214
|
observationTriage: remoteConfig.observationTriage,
|
|
3178
3215
|
triageGate: harnessTriageGate({
|
|
3179
3216
|
harness: isoHarness,
|
|
3180
3217
|
agentCmd: remoteConfig.agentCmd,
|
|
3181
3218
|
}),
|
|
3182
|
-
triageModel: remoteConfig.
|
|
3219
|
+
triageModel: remoteConfig.triageModel,
|
|
3183
3220
|
note: (message) => console.error(`>> ${message}`),
|
|
3184
3221
|
env: process.env,
|
|
3185
3222
|
};
|
|
@@ -3270,7 +3307,7 @@ export function buildProgram(): Command {
|
|
|
3270
3307
|
noPR: config.noPR,
|
|
3271
3308
|
harness,
|
|
3272
3309
|
agentCmd: config.agentCmd,
|
|
3273
|
-
model: config.
|
|
3310
|
+
model: config.buildModel,
|
|
3274
3311
|
sessionsDir: config.sessionsDir,
|
|
3275
3312
|
review: config.review,
|
|
3276
3313
|
reviewModel: config.reviewModel,
|
|
@@ -3306,12 +3343,12 @@ export function buildProgram(): Command {
|
|
|
3306
3343
|
arbiter: flags.arbiter ?? config.defaultArbiter,
|
|
3307
3344
|
doOptions,
|
|
3308
3345
|
surfaceGate: harnessSurfaceGate({harness, agentCmd: config.agentCmd}),
|
|
3309
|
-
surfaceModel: config.
|
|
3346
|
+
surfaceModel: config.triageModel,
|
|
3310
3347
|
applyDecide: harnessApplyDecider({harness, agentCmd: config.agentCmd}),
|
|
3311
|
-
applyModel: config.
|
|
3348
|
+
applyModel: config.triageModel,
|
|
3312
3349
|
observationTriage: config.observationTriage,
|
|
3313
3350
|
triageGate: harnessTriageGate({harness, agentCmd: config.agentCmd}),
|
|
3314
|
-
triageModel: config.
|
|
3351
|
+
triageModel: config.triageModel,
|
|
3315
3352
|
note: (message) => console.error(`>> ${message}`),
|
|
3316
3353
|
};
|
|
3317
3354
|
|
|
@@ -3391,7 +3428,7 @@ export function buildProgram(): Command {
|
|
|
3391
3428
|
.command('gc')
|
|
3392
3429
|
.helpGroup(ADVANCED_GROUP)
|
|
3393
3430
|
.description(
|
|
3394
|
-
'Re-apply the provably-safe deletion predicate (ADR \u00a74)
|
|
3431
|
+
'Re-apply the provably-safe deletion predicate (ADR \u00a74) to job worktrees under workspacesDir/work/*: reap the provably-safe ones (clean tree AND branch tip reachable on the arbiter \u2014 merged or pushed) via git worktree remove (+ prune, never rm -rf), and report each RETAINED one with a reason. The catch-up for when end-of-job auto-reap did not run (runner crash/kill). SCOPE: by DEFAULT this is ARBITER-SCOPED \u2014 it acts ONLY on worktrees belonging to the arbiter resolved from the cwd (the same arbiter do --isolated/the mirror uses), so a gc in repo A can never reap repo B\u2019s worktrees. Pass --all-arbiters to sweep across EVERY arbiter (loud + global), or --arbiter <remote-or-url> to target a specific one. --force overrides the predicate (discards un-saved work) and requires --yes \u2014 loud, never default; --all-arbiters --force must be opted into explicitly, never reached by default.',
|
|
3395
3432
|
)
|
|
3396
3433
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
3397
3434
|
.option(
|
|
@@ -3416,8 +3453,12 @@ export function buildProgram(): Command {
|
|
|
3416
3453
|
'SWEEP the arbiter’s remote work/* BRANCHES instead of job worktrees: delete (via git push --delete, NEVER --force) exactly those PROVABLY MERGED into <arbiter>/main (git merge-base --is-ancestor, the SAME predicate the worktree reaper uses), and RETAIN the rest with a reason. An in-flight/un-merged branch (the recovery point) is NEVER touched. Provider-agnostic plain git — works on a --bare arbiter. The merged-only complement of `requeue --reset`.',
|
|
3417
3454
|
)
|
|
3418
3455
|
.option(
|
|
3419
|
-
'--arbiter <remote>',
|
|
3420
|
-
'(
|
|
3456
|
+
'--arbiter <remote-or-url>',
|
|
3457
|
+
'SCOPE the worktree sweep to a SPECIFIC arbiter (a remote NAME resolved from --cwd, or a direct arbiter URL) instead of the cwd\u2019s. (With --remote-branches / --ledger it names the arbiter git remote whose work/* branches / locks to sweep; default there: origin.)',
|
|
3458
|
+
)
|
|
3459
|
+
.option(
|
|
3460
|
+
'--all-arbiters',
|
|
3461
|
+
'GLOBAL, LOUD override: sweep worktrees across EVERY arbiter under workspacesDir/work/* (today\u2019s pre-scoping behaviour), not just the cwd\u2019s. Prints a banner naming every arbiter it will act on before doing anything; combining it with --force still requires --yes. This is the destructive cross-repo path \u2014 opt into it explicitly.',
|
|
3421
3462
|
)
|
|
3422
3463
|
.option(
|
|
3423
3464
|
'--cwd <dir>',
|
|
@@ -3614,6 +3655,77 @@ export function buildProgram(): Command {
|
|
|
3614
3655
|
return;
|
|
3615
3656
|
}
|
|
3616
3657
|
|
|
3658
|
+
// SCOPE RESOLUTION (this task): the worktree reaper is ARBITER-SCOPED by
|
|
3659
|
+
// default. `--all-arbiters` restores the GLOBAL (cross-arbiter) sweep behind
|
|
3660
|
+
// a LOUD banner; `--arbiter <remote-or-url>` targets a specific arbiter; the
|
|
3661
|
+
// bare default resolves the arbiter from the cwd (the SAME resolution
|
|
3662
|
+
// `do --isolated` / the mirror uses). With NO resolvable cwd arbiter and
|
|
3663
|
+
// neither flag, we REFUSE (never silently fall back to global) — gc used to
|
|
3664
|
+
// default to global, and a global `gc --force --yes` from repo A could
|
|
3665
|
+
// irreversibly discard repo B's un-pushed work.
|
|
3666
|
+
let arbiterKey: string | undefined;
|
|
3667
|
+
if (flags.allArbiters !== true) {
|
|
3668
|
+
if (flags.arbiter !== undefined && flags.arbiter.trim() !== '') {
|
|
3669
|
+
// An explicit arbiter: a direct URL (contains `://`, a scp-like `:`,
|
|
3670
|
+
// or an absolute path) is keyed directly; otherwise it is a remote NAME
|
|
3671
|
+
// resolved from the cwd (default `origin`).
|
|
3672
|
+
const spec = flags.arbiter.trim();
|
|
3673
|
+
const looksLikeUrl =
|
|
3674
|
+
spec.includes('://') ||
|
|
3675
|
+
spec.startsWith('/') ||
|
|
3676
|
+
/^[^/]+@/.test(spec);
|
|
3677
|
+
// A remote NAME resolves from --cwd if given (consistent with the
|
|
3678
|
+
// other gc sub-modes), else the process cwd.
|
|
3679
|
+
const resolveFrom = flags.cwd ?? process.cwd();
|
|
3680
|
+
arbiterKey = looksLikeUrl
|
|
3681
|
+
? encodeRepoKey(spec)
|
|
3682
|
+
: resolveArbiterKeyFromCwd(resolveFrom, spec, process.env);
|
|
3683
|
+
if (arbiterKey === undefined) {
|
|
3684
|
+
console.error(
|
|
3685
|
+
`refusing: could not resolve arbiter '${spec}' \u2014 not a URL and ` +
|
|
3686
|
+
'no git remote with that name in the cwd. Pass a remote name that ' +
|
|
3687
|
+
'exists here, an arbiter URL, or --all-arbiters.',
|
|
3688
|
+
);
|
|
3689
|
+
process.exit(1);
|
|
3690
|
+
}
|
|
3691
|
+
} else {
|
|
3692
|
+
arbiterKey = resolveArbiterKeyFromCwd(
|
|
3693
|
+
flags.cwd ?? process.cwd(),
|
|
3694
|
+
config.defaultArbiter,
|
|
3695
|
+
process.env,
|
|
3696
|
+
);
|
|
3697
|
+
if (arbiterKey === undefined) {
|
|
3698
|
+
console.error(
|
|
3699
|
+
'refusing: `gc` is now ARBITER-SCOPED and could not resolve an ' +
|
|
3700
|
+
`arbiter from the current directory (no '${config.defaultArbiter}' ` +
|
|
3701
|
+
'git remote, or not inside a repo). It will NOT fall back to a ' +
|
|
3702
|
+
'global cross-arbiter sweep. Run it from a repo with an arbiter ' +
|
|
3703
|
+
'remote, pass --arbiter <remote-or-url>, or --all-arbiters to sweep ' +
|
|
3704
|
+
'every arbiter.',
|
|
3705
|
+
);
|
|
3706
|
+
process.exit(1);
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
} else {
|
|
3710
|
+
// GLOBAL sweep: LOUD banner naming EVERY arbiter it will act on before
|
|
3711
|
+
// doing anything (this task's "loud" requirement). Global + --force must be
|
|
3712
|
+
// opted into explicitly (the --yes gate below still applies), never reached
|
|
3713
|
+
// by default.
|
|
3714
|
+
const arbiters = enumerateWorkspaceArbiters(workspacesDir, process.env);
|
|
3715
|
+
console.error(
|
|
3716
|
+
'>> --all-arbiters: operating GLOBALLY across ALL arbiters under ' +
|
|
3717
|
+
`${workspacesDir}/work/* (NOT scoped to the current repo).`,
|
|
3718
|
+
);
|
|
3719
|
+
if (arbiters.length > 0) {
|
|
3720
|
+
console.error('>> arbiters in scope:');
|
|
3721
|
+
for (const a of arbiters) {
|
|
3722
|
+
console.error(`>> - ${a.url ?? a.key ?? '(malformed mirror)'}`);
|
|
3723
|
+
}
|
|
3724
|
+
} else {
|
|
3725
|
+
console.error('>> (no worktrees found under any arbiter)');
|
|
3726
|
+
}
|
|
3727
|
+
}
|
|
3728
|
+
|
|
3617
3729
|
// `--force` discards un-saved work, so it is gated behind an explicit
|
|
3618
3730
|
// confirmation (`--yes`) — loud + intentional, NEVER the default (ADR §4).
|
|
3619
3731
|
if (flags.force && !flags.yes) {
|
|
@@ -3633,6 +3745,7 @@ export function buildProgram(): Command {
|
|
|
3633
3745
|
|
|
3634
3746
|
const result = gc({
|
|
3635
3747
|
workspacesDir,
|
|
3748
|
+
arbiterKey,
|
|
3636
3749
|
force: flags.force === true,
|
|
3637
3750
|
note: (message) => console.error(`>> ${message}`),
|
|
3638
3751
|
});
|
|
@@ -4304,6 +4417,10 @@ export function buildProgram(): Command {
|
|
|
4304
4417
|
'--model <id>',
|
|
4305
4418
|
'model the decision agent runs on (routing intent; resolved flag > env > per-repo > global > default)',
|
|
4306
4419
|
)
|
|
4420
|
+
.option(
|
|
4421
|
+
'--intake-model <id>',
|
|
4422
|
+
'model the intake decision agent runs on (the builder-specific override of --model). Unset at every level inherits --model. Resolved flag > env > per-repo > global > fallback to model > default (unset).',
|
|
4423
|
+
)
|
|
4307
4424
|
.option(
|
|
4308
4425
|
'--harness <adapter>',
|
|
4309
4426
|
'harness adapter that launches the decision agent: null (default) or pi',
|
|
@@ -4338,6 +4455,11 @@ export function buildProgram(): Command {
|
|
|
4338
4455
|
...harnessFlagOverrides(flags),
|
|
4339
4456
|
// `--no-pr` (the PR-INTENT axis) rides the SAME chain.
|
|
4340
4457
|
...noPRFlagOverrides(flags),
|
|
4458
|
+
// `--intake-model` rides the SAME chain (falls back to `model`
|
|
4459
|
+
// when unset at all levels via `applyModelFallbacks`).
|
|
4460
|
+
...(flags.intakeModel !== undefined
|
|
4461
|
+
? {intakeModel: flags.intakeModel}
|
|
4462
|
+
: {}),
|
|
4341
4463
|
},
|
|
4342
4464
|
});
|
|
4343
4465
|
if (resolved.message) {
|
|
@@ -4452,7 +4574,7 @@ export function buildProgram(): Command {
|
|
|
4452
4574
|
explicitTasksLandIn,
|
|
4453
4575
|
harness,
|
|
4454
4576
|
agentCmd: config.agentCmd,
|
|
4455
|
-
model: config.
|
|
4577
|
+
model: config.intakeModel,
|
|
4456
4578
|
sessionsDir: config.sessionsDir,
|
|
4457
4579
|
// Host-only runner IDENTITY — scopes intake's `gh`/git ops (not the
|
|
4458
4580
|
// decision/review AGENT launches); absent ⇒ ambient.
|
package/src/config.ts
CHANGED
|
@@ -528,9 +528,25 @@ export interface Config {
|
|
|
528
528
|
* (`LaunchInput.model`); the ADAPTER decides HOW it reaches its tool — the pi
|
|
529
529
|
* adapter passes `--model <model>` natively, the null/shell adapter substitutes
|
|
530
530
|
* a `{model}` placeholder in `agentCmd`. Resolved per-repo like `integration`:
|
|
531
|
-
* flag (`--model`) > env > per-repo > global > default (unset).
|
|
531
|
+
* flag (`--model`) > env > per-repo > global > default (unset). The fallback
|
|
532
|
+
* to this field from `buildModel` / `reviewModel` / `taskerLoopModel` is
|
|
533
|
+
* applied AFTER the full precedence chain resolves (in `applyModelFallbacks`),
|
|
534
|
+
* so a per-role knob set at ANY level (even global config) is NEVER reset by a
|
|
535
|
+
* higher-precedence `--model`.
|
|
532
536
|
*/
|
|
533
537
|
model?: string;
|
|
538
|
+
/**
|
|
539
|
+
* The model the BUILD agent runs on (the builder-specific override of the
|
|
540
|
+
* general `model`). Optional with NO default so "unset" means "inherit the
|
|
541
|
+
* general `model`" (via `applyModelFallbacks`, applied at the resolution FINAL
|
|
542
|
+
* point — after the full flag > env > per-repo > global chain). Carried to the
|
|
543
|
+
* build launch through the same harness seam (`LaunchInput.model`). Resolved
|
|
544
|
+
* per-repo like `model`: flag (`--build-model`) > env (`DORFL_BUILD_MODEL`) >
|
|
545
|
+
* per-repo > global > fallback to `model` > default (unset). DISTINCT from
|
|
546
|
+
* `reviewModel` (Gate 2) and `taskerLoopModel` (the improver loop) — each has
|
|
547
|
+
* its OWN knob and each falls back to `model` independently.
|
|
548
|
+
*/
|
|
549
|
+
buildModel?: string;
|
|
534
550
|
/**
|
|
535
551
|
* Which harness adapter launches + reports liveness for a job's agent (the
|
|
536
552
|
* harness seam, ADR §5): `null` (default — shells out to `agentCmd`,
|
|
@@ -600,11 +616,14 @@ export interface Config {
|
|
|
600
616
|
review: boolean;
|
|
601
617
|
/**
|
|
602
618
|
* The model the REVIEW agent runs on (de-correlation from the builder's
|
|
603
|
-
* `
|
|
604
|
-
*
|
|
605
|
-
*
|
|
606
|
-
*
|
|
607
|
-
*
|
|
619
|
+
* `buildModel` when explicitly set). Optional with NO default so "unset" means
|
|
620
|
+
* "inherit the general `model`" (via `applyModelFallbacks`, applied at the
|
|
621
|
+
* resolution FINAL point — after the full flag > env > per-repo > global chain).
|
|
622
|
+
* Carried to the review-agent launch through the EXISTING harness seam
|
|
623
|
+
* (`LaunchInput.model` / `substituteModel`) — NOT a new mechanism. Resolved like
|
|
624
|
+
* `model`: flag (`--review-model`) > env > per-repo > global > fallback to
|
|
625
|
+
* `model` > default (unset). Distinct from the builder's `buildModel` — each
|
|
626
|
+
* has its OWN knob and each falls back to `model` independently.
|
|
608
627
|
*/
|
|
609
628
|
reviewModel?: string;
|
|
610
629
|
/**
|
|
@@ -650,14 +669,44 @@ export interface Config {
|
|
|
650
669
|
taskerLoopMax: number;
|
|
651
670
|
/**
|
|
652
671
|
* The model the tasker IMPROVER loop's review agent runs on (de-correlation
|
|
653
|
-
* from the tasker). Optional with NO default so "unset"
|
|
654
|
-
*
|
|
655
|
-
*
|
|
656
|
-
*
|
|
657
|
-
* (
|
|
658
|
-
* `--
|
|
672
|
+
* from the tasker when explicitly set). Optional with NO default so "unset"
|
|
673
|
+
* means "inherit the general `model`" (via `applyModelFallbacks`, applied at
|
|
674
|
+
* the resolution FINAL point — after the full flag > env > per-repo > global
|
|
675
|
+
* chain). Carried to the review-agent launch through the EXISTING harness seam
|
|
676
|
+
* (`LaunchInput.model` / `substituteModel`). Resolved like `model`: flag
|
|
677
|
+
* (`--tasker-loop-model`) > env > per-repo > global > fallback to `model` >
|
|
678
|
+
* default (unset). DISTINCT from the acceptance gate's `reviewModel` (build
|
|
679
|
+
* `--review-model`) and from `buildModel` — each has its OWN knob and each falls
|
|
680
|
+
* back to `model` independently.
|
|
659
681
|
*/
|
|
660
682
|
taskerLoopModel?: string;
|
|
683
|
+
/**
|
|
684
|
+
* The model the ADVANCE LIFECYCLE GATES (surface, apply, triage) run on —
|
|
685
|
+
* the fresh-context judgement agents that surface questions from blocked work
|
|
686
|
+
* (`surface`), interpret answered questions and decide what to mint (`apply`),
|
|
687
|
+
* and auto-disposition no-question observations (`triage`). Optional with NO
|
|
688
|
+
* default so "unset" means "inherit the general `model`" (via
|
|
689
|
+
* `applyModelFallbacks`, applied at the resolution FINAL point). Carried to each
|
|
690
|
+
* gate's launch through the EXISTING harness seam (`LaunchInput.model`). Resolved
|
|
691
|
+
* like `model`: flag (`--triage-model`) > env (`DORFL_TRIAGE_MODEL`) > per-repo >
|
|
692
|
+
* global > fallback to `model` > default (unset). DISTINCT from `reviewModel`,
|
|
693
|
+
* `taskerLoopModel`, `buildModel`, and `intakeModel` — each has its OWN knob and
|
|
694
|
+
* each falls back to `model` independently.
|
|
695
|
+
*/
|
|
696
|
+
triageModel?: string;
|
|
697
|
+
/**
|
|
698
|
+
* The model the INTAKE decision agent runs on — the front-door fresh-context
|
|
699
|
+
* agent that reads an issue/PR and decides whether to mint a task, mint a spec,
|
|
700
|
+
* or ask a follow-up question. Optional with NO default so "unset" means
|
|
701
|
+
* "inherit the general `model`" (via `applyModelFallbacks`, applied at the
|
|
702
|
+
* resolution FINAL point). Carried to the intake-agent launch through the
|
|
703
|
+
* EXISTING harness seam (`LaunchInput.model`). Resolved like `model`: flag
|
|
704
|
+
* (`--intake-model`) > env (`DORFL_INTAKE_MODEL`) > per-repo > global > fallback
|
|
705
|
+
* to `model` > default (unset). DISTINCT from the advance lifecycle gates'
|
|
706
|
+
* `triageModel` and from `buildModel`/`reviewModel`/`taskerLoopModel` — each has
|
|
707
|
+
* its OWN knob and each falls back to `model` independently.
|
|
708
|
+
*/
|
|
709
|
+
intakeModel?: string;
|
|
661
710
|
/**
|
|
662
711
|
* **The fresh-worktree acceptance-gate toggle** (`--fresh-worktree-gate` /
|
|
663
712
|
* `--no-fresh-worktree-gate`). When ON (the default), the acceptance gate
|
|
@@ -1078,6 +1127,35 @@ export function mergeConfig(overrides: PartialConfig): Config {
|
|
|
1078
1127
|
return merged;
|
|
1079
1128
|
}
|
|
1080
1129
|
|
|
1130
|
+
/**
|
|
1131
|
+
* Apply the per-role model fallbacks IN PLACE on a FULLY RESOLVED {@link Config}:
|
|
1132
|
+
* `buildModel`, `reviewModel`, and `taskerLoopModel` each inherit the general
|
|
1133
|
+
* `model` when they are unset (i.e. were not set at ANY precedence level — flag,
|
|
1134
|
+
* env, per-repo, or global). This is the single place the fallback lives; it is
|
|
1135
|
+
* called at the resolution FINAL points (`resolveRepoConfigFromLoaded` for the
|
|
1136
|
+
* per-repo chain; `resolveGlobalConfig` for the global-only chain) so the
|
|
1137
|
+
* fallback uses the FINAL resolved `model`, never an intermediate layer's. A
|
|
1138
|
+
* per-role knob set at ANY level is NEVER reset — `applyModelFallbacks` only
|
|
1139
|
+
* touches fields that are still `undefined` after the full chain resolved.
|
|
1140
|
+
*/
|
|
1141
|
+
export function applyModelFallbacks(config: Config): void {
|
|
1142
|
+
if (config.buildModel === undefined) {
|
|
1143
|
+
config.buildModel = config.model;
|
|
1144
|
+
}
|
|
1145
|
+
if (config.reviewModel === undefined) {
|
|
1146
|
+
config.reviewModel = config.model;
|
|
1147
|
+
}
|
|
1148
|
+
if (config.taskerLoopModel === undefined) {
|
|
1149
|
+
config.taskerLoopModel = config.model;
|
|
1150
|
+
}
|
|
1151
|
+
if (config.triageModel === undefined) {
|
|
1152
|
+
config.triageModel = config.model;
|
|
1153
|
+
}
|
|
1154
|
+
if (config.intakeModel === undefined) {
|
|
1155
|
+
config.intakeModel = config.model;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1081
1159
|
/**
|
|
1082
1160
|
* Persist `config` to `path` as pretty JSON (creating the parent dir). Used by
|
|
1083
1161
|
* `work-on` to SAVE the prompted `humanWorktreesDir` on first use so the human is
|
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',
|