dorfl 0.1.2 → 0.2.0
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/advance-drivers.d.ts +1 -1
- package/dist/advance-drivers.d.ts.map +1 -1
- package/dist/advance-drivers.js +15 -3
- package/dist/advance-drivers.js.map +1 -1
- package/dist/advance-lifecycle-template.d.ts.map +1 -1
- package/dist/advance-lifecycle-template.js +76 -4
- package/dist/advance-lifecycle-template.js.map +1 -1
- package/dist/advance-treeless-publish.d.ts +23 -0
- package/dist/advance-treeless-publish.d.ts.map +1 -1
- package/dist/advance-treeless-publish.js +41 -0
- package/dist/advance-treeless-publish.js.map +1 -1
- package/dist/advance.d.ts +59 -7
- package/dist/advance.d.ts.map +1 -1
- package/dist/advance.js +389 -82
- package/dist/advance.js.map +1 -1
- package/dist/advancing-lock.d.ts +30 -2
- package/dist/advancing-lock.d.ts.map +1 -1
- package/dist/advancing-lock.js +50 -0
- package/dist/advancing-lock.js.map +1 -1
- package/dist/agent-launch.d.ts +12 -0
- package/dist/agent-launch.d.ts.map +1 -1
- package/dist/agent-launch.js +22 -12
- package/dist/agent-launch.js.map +1 -1
- package/dist/agent-stop.d.ts +40 -2
- package/dist/agent-stop.d.ts.map +1 -1
- package/dist/agent-stop.js +30 -2
- package/dist/agent-stop.js.map +1 -1
- package/dist/apply-decide.d.ts +17 -3
- package/dist/apply-decide.d.ts.map +1 -1
- package/dist/apply-decide.js +36 -7
- package/dist/apply-decide.js.map +1 -1
- package/dist/apply-merge-action.d.ts +17 -5
- package/dist/apply-merge-action.d.ts.map +1 -1
- package/dist/apply-merge-action.js +43 -8
- package/dist/apply-merge-action.js.map +1 -1
- package/dist/apply-persist.d.ts +57 -26
- package/dist/apply-persist.d.ts.map +1 -1
- package/dist/apply-persist.js +166 -37
- package/dist/apply-persist.js.map +1 -1
- package/dist/apply-stuck-action.d.ts +151 -0
- package/dist/apply-stuck-action.d.ts.map +1 -0
- package/dist/apply-stuck-action.js +125 -0
- package/dist/apply-stuck-action.js.map +1 -0
- package/dist/brand.d.ts +12 -1
- package/dist/brand.d.ts.map +1 -1
- package/dist/brand.js +2 -1
- package/dist/brand.js.map +1 -1
- package/dist/cli-spinner.d.ts +1 -1
- package/dist/cli-spinner.js +1 -1
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +320 -63
- package/dist/cli.js.map +1 -1
- package/dist/close-job-template.js +1 -1
- package/dist/complete.d.ts +6 -3
- package/dist/complete.d.ts.map +1 -1
- package/dist/complete.js +91 -37
- package/dist/complete.js.map +1 -1
- package/dist/config.d.ts +55 -9
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +56 -4
- package/dist/config.js.map +1 -1
- package/dist/continue-branch.d.ts.map +1 -1
- package/dist/continue-branch.js +22 -0
- package/dist/continue-branch.js.map +1 -1
- package/dist/decision-engine.d.ts +38 -10
- package/dist/decision-engine.d.ts.map +1 -1
- package/dist/decision-engine.js +10 -6
- package/dist/decision-engine.js.map +1 -1
- package/dist/do-autopick.d.ts.map +1 -1
- package/dist/do-autopick.js +11 -1
- package/dist/do-autopick.js.map +1 -1
- package/dist/do-config.d.ts +1 -1
- package/dist/do.d.ts +49 -56
- package/dist/do.d.ts.map +1 -1
- package/dist/do.js +419 -30
- package/dist/do.js.map +1 -1
- package/dist/env-config.d.ts.map +1 -1
- package/dist/env-config.js +12 -4
- package/dist/env-config.js.map +1 -1
- package/dist/failure-cause.d.ts +3 -2
- package/dist/failure-cause.d.ts.map +1 -1
- package/dist/failure-cause.js +28 -1
- package/dist/failure-cause.js.map +1 -1
- package/dist/format.d.ts +6 -6
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +10 -27
- package/dist/format.js.map +1 -1
- package/dist/frontmatter.d.ts +16 -4
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +27 -1
- package/dist/frontmatter.js.map +1 -1
- package/dist/gc.d.ts +65 -6
- package/dist/gc.d.ts.map +1 -1
- package/dist/gc.js +126 -9
- package/dist/gc.js.map +1 -1
- package/dist/github.d.ts +14 -0
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +73 -0
- package/dist/github.js.map +1 -1
- package/dist/harness.d.ts +25 -0
- package/dist/harness.d.ts.map +1 -1
- package/dist/harness.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/install-ci-branch-protection.d.ts +132 -38
- package/dist/install-ci-branch-protection.d.ts.map +1 -1
- package/dist/install-ci-branch-protection.js +190 -48
- package/dist/install-ci-branch-protection.js.map +1 -1
- package/dist/install-ci-core.d.ts +21 -1
- package/dist/install-ci-core.d.ts.map +1 -1
- package/dist/install-ci-core.js +10 -3
- package/dist/install-ci-core.js.map +1 -1
- package/dist/install-ci-github.d.ts +32 -0
- package/dist/install-ci-github.d.ts.map +1 -1
- package/dist/install-ci-github.js +73 -0
- package/dist/install-ci-github.js.map +1 -1
- package/dist/install-skills.d.ts +123 -0
- package/dist/install-skills.d.ts.map +1 -0
- package/dist/install-skills.js +100 -0
- package/dist/install-skills.js.map +1 -0
- package/dist/intake-trigger-template.js +1 -1
- package/dist/intake.d.ts +17 -4
- package/dist/intake.d.ts.map +1 -1
- package/dist/intake.js +23 -7
- package/dist/intake.js.map +1 -1
- package/dist/integration-core.d.ts +10 -7
- package/dist/integration-core.d.ts.map +1 -1
- package/dist/integration-core.js +174 -60
- package/dist/integration-core.js.map +1 -1
- package/dist/integrator.d.ts +1 -1
- package/dist/integrator.d.ts.map +1 -1
- package/dist/integrator.js +19 -3
- package/dist/integrator.js.map +1 -1
- package/dist/isolation.d.ts +2 -2
- package/dist/isolation.d.ts.map +1 -1
- package/dist/isolation.js +9 -0
- package/dist/isolation.js.map +1 -1
- package/dist/item-lock.d.ts +196 -129
- package/dist/item-lock.d.ts.map +1 -1
- package/dist/item-lock.js +329 -254
- package/dist/item-lock.js.map +1 -1
- package/dist/ledger-write.d.ts +31 -26
- package/dist/ledger-write.d.ts.map +1 -1
- package/dist/ledger-write.js +100 -122
- package/dist/ledger-write.js.map +1 -1
- package/dist/lifecycle-gather.d.ts +17 -0
- package/dist/lifecycle-gather.d.ts.map +1 -1
- package/dist/lifecycle-gather.js +7 -1
- package/dist/lifecycle-gather.js.map +1 -1
- package/dist/lifecycle-pools.d.ts +40 -3
- package/dist/lifecycle-pools.d.ts.map +1 -1
- package/dist/lifecycle-pools.js +26 -6
- package/dist/lifecycle-pools.js.map +1 -1
- package/dist/merge-question-surfacer.d.ts +15 -1
- package/dist/merge-question-surfacer.d.ts.map +1 -1
- package/dist/merge-question-surfacer.js +18 -3
- package/dist/merge-question-surfacer.js.map +1 -1
- package/dist/migrate-stuck-locks.d.ts +129 -0
- package/dist/migrate-stuck-locks.d.ts.map +1 -0
- package/dist/migrate-stuck-locks.js +355 -0
- package/dist/migrate-stuck-locks.js.map +1 -0
- package/dist/needs-attention.d.ts +293 -16
- package/dist/needs-attention.d.ts.map +1 -1
- package/dist/needs-attention.js +532 -57
- package/dist/needs-attention.js.map +1 -1
- package/dist/orphan-sidecar.d.ts +8 -4
- package/dist/orphan-sidecar.d.ts.map +1 -1
- package/dist/orphan-sidecar.js +35 -2
- package/dist/orphan-sidecar.js.map +1 -1
- package/dist/pi-harness.d.ts +16 -0
- package/dist/pi-harness.d.ts.map +1 -1
- package/dist/pi-harness.js +82 -2
- package/dist/pi-harness.js.map +1 -1
- package/dist/placement.d.ts +3 -3
- package/dist/placement.js +1 -1
- package/dist/prd-to-spec.d.ts.map +1 -1
- package/dist/prd-to-spec.js +9 -5
- package/dist/prd-to-spec.js.map +1 -1
- package/dist/protocol/CLAIM-PROTOCOL.md +17 -10
- package/dist/protocol/REVIEW-PROTOCOL.md +4 -1
- package/dist/protocol/SURFACE-PROTOCOL.md +16 -2
- package/dist/protocol/TASKING-PROTOCOL.md +3 -1
- package/dist/protocol/WORK-CONTRACT.md +22 -18
- package/dist/protocol/task-template.md +1 -1
- package/dist/reap-branches.d.ts +12 -9
- package/dist/reap-branches.d.ts.map +1 -1
- package/dist/reap-branches.js +25 -7
- package/dist/reap-branches.js.map +1 -1
- package/dist/recover-isolated.d.ts +12 -0
- package/dist/recover-isolated.d.ts.map +1 -1
- package/dist/recover-isolated.js +5 -0
- package/dist/recover-isolated.js.map +1 -1
- package/dist/repo-config.d.ts +23 -2
- package/dist/repo-config.d.ts.map +1 -1
- package/dist/repo-config.js +53 -7
- package/dist/repo-config.js.map +1 -1
- package/dist/repo-mirror.d.ts.map +1 -1
- package/dist/repo-mirror.js +18 -2
- package/dist/repo-mirror.js.map +1 -1
- package/dist/review-verdict.js +1 -1
- package/dist/review-verdict.js.map +1 -1
- package/dist/run.d.ts +1 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +54 -19
- package/dist/run.js.map +1 -1
- package/dist/scan.d.ts +17 -8
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +22 -8
- package/dist/scan.js.map +1 -1
- package/dist/select-priority.d.ts +2 -2
- package/dist/select-priority.js +1 -1
- package/dist/select-priority.js.map +1 -1
- package/dist/sidecar-apply.js +1 -1
- package/dist/sidecar-apply.js.map +1 -1
- package/dist/sidecar.d.ts +45 -10
- package/dist/sidecar.d.ts.map +1 -1
- package/dist/sidecar.js +82 -2
- package/dist/sidecar.js.map +1 -1
- package/dist/skills/answer-questions/SKILL.md +89 -0
- package/dist/skills/capture-signal/SKILL.md +52 -0
- package/dist/skills/convert-from-prd-to-spec/SKILL.md +90 -0
- package/dist/skills/drive-tasks/SKILL.md +218 -0
- package/dist/skills/from-idea/SKILL.md +83 -0
- package/dist/skills/merge-prs/SKILL.md +70 -0
- package/dist/skills/orchestrate/SKILL.md +101 -0
- package/dist/skills/promote/SKILL.md +35 -0
- package/dist/skills/review/SKILL.md +16 -0
- package/dist/skills/setup/SKILL.md +258 -0
- package/dist/skills/setup/protocol/ADR-FORMAT.md +47 -0
- package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +224 -0
- package/dist/skills/setup/protocol/REVIEW-PROTOCOL.md +122 -0
- package/dist/skills/setup/protocol/SURFACE-PROTOCOL.md +135 -0
- package/dist/skills/setup/protocol/TASKING-PROTOCOL.md +124 -0
- package/dist/skills/setup/protocol/WORK-CONTRACT.md +280 -0
- package/dist/skills/setup/protocol/spec-template.md +71 -0
- package/dist/skills/setup/protocol/task-template.md +65 -0
- package/dist/skills/surface-questions/SKILL.md +16 -0
- package/dist/skills/to-spec/SKILL.md +34 -0
- package/dist/skills/to-task/SKILL.md +19 -0
- package/dist/skills/triage-observations/SKILL.md +78 -0
- package/dist/skills/work/SKILL.md +51 -0
- package/dist/slug-namespace.d.ts +5 -5
- package/dist/slug-namespace.js +5 -5
- package/dist/slug-namespace.js.map +1 -1
- package/dist/spec-complete.d.ts.map +1 -1
- package/dist/spec-complete.js +2 -3
- package/dist/spec-complete.js.map +1 -1
- package/dist/start.d.ts.map +1 -1
- package/dist/start.js +51 -57
- package/dist/start.js.map +1 -1
- package/dist/surface-gate.d.ts.map +1 -1
- package/dist/surface-gate.js +10 -3
- package/dist/surface-gate.js.map +1 -1
- package/dist/surface-persist.js +1 -1
- package/dist/surface-persist.js.map +1 -1
- package/dist/tasker-review-loop.d.ts +4 -3
- package/dist/tasker-review-loop.d.ts.map +1 -1
- package/dist/tasker-review-loop.js.map +1 -1
- package/dist/tasking-lock.d.ts +2 -2
- package/dist/tasking-lock.d.ts.map +1 -1
- package/dist/tasking-lock.js +53 -32
- package/dist/tasking-lock.js.map +1 -1
- package/dist/tasking.d.ts +40 -6
- package/dist/tasking.d.ts.map +1 -1
- package/dist/tasking.js +224 -12
- package/dist/tasking.js.map +1 -1
- package/dist/triage-persist.d.ts +10 -6
- package/dist/triage-persist.d.ts.map +1 -1
- package/dist/triage-persist.js +34 -4
- package/dist/triage-persist.js.map +1 -1
- package/dist/vendor/incur/agents.d.ts +58 -0
- package/dist/vendor/incur/agents.d.ts.map +1 -0
- package/dist/vendor/incur/agents.js +343 -0
- package/dist/vendor/incur/agents.js.map +1 -0
- package/dist/verify-workflow-template.js +1 -1
- package/dist/watch-session.d.ts +11 -3
- package/dist/watch-session.d.ts.map +1 -1
- package/dist/watch-session.js +94 -7
- package/dist/watch-session.js.map +1 -1
- package/dist/work-layout.d.ts +7 -3
- package/dist/work-layout.d.ts.map +1 -1
- package/dist/work-layout.js +6 -3
- package/dist/work-layout.js.map +1 -1
- package/dist/workspace.d.ts +1 -1
- package/package.json +2 -2
- package/src/advance-drivers.ts +16 -3
- package/src/advance-lifecycle-template.ts +89 -4
- package/src/advance-treeless-publish.ts +45 -0
- package/src/advance.ts +467 -87
- package/src/advancing-lock.ts +96 -1
- package/src/agent-launch.ts +37 -12
- package/src/agent-stop.ts +60 -2
- package/src/apply-decide.ts +36 -7
- package/src/apply-merge-action.ts +45 -7
- package/src/apply-persist.ts +228 -54
- package/src/apply-stuck-action.ts +260 -0
- package/src/brand.ts +14 -2
- package/src/cli-spinner.ts +1 -1
- package/src/cli.ts +401 -70
- package/src/close-job-template.ts +1 -1
- package/src/complete.ts +110 -52
- package/src/config.ts +107 -13
- package/src/continue-branch.ts +22 -0
- package/src/decision-engine.ts +55 -17
- package/src/do-autopick.ts +12 -1
- package/src/do-config.ts +1 -1
- package/src/do.ts +511 -38
- package/src/env-config.ts +12 -4
- package/src/failure-cause.ts +30 -1
- package/src/format.ts +10 -30
- package/src/frontmatter.ts +35 -5
- package/src/gc.ts +172 -9
- package/src/github.ts +78 -0
- package/src/harness.ts +25 -0
- package/src/index.ts +10 -0
- package/src/install-ci-branch-protection.ts +282 -57
- package/src/install-ci-core.ts +30 -3
- package/src/install-ci-github.ts +86 -0
- package/src/install-skills.ts +166 -0
- package/src/intake-trigger-template.ts +1 -1
- package/src/intake.ts +46 -11
- package/src/integration-core.ts +199 -65
- package/src/integrator.ts +22 -9
- package/src/isolation.ts +11 -2
- package/src/item-lock.ts +485 -341
- package/src/ledger-write.ts +135 -150
- package/src/lifecycle-gather.ts +29 -6
- package/src/lifecycle-pools.ts +67 -9
- package/src/merge-question-surfacer.ts +28 -4
- package/src/migrate-stuck-locks.ts +451 -0
- package/src/needs-attention.ts +857 -84
- package/src/orphan-sidecar.ts +47 -6
- package/src/pi-harness.ts +82 -2
- package/src/placement.ts +3 -3
- package/src/prd-to-spec.ts +13 -5
- package/src/reap-branches.ts +38 -14
- package/src/recover-isolated.ts +17 -0
- package/src/repo-config.ts +55 -6
- package/src/repo-mirror.ts +23 -1
- package/src/review-verdict.ts +1 -1
- package/src/run.ts +64 -18
- package/src/scan.ts +22 -8
- package/src/select-priority.ts +3 -3
- package/src/sidecar-apply.ts +1 -1
- package/src/sidecar.ts +138 -11
- package/src/slug-namespace.ts +7 -7
- package/src/spec-complete.ts +2 -3
- package/src/start.ts +53 -59
- package/src/surface-gate.ts +10 -3
- package/src/surface-persist.ts +1 -1
- package/src/tasker-review-loop.ts +4 -3
- package/src/tasking-lock.ts +52 -36
- package/src/tasking.ts +261 -17
- package/src/triage-persist.ts +56 -12
- package/src/vendor/incur/LICENSE +21 -0
- package/src/vendor/incur/README.md +19 -0
- package/src/vendor/incur/agents.ts +392 -0
- package/src/verify-workflow-template.ts +1 -1
- package/src/watch-session.ts +102 -7
- package/src/work-layout.ts +6 -3
- package/src/workspace.ts +1 -1
package/src/complete.ts
CHANGED
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
import {ledgerWrite} from './ledger-write.js';
|
|
14
14
|
import {releaseItemLock} from './item-lock.js';
|
|
15
15
|
import {workBranchRef, parseWorkBranchRef} from './slug-namespace.js';
|
|
16
|
+
import {isProvablyMergedForReap} from './gc.js';
|
|
17
|
+
import type {SidecarType} from './sidecar.js';
|
|
16
18
|
import type {ReviewProvider} from './integrator.js';
|
|
17
19
|
import type {IntegrationMode} from './config.js';
|
|
18
20
|
import {runAsync, localMainAheadCount, type RunResult} from './git.js';
|
|
@@ -315,9 +317,12 @@ export interface CompleteResult {
|
|
|
315
317
|
outcome: CompleteOutcome;
|
|
316
318
|
/**
|
|
317
319
|
* True iff a FAILURE outcome (gate-failed / rebase-conflict) was routed to
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
320
|
+
* the shared needs-attention mechanism (ADR §12) — post lock-cutover, the
|
|
321
|
+
* per-item lock is amended to `state: stuck` with the reason recorded on the
|
|
322
|
+
* lock entry (there is no `work/needs-attention/` folder write anymore),
|
|
323
|
+
* rather than the item being left dangling. Undefined/false on the success,
|
|
324
|
+
* `--skip-verify`, refused, and usage-error paths (none of which mark the
|
|
325
|
+
* item stuck).
|
|
321
326
|
*/
|
|
322
327
|
routedToNeedsAttention?: boolean;
|
|
323
328
|
/** The work branch that was completed, when one was resolved. */
|
|
@@ -489,13 +494,11 @@ function strandRefusalSlug(err: unknown): string | undefined {
|
|
|
489
494
|
}
|
|
490
495
|
|
|
491
496
|
/**
|
|
492
|
-
* Surface an autonomous-path strand refusal
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
* build the one-file `work/<src>/<slug>.md → work/needs-attention/<slug>.md`
|
|
498
|
-
* move on a scratch index, and CAS-publish it. It NEVER touches the caller's
|
|
497
|
+
* Surface an autonomous-path strand refusal via the TREE-LESS seam ({@link
|
|
498
|
+
* ledgerWrite.applyTreelessNeedsAttentionTransition}). Post lock-cutover the
|
|
499
|
+
* seam is a pure LOCK AMEND — the item's per-item lock is moved to
|
|
500
|
+
* `state: stuck` with the reason on the lock entry; no folder move, no
|
|
501
|
+
* `work/needs-attention/` write. It NEVER touches the caller's
|
|
499
502
|
* working tree / HEAD / index — important here because the refusal was raised
|
|
500
503
|
* precisely because the working tree does NOT hold an `.md` to `git mv`. It is
|
|
501
504
|
* idempotent (a re-surface of an already-surfaced slug is a no-op) and reports
|
|
@@ -523,11 +526,13 @@ async function surfaceAutonomousStrand(params: {
|
|
|
523
526
|
});
|
|
524
527
|
if (surfaced.moved) {
|
|
525
528
|
const message =
|
|
526
|
-
`'${slug}' refused (${reason}); surfaced
|
|
527
|
-
|
|
529
|
+
`'${slug}' refused (${reason}); surfaced on ${arbiter}/main as a stuck ` +
|
|
530
|
+
'question (needsAnswers) so the next autonomous tick does NOT re-claim it.';
|
|
528
531
|
note(message);
|
|
532
|
+
// PR-2b D3: a clean surface is GREEN (exitCode 0). The autonomous strand
|
|
533
|
+
// surface is one of the cleanly-surfaced bounce sites this policy applies to.
|
|
529
534
|
return {
|
|
530
|
-
exitCode:
|
|
535
|
+
exitCode: 0,
|
|
531
536
|
outcome: 'strand-surfaced',
|
|
532
537
|
routedToNeedsAttention: true,
|
|
533
538
|
message,
|
|
@@ -668,12 +673,14 @@ async function runComplete(
|
|
|
668
673
|
// removes it from THERE, so the move can never disagree with what the arbiter
|
|
669
674
|
// holds even when the local tree diverges (ledger-integrity defect 1). The
|
|
670
675
|
// `source` we pass is the arbiter-content fallback for the degenerate "arbiter
|
|
671
|
-
// holds nothing" case, not the authority. `in-progress/`
|
|
672
|
-
//
|
|
673
|
-
//
|
|
676
|
+
// holds nothing" case, not the authority. The legacy `in-progress/` FOLDER PROBE
|
|
677
|
+
// is GONE (task `finish-in-progress-folder-cutover-remove-legacy-recovery-readers`):
|
|
678
|
+
// the per-item-lock cutover moved claim OFF writing `work/in-progress/` (the body
|
|
679
|
+
// rests in `tasks/backlog/`, liveness is the held lock), so nothing writes that
|
|
680
|
+
// folder anymore and the `existsSync(inProgress)` arm could not fire. Liveness is
|
|
681
|
+
// the lock, not the folder.
|
|
674
682
|
const backlog = workItemPath(cwd, 'tasks-ready', slug);
|
|
675
683
|
const staged = workItemPath(cwd, 'tasks-backlog', slug);
|
|
676
|
-
const inProgress = workItemPath(cwd, 'in-progress', slug);
|
|
677
684
|
const done = workItemPath(cwd, 'done', slug);
|
|
678
685
|
const onBacklog = existsSync(backlog);
|
|
679
686
|
// `--allow-backlog` drive (spec
|
|
@@ -686,7 +693,6 @@ async function runComplete(
|
|
|
686
693
|
// strand `complete` must REFUSE (the honest "not finished, not done" position),
|
|
687
694
|
// NOT silently integrate — only the deliberate drive promotes from staging.
|
|
688
695
|
const onPreBacklog = options.allowBacklog === true && existsSync(staged);
|
|
689
|
-
const onInProgress = existsSync(inProgress);
|
|
690
696
|
// SELF-RENAMING FOLDER task backstop: the binary-known `work/done/<slug>.md`,
|
|
691
697
|
// OR — when none of the binary-known ledger folders hold the record — the slug
|
|
692
698
|
// at a RENAMED done-position the migration agent placed it in (e.g.
|
|
@@ -695,13 +701,10 @@ async function runComplete(
|
|
|
695
701
|
// resolution is byte-for-byte unchanged.
|
|
696
702
|
const onDone =
|
|
697
703
|
existsSync(done) ||
|
|
698
|
-
(!onBacklog &&
|
|
699
|
-
!onPreBacklog &&
|
|
700
|
-
!onInProgress &&
|
|
701
|
-
recordAtRenamedDonePosition(cwd, slug));
|
|
704
|
+
(!onBacklog && !onPreBacklog && recordAtRenamedDonePosition(cwd, slug));
|
|
702
705
|
// STRANDED-DONE AUTO-RECOVER (spec `ledger-integrity` story 6, the autonomous
|
|
703
|
-
// half of `finish-already-committed-branch`). When
|
|
704
|
-
//
|
|
706
|
+
// half of `finish-already-committed-branch`). When the pool/staging does NOT
|
|
707
|
+
// hold the slug on the BRANCH tree BUT done/ does, the work
|
|
705
708
|
// branch was already built + done-moved + committed by a prior run that never
|
|
706
709
|
// landed on the arbiter (a terminal push failed, or the PR never merged). The
|
|
707
710
|
// autonomous `do`/`advance`/plain-`complete` path used to refuse this with
|
|
@@ -713,8 +716,7 @@ async function runComplete(
|
|
|
713
716
|
// `<arbiter>/main` returns `already-integrated` (clean no-op, NEVER a
|
|
714
717
|
// re-push/double-integrate). Mutually exclusive with the build-path source +
|
|
715
718
|
// `recovering` flags — the core ignores them when `committedRecovery` is set.
|
|
716
|
-
const folderShapeStranded =
|
|
717
|
-
!onBacklog && !onPreBacklog && !onInProgress && onDone;
|
|
719
|
+
const folderShapeStranded = !onBacklog && !onPreBacklog && onDone;
|
|
718
720
|
// DIRTY-CONTINUE GATE (task `recover-autodetect-gated-on-nothing-to-commit`).
|
|
719
721
|
// The folder-shape stranded-done auto-detect is necessary but NOT sufficient on
|
|
720
722
|
// a CONTINUE: a requeued task whose prior attempt already done-moved the slug
|
|
@@ -749,24 +751,19 @@ async function runComplete(
|
|
|
749
751
|
// "nothing to complete" CompleteRefusal). We default `source` to `'tasks-ready'`
|
|
750
752
|
// so `sourcePath` resolves to the canonical pool path the refusal message
|
|
751
753
|
// names; the refusal fires because that file does not exist on disk.
|
|
752
|
-
const source: 'tasks-ready' | 'tasks-backlog' | '
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
: onPreBacklog
|
|
760
|
-
? 'tasks-backlog'
|
|
761
|
-
: 'tasks-ready';
|
|
754
|
+
const source: 'tasks-ready' | 'tasks-backlog' | 'done' = dirtyContinue
|
|
755
|
+
? 'done'
|
|
756
|
+
: onBacklog
|
|
757
|
+
? 'tasks-ready'
|
|
758
|
+
: onPreBacklog
|
|
759
|
+
? 'tasks-backlog'
|
|
760
|
+
: 'tasks-ready';
|
|
762
761
|
const sourcePath =
|
|
763
762
|
source === 'tasks-ready'
|
|
764
763
|
? backlog
|
|
765
764
|
: source === 'tasks-backlog'
|
|
766
765
|
? staged
|
|
767
|
-
:
|
|
768
|
-
? inProgress
|
|
769
|
-
: done;
|
|
766
|
+
: done;
|
|
770
767
|
if (dirtyContinue) {
|
|
771
768
|
// Announce LOUDLY (parallel to the `committedRecovery` recovery note above):
|
|
772
769
|
// the autonomous integrate path took the CONTINUE-BUILD branch, not the
|
|
@@ -924,9 +921,19 @@ async function runComplete(
|
|
|
924
921
|
|
|
925
922
|
// The FAILURE outcomes map 1:1 onto `complete`'s — the core already note()'d
|
|
926
923
|
// the reason and did any routing; the tail never runs for them.
|
|
924
|
+
// PR-2b exit-code policy (spec
|
|
925
|
+
// `surface-stuck-as-questions-and-retire-stuck-lock-state`, decision #4 / D3):
|
|
926
|
+
// a cleanly-surfaced bounce is GREEN (`exitCode: 0`) iff its surface transition
|
|
927
|
+
// SUCCEEDED (`routedToNeedsAttention: true`); a FAILED surface stays non-zero
|
|
928
|
+
// (retry/resolve). Applies to `prepare-failed`/`gate-failed`/`review-blocked`/
|
|
929
|
+
// `review-unparseable`/`rebase-conflict`. The empty-diff backstop is owned by
|
|
930
|
+
// task `empty-diff-bounce-surfaces-dispose-defaulted-question` and is NOT
|
|
931
|
+
// reached here.
|
|
932
|
+
const surfaceExit = (routed: boolean | undefined): 0 | 1 =>
|
|
933
|
+
routed === true ? 0 : 1;
|
|
927
934
|
if (core.outcome === 'prepare-failed') {
|
|
928
935
|
return {
|
|
929
|
-
exitCode:
|
|
936
|
+
exitCode: surfaceExit(core.routedToNeedsAttention),
|
|
930
937
|
outcome: 'prepare-failed',
|
|
931
938
|
routedToNeedsAttention: core.routedToNeedsAttention,
|
|
932
939
|
branch: core.branch,
|
|
@@ -935,7 +942,7 @@ async function runComplete(
|
|
|
935
942
|
}
|
|
936
943
|
if (core.outcome === 'gate-failed') {
|
|
937
944
|
return {
|
|
938
|
-
exitCode:
|
|
945
|
+
exitCode: surfaceExit(core.routedToNeedsAttention),
|
|
939
946
|
outcome: 'gate-failed',
|
|
940
947
|
routedToNeedsAttention: core.routedToNeedsAttention,
|
|
941
948
|
branch: core.branch,
|
|
@@ -944,7 +951,7 @@ async function runComplete(
|
|
|
944
951
|
}
|
|
945
952
|
if (core.outcome === 'review-blocked') {
|
|
946
953
|
return {
|
|
947
|
-
exitCode:
|
|
954
|
+
exitCode: surfaceExit(core.routedToNeedsAttention),
|
|
948
955
|
outcome: 'review-blocked',
|
|
949
956
|
routedToNeedsAttention: core.routedToNeedsAttention,
|
|
950
957
|
branch: core.branch,
|
|
@@ -958,7 +965,7 @@ async function runComplete(
|
|
|
958
965
|
// `do`/`run` tail re-labels its cause `transient-infra` (NOT a reviewer block) off
|
|
959
966
|
// the parse-failure phrase in `core.reason`.
|
|
960
967
|
return {
|
|
961
|
-
exitCode:
|
|
968
|
+
exitCode: surfaceExit(core.routedToNeedsAttention),
|
|
962
969
|
outcome: 'review-unparseable',
|
|
963
970
|
routedToNeedsAttention: core.routedToNeedsAttention,
|
|
964
971
|
branch: core.branch,
|
|
@@ -967,7 +974,7 @@ async function runComplete(
|
|
|
967
974
|
}
|
|
968
975
|
if (core.outcome === 'rebase-conflict') {
|
|
969
976
|
return {
|
|
970
|
-
exitCode:
|
|
977
|
+
exitCode: surfaceExit(core.routedToNeedsAttention),
|
|
971
978
|
outcome: 'rebase-conflict',
|
|
972
979
|
routedToNeedsAttention: core.routedToNeedsAttention,
|
|
973
980
|
branch: core.branch,
|
|
@@ -1120,6 +1127,43 @@ async function runComplete(
|
|
|
1120
1127
|
// ONE thing the human must act on, so it is emitted as a visually-distinct
|
|
1121
1128
|
// block (blank lines + heading + TTY-aware color) ON TOP of the plain summary
|
|
1122
1129
|
// note below — cosmetic only; the structured result is unchanged.
|
|
1130
|
+
//
|
|
1131
|
+
// BENIGN ALREADY-LANDED RACE TAIL (task `propose-push-survives-stale-lease-
|
|
1132
|
+
// on-reaped-work-ref`, follow-up
|
|
1133
|
+
// `complete-propose-honour-already-landed-and-rename-continue-branch-module`):
|
|
1134
|
+
// when the integrator's propose push observed a REAPED `work/<slug>` ref AND
|
|
1135
|
+
// the work was provably on `<arbiter>/main`, it returns `alreadyLanded: true`
|
|
1136
|
+
// with a custom `instruction`. In that case NOTHING was pushed and there is
|
|
1137
|
+
// NO ref for a PR to point at, so emitting the generic
|
|
1138
|
+
// `formatProposeNextStep` push+PR block ("Pushed ... Open a PR/MR ...") would
|
|
1139
|
+
// misreport a clean no-op as unfinished work. Surface the integrator's
|
|
1140
|
+
// `instruction` VERBATIM instead — it is already the correct clean-no-op
|
|
1141
|
+
// prose (the CI-dominant recovery path via `performIntegration` already does
|
|
1142
|
+
// the same; this is the sibling `complete.ts` caller's honest report). The
|
|
1143
|
+
// tail (switch-to-main / delete-local-branch / `--no-switch`) is UNCHANGED
|
|
1144
|
+
// — only the emitted next-step + summary text differ.
|
|
1145
|
+
if (result.alreadyLanded === true) {
|
|
1146
|
+
const instruction =
|
|
1147
|
+
result.instruction ??
|
|
1148
|
+
`${branch}: already on ${arbiter}/main (clean no-op).`;
|
|
1149
|
+
const tail = options.noSwitch
|
|
1150
|
+
? `; left on ${branch} (--no-switch).`
|
|
1151
|
+
: deletedLocalBranch
|
|
1152
|
+
? `; switched to main and deleted ${branch}.`
|
|
1153
|
+
: '; switched to main.';
|
|
1154
|
+
const message = `Completed '${slug}': ${instruction}${tail}`;
|
|
1155
|
+
note(message);
|
|
1156
|
+
return {
|
|
1157
|
+
exitCode: 0,
|
|
1158
|
+
outcome: 'completed',
|
|
1159
|
+
branch,
|
|
1160
|
+
commitMessage,
|
|
1161
|
+
mergedToMain: false,
|
|
1162
|
+
switchedTo,
|
|
1163
|
+
deletedLocalBranch,
|
|
1164
|
+
message,
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1123
1167
|
const color = options.color ?? shouldUseColor(process.stdout);
|
|
1124
1168
|
const noteBlock = options.noteBlock ?? note;
|
|
1125
1169
|
noteBlock(
|
|
@@ -1316,15 +1360,29 @@ export async function isLocalBranchProvablyOnArbiter(
|
|
|
1316
1360
|
// Refresh remote-tracking refs against the live arbiter (best-effort).
|
|
1317
1361
|
await gitSoft(['fetch', '--quiet', arbiter], cwd, env);
|
|
1318
1362
|
|
|
1319
|
-
// Merged: tip is
|
|
1363
|
+
// Merged: the tip is provably on <arbiter>/main — fast-path ancestor OR
|
|
1364
|
+
// squash-aware fallback (durable done/dropped record on main + branch
|
|
1365
|
+
// carries nothing main lacks). The SAME predicate `gc.ts`'s worktree reaper
|
|
1366
|
+
// and `reap-branches.ts`'s remote sweep use — do not fork it.
|
|
1367
|
+
const parsed = parseWorkBranchRef(branch);
|
|
1368
|
+
const arbiterMainRef = `${arbiter}/main`;
|
|
1320
1369
|
const merged =
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1370
|
+
parsed === undefined
|
|
1371
|
+
? (
|
|
1372
|
+
await gitSoft(
|
|
1373
|
+
['merge-base', '--is-ancestor', localTip, arbiterMainRef],
|
|
1374
|
+
cwd,
|
|
1375
|
+
env,
|
|
1376
|
+
)
|
|
1377
|
+
).status === 0
|
|
1378
|
+
: isProvablyMergedForReap({
|
|
1379
|
+
cwd,
|
|
1380
|
+
tip: localTip,
|
|
1381
|
+
arbiterMain: arbiterMainRef,
|
|
1382
|
+
namespace: parsed.namespace as SidecarType,
|
|
1383
|
+
slug: parsed.slug,
|
|
1384
|
+
env,
|
|
1385
|
+
});
|
|
1328
1386
|
if (merged) {
|
|
1329
1387
|
return true;
|
|
1330
1388
|
}
|
package/src/config.ts
CHANGED
|
@@ -22,7 +22,7 @@ export type IntegrationMode = 'propose' | 'merge';
|
|
|
22
22
|
* `runner-deterministic-slice-placement-policy-and-precedence`, governing ADR
|
|
23
23
|
* `placement-is-runner-deterministic-humanonly-is-agent-judgement`). Which
|
|
24
24
|
* folder the runner lands the tasker's emitted task files in BY DEFAULT —
|
|
25
|
-
* `'
|
|
25
|
+
* `'backlog'` (staging — durable + readable but NOT in the agent-eligible
|
|
26
26
|
* POOL; a runner/human promotion is needed to make an item claimable; the on-disk
|
|
27
27
|
* folder for this value is `work/tasks/backlog/`) or `'ready'` (the agent-eligible
|
|
28
28
|
* POOL — the trusted fast-path landing, on-disk `work/tasks/ready/`). The pool
|
|
@@ -30,12 +30,13 @@ export type IntegrationMode = 'propose' | 'merge';
|
|
|
30
30
|
* `rename-task-pool-folder-todo-to-ready`, a CLEAN BREAK matching the on-disk
|
|
31
31
|
* folder `tasks/ready/` and the spec-side `'ready'` pool spelling). The runner-deterministic
|
|
32
32
|
* placement RESOLVER (`src/placement.ts`) layers on top: `explicit operator flag
|
|
33
|
-
* > untrusted-origin ⇒
|
|
34
|
-
* (
|
|
33
|
+
* > untrusted-origin ⇒ backlog > tasksLandIn default > built-in
|
|
34
|
+
* (backlog)`. The staging value was renamed `'pre-backlog'` → `'backlog'`
|
|
35
|
+
* (matching the on-disk folder `tasks/backlog/`). An untrusted-origin tasker output is FORCED to staging even in
|
|
35
36
|
* a `'ready'` repo (the positional analogue of the existing
|
|
36
37
|
* `untrusted-origin-forces-build-propose` rule).
|
|
37
38
|
*/
|
|
38
|
-
export type TasksLandIn = '
|
|
39
|
+
export type TasksLandIn = 'backlog' | 'ready';
|
|
39
40
|
|
|
40
41
|
/**
|
|
41
42
|
* **Per-repo SPEC-PLACEMENT default** (spec
|
|
@@ -352,7 +353,7 @@ export interface Config {
|
|
|
352
353
|
* {@link integration}: flag (`--merge`/`--propose`) > env
|
|
353
354
|
* (`DORFL_TASKING_INTEGRATION`) > per-repo > global > (fall back to)
|
|
354
355
|
* `integration` > default `propose`. DISTINCT from intake's per-EMITTED-TYPE
|
|
355
|
-
* `{task,
|
|
356
|
+
* `{task, spec}` resolver (front door, author-trust-resolved): this is a
|
|
356
357
|
* per-LIFECYCLE-TRANSITION knob, inside the trust boundary, operator/config-only.
|
|
357
358
|
*/
|
|
358
359
|
taskingIntegration?: IntegrationMode;
|
|
@@ -362,7 +363,7 @@ export interface Config {
|
|
|
362
363
|
* `runner-deterministic-slice-placement-policy-and-precedence`). Resolved
|
|
363
364
|
* per-repo EXACTLY like {@link taskingIntegration} (flag `--tasks-land-in`
|
|
364
365
|
* > env `DORFL_TASKS_LAND_IN` > per-repo > global > built-in
|
|
365
|
-
* `'
|
|
366
|
+
* `'backlog'`). The tasking path reads it and passes it as the
|
|
366
367
|
* CONFIGURED-DEFAULT rung into the shared placement resolver
|
|
367
368
|
* (`src/placement.ts`); the resolver overlays an EXPLICIT operator flag
|
|
368
369
|
* (top) and the UNTRUSTED-ORIGIN force (staging) on top, in that order. The
|
|
@@ -385,7 +386,7 @@ export interface Config {
|
|
|
385
386
|
* influence it. KEY-LEVEL SYMMETRY with `tasksLandIn` — one resolver, two
|
|
386
387
|
* lifecycles, one precedence change touches ONE place.
|
|
387
388
|
*
|
|
388
|
-
* The sole spec-placement key after the
|
|
389
|
+
* The sole spec-placement key after the ''prd'' → `spec` HARD CUTOVER (spec
|
|
389
390
|
* `prd-to-spec-vocabulary-cutover-and-migration-command`): the legacy
|
|
390
391
|
* `prdsLandIn` config key + `--prds-land-in` flag are GONE (clean break).
|
|
391
392
|
*/
|
|
@@ -508,8 +509,8 @@ export interface Config {
|
|
|
508
509
|
* re-rolled — re-reviewing an unchanged tip after a block would just be a
|
|
509
510
|
* stochastic dice re-roll that could launder a real reject into a pass). So a
|
|
510
511
|
* value > 1 makes a FALSE APPROVE harder to slip through (each extra round is a
|
|
511
|
-
* veto), it does NOT give blocked work a second chance. A non-approve forces
|
|
512
|
-
* `
|
|
512
|
+
* veto), it does NOT give blocked work a second chance. A non-approve forces the
|
|
513
|
+
* item stuck (the lock `state: stuck`; never silently merges or loops), per the maintainer
|
|
513
514
|
* decision. Default a small N (2): two independent approvals required. (A future
|
|
514
515
|
* builder-REVISE step that MUTATES the tree between rounds is the only thing that
|
|
515
516
|
* should make a block retryable, because it changes the artifact under review; it
|
|
@@ -626,6 +627,40 @@ export interface Config {
|
|
|
626
627
|
* `observationTriage` default or shape — a SEPARATE, independent axis.
|
|
627
628
|
*/
|
|
628
629
|
strictMergeApproval: boolean;
|
|
630
|
+
/**
|
|
631
|
+
* **The dorfl-INTERNAL agent deadline** (minutes). The agent session self-stops
|
|
632
|
+
* at this budget; dorfl then SAVES the WIP (commit + push `work/<slug>`) and
|
|
633
|
+
* routes it per the progress+ceiling decision (auto-continue, or surface). This
|
|
634
|
+
* is the PRIMARY stop (see spec `graceful-pre-timeout-wip-checkpoint`); the
|
|
635
|
+
* GitHub `timeout-minutes` cap is a BACKSTOP set above it as
|
|
636
|
+
* `agentDeadlineMinutes + checkpointHeadroomMinutes`. Default 60; MAX 240 (4h).
|
|
637
|
+
* FAIL-LOUD: values outside `[1, 240]` throw at config load (never silently
|
|
638
|
+
* clamped). Resolved per-repo through the SAME chain as `integration` (flag >
|
|
639
|
+
* env `DORFL_AGENT_DEADLINE_MINUTES` > per-repo > global > default).
|
|
640
|
+
*/
|
|
641
|
+
agentDeadlineMinutes: number;
|
|
642
|
+
/**
|
|
643
|
+
* **The GitHub-backstop head-room above the internal deadline** (minutes). The
|
|
644
|
+
* enumerate job's dynamic `githubTimeout` output is
|
|
645
|
+
* `agentDeadlineMinutes + checkpointHeadroomMinutes`; the MIN 10 guarantees the
|
|
646
|
+
* GitHub cap is always at least 10 minutes above the internal deadline so the
|
|
647
|
+
* graceful save always has room to commit + push before the hard kill. Default
|
|
648
|
+
* 30; MIN 10; MAX 60 (1h). FAIL-LOUD: out-of-range throws at load. Resolved
|
|
649
|
+
* per-repo through the SAME chain as `agentDeadlineMinutes`.
|
|
650
|
+
*/
|
|
651
|
+
checkpointHeadroomMinutes: number;
|
|
652
|
+
/**
|
|
653
|
+
* **The auto-continue ceiling** for the deadline-checkpoint anti-loop guard
|
|
654
|
+
* (spec `graceful-pre-timeout-wip-checkpoint`). Counts CONSECUTIVE deadline
|
|
655
|
+
* auto-continues; on hitting this cap the next deadline checkpoint SURFACES a
|
|
656
|
+
* `needsAnswers:true` question ("this task has hit the deadline N times — it
|
|
657
|
+
* may be too big for one CI leg; split it or run it locally?") instead of
|
|
658
|
+
* auto-continuing. The counter naturally resets when the item completes (the
|
|
659
|
+
* work branch is discarded on integration). Default 5. Must be a positive
|
|
660
|
+
* integer (fail-loud). Resolved per-repo through the SAME chain as its
|
|
661
|
+
* siblings.
|
|
662
|
+
*/
|
|
663
|
+
maxAutoCheckpoints: number;
|
|
629
664
|
/**
|
|
630
665
|
* The optional runner **identity** (a bot): run the runner's git + provider
|
|
631
666
|
* operations as a configured entity via process-scoped env overrides, without
|
|
@@ -741,7 +776,7 @@ export const DEFAULT_CONFIG: Config = {
|
|
|
741
776
|
// always first. Reproduces today's tasks-first two-pool default. Subsumes the
|
|
742
777
|
// removed `prdsFirst` boolean (`[task, build, ...]` reproduces `prdsFirst: true`).
|
|
743
778
|
selectionOrder: DEFAULT_SELECTION_ORDER,
|
|
744
|
-
maxParallel:
|
|
779
|
+
maxParallel: 2,
|
|
745
780
|
perRepoMax: 2,
|
|
746
781
|
defaultArbiter: 'origin',
|
|
747
782
|
workspacesDir: join(homedir(), brand.workdirName),
|
|
@@ -752,14 +787,14 @@ export const DEFAULT_CONFIG: Config = {
|
|
|
752
787
|
// branch but deliberately skip the PR (the explicit suppress-PR intent that
|
|
753
788
|
// re-homes the old `provider: none` use). NOT a provider choice.
|
|
754
789
|
noPR: false,
|
|
755
|
-
// The tasker's emitted tasks land STAGED (`
|
|
790
|
+
// The tasker's emitted tasks land STAGED (`tasks/backlog/`) by default — the
|
|
756
791
|
// conservative landing that preserves the tracer task's behaviour: an item is
|
|
757
792
|
// durable + readable but NOT in the agent-eligible pool until a human/runner
|
|
758
793
|
// promotes it. A repo opts into the trusted fast-path with `tasksLandIn:
|
|
759
794
|
// 'ready'` (or `--tasks-land-in ready` / `DORFL_TASKS_LAND_IN=ready`).
|
|
760
795
|
// The runner-deterministic resolver overlays explicit-flag + untrusted-origin
|
|
761
796
|
// force on top of this default (`src/placement.ts`).
|
|
762
|
-
tasksLandIn: '
|
|
797
|
+
tasksLandIn: 'backlog',
|
|
763
798
|
// `intake`-authored specs land STAGED (`pre-proposed/`) by default — the
|
|
764
799
|
// conservative landing that mirrors `tasksLandIn`'s built-in floor: a spec is
|
|
765
800
|
// durable + readable but NOT in the auto-tasking POOL until a human/runner
|
|
@@ -806,8 +841,65 @@ export const DEFAULT_CONFIG: Config = {
|
|
|
806
841
|
// `DORFL_STRICT_MERGE_APPROVAL=true`). The re-surface vs. land branch is
|
|
807
842
|
// `apply-rung-merge-disposition`'s consumer, NOT here.
|
|
808
843
|
strictMergeApproval: false,
|
|
844
|
+
// The dorfl-INTERNAL agent deadline defaults to 60 minutes (spec
|
|
845
|
+
// `graceful-pre-timeout-wip-checkpoint`). A legitimately-long task that runs
|
|
846
|
+
// past this checkpoints its WIP + auto-continues on the next tick (rather than
|
|
847
|
+
// losing everything to a 6h GitHub SIGKILL). MAX 240 (4h); fail-loud outside.
|
|
848
|
+
agentDeadlineMinutes: 60,
|
|
849
|
+
// The GitHub-backstop head-room above the internal deadline defaults to 30
|
|
850
|
+
// minutes: the enumerate job's dynamic `githubTimeout` output is
|
|
851
|
+
// deadline + headroom, so the default GitHub cap is 60 + 30 = 90 (1h30). MIN 10
|
|
852
|
+
// (guarantees the graceful save has room); MAX 60; fail-loud outside.
|
|
853
|
+
checkpointHeadroomMinutes: 30,
|
|
854
|
+
// The auto-continue ceiling defaults to 5 CONSECUTIVE deadline checkpoints;
|
|
855
|
+
// past that the next checkpoint SURFACES to a human (spec
|
|
856
|
+
// `graceful-pre-timeout-wip-checkpoint` anti-loop guard).
|
|
857
|
+
maxAutoCheckpoints: 5,
|
|
809
858
|
};
|
|
810
859
|
|
|
860
|
+
/**
|
|
861
|
+
* Fail-loud range validation for the deadline / backstop / ceiling triple
|
|
862
|
+
* (spec `graceful-pre-timeout-wip-checkpoint`). Called at the two resolution
|
|
863
|
+
* FINAL points (`loadConfig` for the global chain; `resolveRepoConfigFromLoaded`
|
|
864
|
+
* for the per-repo chain), so an out-of-range value from ANY layer (flag / env /
|
|
865
|
+
* per-repo / global) surfaces as a clean throw naming the field + the valid
|
|
866
|
+
* range. Never clamps — the task's decision is fail-loud, since a silently
|
|
867
|
+
* clamped 400-minute deadline would look correct in `dorfl config --json` and
|
|
868
|
+
* quietly break the invariant that GitHub cap > internal deadline.
|
|
869
|
+
*/
|
|
870
|
+
export function validateDeadlineConfig(config: Config): void {
|
|
871
|
+
const adm = config.agentDeadlineMinutes;
|
|
872
|
+
if (!Number.isInteger(adm) || adm < 1 || adm > 240) {
|
|
873
|
+
throw new Error(
|
|
874
|
+
`agentDeadlineMinutes must be a positive integer in [1, 240] ` +
|
|
875
|
+
`(got ${adm}). The dorfl-internal agent deadline caps a single ` +
|
|
876
|
+
`agent session; 240 minutes (4h) is the hard ceiling so the ` +
|
|
877
|
+
`GitHub backstop (deadline + headroom) still fits under GitHub's ` +
|
|
878
|
+
`6h job limit.`,
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
const chm = config.checkpointHeadroomMinutes;
|
|
882
|
+
if (!Number.isInteger(chm) || chm < 10 || chm > 60) {
|
|
883
|
+
throw new Error(
|
|
884
|
+
`checkpointHeadroomMinutes must be an integer in [10, 60] ` +
|
|
885
|
+
`(got ${chm}). This is the head-room the GitHub 'timeout-minutes' ` +
|
|
886
|
+
`backstop sits above the internal deadline (rendered dynamically ` +
|
|
887
|
+
`as agentDeadlineMinutes + checkpointHeadroomMinutes by the ` +
|
|
888
|
+
`advance-lifecycle 'enumerate' job); MIN 10 guarantees the ` +
|
|
889
|
+
`graceful save always has room to commit + push before the hard kill.`,
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
const cap = config.maxAutoCheckpoints;
|
|
893
|
+
if (!Number.isInteger(cap) || cap < 1) {
|
|
894
|
+
throw new Error(
|
|
895
|
+
`maxAutoCheckpoints must be a positive integer (got ${cap}). ` +
|
|
896
|
+
`It caps CONSECUTIVE deadline auto-continues; past this the ` +
|
|
897
|
+
`next checkpoint surfaces a needs-answers question to a human ` +
|
|
898
|
+
`(the anti-loop guard for the graceful pre-timeout checkpoint).`,
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
811
903
|
/** The conventional config location (`~/.config/dorfl/config.json`). */
|
|
812
904
|
export function defaultConfigPath(): string {
|
|
813
905
|
return join(homedir(), '.config', brand.configDirName, 'config.json');
|
|
@@ -879,5 +971,7 @@ export function loadConfig(path: string = defaultConfigPath()): Config {
|
|
|
879
971
|
);
|
|
880
972
|
}
|
|
881
973
|
}
|
|
882
|
-
|
|
974
|
+
const merged = mergeConfig(parsed);
|
|
975
|
+
validateDeadlineConfig(merged);
|
|
976
|
+
return merged;
|
|
883
977
|
}
|
package/src/continue-branch.ts
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import {run, type RunResult} from './git.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
+
* BROADENED SCOPE NOTE (follow-up task
|
|
5
|
+
* `complete-propose-honour-already-landed-and-rename-continue-branch-module`):
|
|
6
|
+
* this module's name still reads as "continue-branch" (its ORIGINAL role: the
|
|
7
|
+
* continue-detection + rebase-onto-fresh-main helpers used by the onboarding
|
|
8
|
+
* paths, documented below), BUT it now ALSO hosts the shared
|
|
9
|
+
* **stale-lease work-branch push** helpers used by MULTIPLE callers:
|
|
10
|
+
*
|
|
11
|
+
* - {@link pushContinuedBranchWithStaleLeaseRetry} — the original
|
|
12
|
+
* continue/onboard caller (the mirror hub → arbiter reconcile push).
|
|
13
|
+
* - {@link pushProposeBranchWithStaleLeaseRetry} — the propose-mode
|
|
14
|
+
* integrator's push (task `propose-push-survives-stale-lease-on-reaped-work-
|
|
15
|
+
* ref`), which adds the BENIGN already-landed predicate (gone remote ref +
|
|
16
|
+
* work reachable from `<arbiter>/main`) on top of the shared `stale info`
|
|
17
|
+
* retry.
|
|
18
|
+
*
|
|
19
|
+
* A rename to something like `stale-lease-work-push.ts` was considered; the
|
|
20
|
+
* scope-note is preferred here because a rename would churn many imports
|
|
21
|
+
* across `src/` + `test/` for no behavioural gain, and the module already reads
|
|
22
|
+
* as a single conceptual bucket ("the work-branch push + continue-detection
|
|
23
|
+
* helpers"). Add new stale-lease work-ref push helpers here rather than
|
|
24
|
+
* spawning a sibling module.
|
|
25
|
+
*
|
|
4
26
|
* The **continue-detection** shared by BOTH onboarding paths (the keystone of
|
|
5
27
|
* the `requeue-continue-and-reset` task). `requeue` keeps the `work/<slug>`
|
|
6
28
|
* branch so the NEXT claim CONTINUES from its tip instead of force-cutting a
|