dorfl 0.1.1 → 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-ci-template.d.ts +3 -3
- package/dist/advance-ci-template.js +1 -1
- package/dist/advance-ci-template.js.map +1 -1
- package/dist/advance-classify.d.ts +5 -5
- package/dist/advance-classify.d.ts.map +1 -1
- package/dist/advance-classify.js +4 -4
- package/dist/advance-drivers.d.ts +8 -8
- package/dist/advance-drivers.d.ts.map +1 -1
- package/dist/advance-drivers.js +20 -8
- package/dist/advance-drivers.js.map +1 -1
- package/dist/advance-isolated.d.ts +3 -3
- package/dist/advance-isolated.d.ts.map +1 -1
- package/dist/advance-isolated.js +1 -1
- package/dist/advance-lifecycle-template.d.ts +2 -2
- package/dist/advance-lifecycle-template.d.ts.map +1 -1
- package/dist/advance-lifecycle-template.js +85 -13
- package/dist/advance-lifecycle-template.js.map +1 -1
- package/dist/advance-loop-driver.d.ts +3 -3
- package/dist/advance-loop-driver.d.ts.map +1 -1
- package/dist/advance-loop-driver.js +1 -1
- package/dist/advance-treeless-publish.d.ts +24 -1
- 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 +69 -17
- package/dist/advance.d.ts.map +1 -1
- package/dist/advance.js +409 -102
- package/dist/advance.js.map +1 -1
- package/dist/advancing-lock.d.ts +31 -3
- package/dist/advancing-lock.d.ts.map +1 -1
- package/dist/advancing-lock.js +55 -5
- 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 +19 -5
- package/dist/apply-decide.d.ts.map +1 -1
- package/dist/apply-decide.js +38 -9
- package/dist/apply-decide.js.map +1 -1
- package/dist/apply-merge-action.d.ts +20 -8
- package/dist/apply-merge-action.d.ts.map +1 -1
- package/dist/apply-merge-action.js +44 -9
- package/dist/apply-merge-action.js.map +1 -1
- package/dist/apply-persist.d.ts +62 -31
- package/dist/apply-persist.d.ts.map +1 -1
- package/dist/apply-persist.js +173 -44
- 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/buildable-body.d.ts +14 -14
- package/dist/buildable-body.js +9 -9
- package/dist/claim-cas.d.ts +3 -3
- package/dist/claim-cas.js +1 -1
- package/dist/claim-cas.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 +385 -128
- package/dist/cli.js.map +1 -1
- package/dist/close-job-template.d.ts +3 -3
- package/dist/close-job-template.js +9 -9
- package/dist/close-job.d.ts +8 -8
- package/dist/close-job.js +25 -25
- package/dist/close-job.js.map +1 -1
- package/dist/complete.d.ts +9 -6
- package/dist/complete.d.ts.map +1 -1
- package/dist/complete.js +99 -45
- package/dist/complete.js.map +1 -1
- package/dist/concurrency.d.ts +1 -1
- package/dist/concurrency.js +1 -1
- package/dist/config.d.ts +86 -40
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +61 -9
- package/dist/config.js.map +1 -1
- package/dist/continue-branch.d.ts +1 -1
- package/dist/continue-branch.d.ts.map +1 -1
- package/dist/continue-branch.js +23 -1
- package/dist/continue-branch.js.map +1 -1
- package/dist/cwd-section.js +1 -1
- package/dist/cwd-section.js.map +1 -1
- package/dist/decision-engine.d.ts +39 -11
- 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 +6 -6
- package/dist/do-autopick.d.ts.map +1 -1
- package/dist/do-autopick.js +16 -6
- package/dist/do-autopick.js.map +1 -1
- package/dist/do-config.d.ts +4 -4
- package/dist/do-config.js +3 -3
- package/dist/do-config.js.map +1 -1
- package/dist/do-remote-auto.d.ts +2 -2
- package/dist/do-remote-auto.js +1 -1
- package/dist/do.d.ts +74 -81
- package/dist/do.d.ts.map +1 -1
- package/dist/do.js +439 -50
- package/dist/do.js.map +1 -1
- package/dist/drop-source.d.ts +2 -2
- package/dist/env-config.d.ts.map +1 -1
- package/dist/env-config.js +19 -11
- 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 +13 -30
- package/dist/format.js.map +1 -1
- package/dist/frontmatter.d.ts +28 -15
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +38 -24
- 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 +133 -39
- package/dist/install-ci-branch-protection.d.ts.map +1 -1
- package/dist/install-ci-branch-protection.js +191 -49
- package/dist/install-ci-branch-protection.js.map +1 -1
- package/dist/install-ci-capabilities/advance-lifecycle.d.ts +1 -1
- package/dist/install-ci-capabilities/advance-lifecycle.js +1 -1
- package/dist/install-ci-capabilities/close-job.d.ts +1 -1
- package/dist/install-ci-capabilities/close-job.js +1 -1
- package/dist/install-ci-capabilities/example-noop.d.ts +1 -1
- package/dist/install-ci-capabilities/example-noop.js +1 -1
- package/dist/install-ci-capabilities/intake.d.ts +2 -2
- package/dist/install-ci-capabilities/intake.js +2 -2
- package/dist/install-ci-capabilities/verify.d.ts +1 -1
- package/dist/install-ci-capabilities/verify.js +1 -1
- package/dist/install-ci-core.d.ts +24 -4
- package/dist/install-ci-core.d.ts.map +1 -1
- package/dist/install-ci-core.js +12 -5
- package/dist/install-ci-core.js.map +1 -1
- package/dist/install-ci-github.d.ts +33 -1
- package/dist/install-ci-github.d.ts.map +1 -1
- package/dist/install-ci-github.js +74 -1
- package/dist/install-ci-github.js.map +1 -1
- package/dist/install-ci.d.ts +1 -1
- package/dist/install-ci.js +4 -4
- package/dist/install-ci.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-event.d.ts +6 -6
- package/dist/intake-event.js +6 -6
- package/dist/intake-marker.d.ts +3 -3
- package/dist/intake-marker.d.ts.map +1 -1
- package/dist/intake-triage.d.ts +1 -1
- package/dist/intake-triage.js +2 -2
- package/dist/intake-trigger-template.d.ts +8 -8
- package/dist/intake-trigger-template.d.ts.map +1 -1
- package/dist/intake-trigger-template.js +17 -17
- package/dist/intake-trigger-template.js.map +1 -1
- package/dist/intake.d.ts +65 -52
- package/dist/intake.d.ts.map +1 -1
- package/dist/intake.js +92 -76
- package/dist/intake.js.map +1 -1
- package/dist/integration-core.d.ts +27 -24
- package/dist/integration-core.d.ts.map +1 -1
- package/dist/integration-core.js +191 -77
- 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 +4 -4
- package/dist/isolation.d.ts.map +1 -1
- package/dist/isolation.js +9 -0
- package/dist/isolation.js.map +1 -1
- package/dist/issue-provider.d.ts +4 -4
- package/dist/issue-provider.js +1 -1
- package/dist/item-lock.d.ts +216 -149
- package/dist/item-lock.d.ts.map +1 -1
- package/dist/item-lock.js +345 -270
- package/dist/item-lock.js.map +1 -1
- package/dist/item-path.d.ts +2 -2
- package/dist/item-path.js +2 -2
- package/dist/ledger-lint.d.ts +9 -9
- package/dist/ledger-lint.js +9 -9
- package/dist/ledger-read.d.ts +57 -57
- package/dist/ledger-read.d.ts.map +1 -1
- package/dist/ledger-read.js +24 -24
- package/dist/ledger-read.js.map +1 -1
- package/dist/ledger-write.d.ts +33 -28
- 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 +18 -1
- package/dist/lifecycle-gather.d.ts.map +1 -1
- package/dist/lifecycle-gather.js +23 -17
- package/dist/lifecycle-gather.js.map +1 -1
- package/dist/lifecycle-pools.d.ts +44 -7
- package/dist/lifecycle-pools.d.ts.map +1 -1
- package/dist/lifecycle-pools.js +27 -7
- package/dist/lifecycle-pools.js.map +1 -1
- package/dist/merge-question-surfacer.d.ts +18 -4
- package/dist/merge-question-surfacer.d.ts.map +1 -1
- package/dist/merge-question-surfacer.js +20 -5
- 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/mint-adr.js +4 -4
- package/dist/mirror-pool-scan.d.ts +2 -2
- package/dist/mirror-pool-scan.js +3 -3
- package/dist/mirror-pool-scan.js.map +1 -1
- package/dist/needs-attention.d.ts +304 -27
- package/dist/needs-attention.d.ts.map +1 -1
- package/dist/needs-attention.js +542 -67
- package/dist/needs-attention.js.map +1 -1
- package/dist/orphan-sidecar.d.ts +10 -6
- 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 +8 -8
- package/dist/placement.js +3 -3
- 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/prompt.d.ts +16 -19
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +17 -19
- package/dist/prompt.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/readiness.d.ts +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 +6 -1
- 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 +64 -18
- 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-gate.d.ts +3 -3
- package/dist/review-gate.d.ts.map +1 -1
- package/dist/review-gate.js +11 -10
- package/dist/review-gate.js.map +1 -1
- package/dist/review-verdict.d.ts +2 -2
- package/dist/review-verdict.d.ts.map +1 -1
- package/dist/review-verdict.js +2 -2
- package/dist/review-verdict.js.map +1 -1
- package/dist/run.d.ts +2 -2
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +58 -23
- package/dist/run.js.map +1 -1
- package/dist/scan.d.ts +26 -17
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +29 -15
- package/dist/scan.js.map +1 -1
- package/dist/select-order.d.ts +1 -1
- package/dist/select-priority.d.ts +14 -14
- package/dist/select-priority.d.ts.map +1 -1
- package/dist/select-priority.js +5 -5
- 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 +47 -12
- package/dist/sidecar.d.ts.map +1 -1
- package/dist/sidecar.js +84 -4
- 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 +6 -6
- package/dist/slug-namespace.js +8 -8
- package/dist/slug-namespace.js.map +1 -1
- package/dist/spec-complete.d.ts.map +1 -1
- package/dist/spec-complete.js +4 -5
- package/dist/spec-complete.js.map +1 -1
- package/dist/start.d.ts +1 -1
- package/dist/start.d.ts.map +1 -1
- package/dist/start.js +54 -60
- package/dist/start.js.map +1 -1
- package/dist/status.d.ts +3 -3
- package/dist/status.js +4 -4
- package/dist/status.js.map +1 -1
- package/dist/surface-gate.d.ts +2 -2
- 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.d.ts +2 -2
- package/dist/surface-persist.d.ts.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 +11 -10
- package/dist/tasker-review-loop.d.ts.map +1 -1
- package/dist/tasker-review-loop.js +8 -8
- package/dist/tasker-review-loop.js.map +1 -1
- package/dist/tasking-eligibility.d.ts +21 -21
- package/dist/tasking-eligibility.d.ts.map +1 -1
- package/dist/tasking-eligibility.js +10 -10
- package/dist/tasking-lock.d.ts +10 -10
- package/dist/tasking-lock.d.ts.map +1 -1
- package/dist/tasking-lock.js +69 -49
- package/dist/tasking-lock.js.map +1 -1
- package/dist/tasking.d.ts +73 -39
- package/dist/tasking.d.ts.map +1 -1
- package/dist/tasking.js +309 -95
- package/dist/tasking.js.map +1 -1
- package/dist/triage-gate.d.ts +1 -1
- package/dist/triage-persist.d.ts +15 -11
- package/dist/triage-persist.d.ts.map +1 -1
- package/dist/triage-persist.js +48 -18
- 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.d.ts +1 -1
- package/dist/verify-workflow-template.js +3 -3
- 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 +15 -9
- package/dist/work-layout.d.ts.map +1 -1
- package/dist/work-layout.js +14 -9
- package/dist/work-layout.js.map +1 -1
- package/dist/workspace.d.ts +2 -2
- package/package.json +2 -2
- package/src/advance-ci-template.ts +4 -4
- package/src/advance-classify.ts +5 -5
- package/src/advance-drivers.ts +26 -13
- package/src/advance-isolated.ts +3 -3
- package/src/advance-lifecycle-template.ts +98 -13
- package/src/advance-loop-driver.ts +3 -3
- package/src/advance-treeless-publish.ts +46 -1
- package/src/advance.ts +495 -115
- package/src/advancing-lock.ts +102 -7
- package/src/agent-launch.ts +37 -12
- package/src/agent-stop.ts +60 -2
- package/src/apply-decide.ts +38 -9
- package/src/apply-merge-action.ts +49 -11
- package/src/apply-persist.ts +236 -62
- package/src/apply-stuck-action.ts +260 -0
- package/src/brand.ts +14 -2
- package/src/buildable-body.ts +14 -14
- package/src/claim-cas.ts +4 -4
- package/src/cli-spinner.ts +1 -1
- package/src/cli.ts +472 -141
- package/src/close-job-template.ts +9 -9
- package/src/close-job.ts +26 -26
- package/src/complete.ts +121 -63
- package/src/concurrency.ts +1 -1
- package/src/config.ts +143 -49
- package/src/continue-branch.ts +23 -1
- package/src/cwd-section.ts +1 -1
- package/src/decision-engine.ts +57 -19
- package/src/do-autopick.ts +21 -10
- package/src/do-config.ts +7 -7
- package/src/do-remote-auto.ts +2 -2
- package/src/do.ts +558 -85
- package/src/drop-source.ts +2 -2
- package/src/env-config.ts +19 -11
- package/src/failure-cause.ts +30 -1
- package/src/format.ts +13 -33
- package/src/frontmatter.ts +55 -35
- 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 +283 -58
- package/src/install-ci-capabilities/advance-lifecycle.ts +1 -1
- package/src/install-ci-capabilities/close-job.ts +1 -1
- package/src/install-ci-capabilities/example-noop.ts +1 -1
- package/src/install-ci-capabilities/intake.ts +2 -2
- package/src/install-ci-capabilities/verify.ts +1 -1
- package/src/install-ci-core.ts +34 -7
- package/src/install-ci-github.ts +87 -1
- package/src/install-ci.ts +4 -4
- package/src/install-skills.ts +166 -0
- package/src/intake-event.ts +6 -6
- package/src/intake-marker.ts +3 -3
- package/src/intake-triage.ts +2 -2
- package/src/intake-trigger-template.ts +18 -18
- package/src/intake.ts +138 -103
- package/src/integration-core.ts +234 -100
- package/src/integrator.ts +22 -9
- package/src/isolation.ts +13 -4
- package/src/issue-provider.ts +4 -4
- package/src/item-lock.ts +505 -361
- package/src/item-path.ts +2 -2
- package/src/ledger-lint.ts +9 -9
- package/src/ledger-read.ts +81 -81
- package/src/ledger-write.ts +137 -152
- package/src/lifecycle-gather.ts +45 -22
- package/src/lifecycle-pools.ts +71 -13
- package/src/merge-question-surfacer.ts +32 -8
- package/src/migrate-stuck-locks.ts +451 -0
- package/src/mint-adr.ts +4 -4
- package/src/mirror-pool-scan.ts +5 -5
- package/src/needs-attention.ts +877 -104
- package/src/orphan-sidecar.ts +49 -8
- package/src/pi-harness.ts +82 -2
- package/src/placement.ts +8 -8
- package/src/prd-to-spec.ts +13 -5
- package/src/prompt.ts +20 -25
- package/src/readiness.ts +1 -1
- package/src/reap-branches.ts +38 -14
- package/src/recover-isolated.ts +18 -1
- package/src/repo-config.ts +66 -17
- package/src/repo-mirror.ts +23 -1
- package/src/review-gate.ts +12 -11
- package/src/review-verdict.ts +3 -3
- package/src/run.ts +69 -23
- package/src/scan.ts +36 -22
- package/src/select-order.ts +1 -1
- package/src/select-priority.ts +15 -15
- package/src/sidecar-apply.ts +1 -1
- package/src/sidecar.ts +141 -14
- package/src/slug-namespace.ts +10 -10
- package/src/spec-complete.ts +4 -5
- package/src/start.ts +57 -63
- package/src/status.ts +7 -7
- package/src/surface-gate.ts +12 -5
- package/src/surface-persist.ts +3 -3
- package/src/tasker-review-loop.ts +18 -17
- package/src/tasking-eligibility.ts +21 -21
- package/src/tasking-lock.ts +75 -60
- package/src/tasking.ts +381 -135
- package/src/triage-gate.ts +1 -1
- package/src/triage-persist.ts +71 -27
- 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 +3 -3
- package/src/watch-session.ts +102 -7
- package/src/work-layout.ts +14 -9
- package/src/workspace.ts +2 -2
package/src/cli.ts
CHANGED
|
@@ -121,6 +121,7 @@ import {status, formatStatus} from './status.js';
|
|
|
121
121
|
import {ledgerWrite} from './ledger-write.js';
|
|
122
122
|
import {
|
|
123
123
|
releaseItemLock,
|
|
124
|
+
releaseLiteralLockEntry,
|
|
124
125
|
reportItemLocks,
|
|
125
126
|
formatItemLockReport,
|
|
126
127
|
itemLockReportNeedsAttention,
|
|
@@ -128,6 +129,11 @@ import {
|
|
|
128
129
|
formatReapReport,
|
|
129
130
|
reapReportNeedsAttention,
|
|
130
131
|
} from './item-lock.js';
|
|
132
|
+
import {
|
|
133
|
+
migrateStuckLocks,
|
|
134
|
+
formatMigrateStuckLocksReport,
|
|
135
|
+
migrateStuckLocksNeedsAttention,
|
|
136
|
+
} from './migrate-stuck-locks.js';
|
|
131
137
|
import {
|
|
132
138
|
promoteFromPreBacklog,
|
|
133
139
|
promoteFromPreSpec,
|
|
@@ -146,6 +152,7 @@ import {installCI, type WizardPrompts} from './install-ci.js';
|
|
|
146
152
|
import {GitHubCIContext} from './install-ci-github.js';
|
|
147
153
|
import {loadCapabilityRegistry} from './install-ci-core.js';
|
|
148
154
|
import {performCloseMergedIssues} from './close-job.js';
|
|
155
|
+
import {installSkills, type InstallSkillsResult} from './install-skills.js';
|
|
149
156
|
|
|
150
157
|
interface ScanFlags {
|
|
151
158
|
config?: string;
|
|
@@ -353,10 +360,10 @@ function buildRegistrySetAdvanceTick(options: {
|
|
|
353
360
|
lifecycleGates: {
|
|
354
361
|
triage: config.observationTriage !== 'off',
|
|
355
362
|
surface: config.surfaceBlockers,
|
|
356
|
-
// `surfaceStaging` widens the SURFACE candidate set into STAGING (
|
|
363
|
+
// `surfaceStaging` widens the SURFACE candidate set into STAGING (spec
|
|
357
364
|
// `staging-surface-and-apply-promote-safety` F2). Default `true` — a
|
|
358
|
-
// tasked `needsAnswers` task in `tasks/backlog/` (or
|
|
359
|
-
// `
|
|
365
|
+
// tasked `needsAnswers` task in `tasks/backlog/` (or spec in
|
|
366
|
+
// `specs/proposed/`) surfaces its questions BEFORE promotion. BUILD/claim
|
|
360
367
|
// stays pool-only either way.
|
|
361
368
|
surfaceStaging: config.surfaceStaging,
|
|
362
369
|
},
|
|
@@ -388,11 +395,11 @@ function buildRegistrySetAdvanceTick(options: {
|
|
|
388
395
|
identity: config.identity,
|
|
389
396
|
autoTask: config.autoTask,
|
|
390
397
|
integration: config.integration,
|
|
391
|
-
// The per-TRANSITION TASKING override: the `do
|
|
398
|
+
// The per-TRANSITION TASKING override: the `do spec:` tasking path threads
|
|
392
399
|
// `taskingIntegration ?? integration`; the build path stays on `integration`.
|
|
393
400
|
taskingIntegration: config.taskingIntegration,
|
|
394
|
-
// The TASK-PLACEMENT configured default (`do
|
|
395
|
-
// `
|
|
401
|
+
// The TASK-PLACEMENT configured default (`do spec:` tasking output:
|
|
402
|
+
// `backlog` staged vs `ready` pool). No operator flag on this
|
|
396
403
|
// registry-driven advance context, so only the configured default rung is
|
|
397
404
|
// threaded (the resolver still layers untrusted-origin force + built-in floor).
|
|
398
405
|
tasksLandIn: config.tasksLandIn,
|
|
@@ -400,7 +407,7 @@ function buildRegistrySetAdvanceTick(options: {
|
|
|
400
407
|
verify: config.verify,
|
|
401
408
|
// Single-job build path: gate the REBASED tip (the default) unconditionally.
|
|
402
409
|
freshWorktreeGate: config.freshWorktreeGate,
|
|
403
|
-
// Cross-job merge-serialiser CAS-retry cap (
|
|
410
|
+
// Cross-job merge-serialiser CAS-retry cap (spec `land-time-reverify-and-
|
|
404
411
|
// parallel-merge-ceiling` Story 5 / Applied Answer q1 (a)) — resolved per-repo
|
|
405
412
|
// and threaded so the registry-driven advance path's `do` inherits it.
|
|
406
413
|
mergeRetries: config.mergeRetries,
|
|
@@ -439,7 +446,7 @@ function buildRegistrySetAdvanceTick(options: {
|
|
|
439
446
|
triageGate: harnessTriageGate({harness, agentCmd: config.agentCmd}),
|
|
440
447
|
triageModel: config.model,
|
|
441
448
|
// The ANSWERED-MERGE LAND DISPATCH context (task
|
|
442
|
-
// `apply-rung-merge-disposition`,
|
|
449
|
+
// `apply-rung-merge-disposition`, spec `land-time-reverify-and-parallel-
|
|
443
450
|
// merge-ceiling`): the dispatcher cuts a per-job worktree via
|
|
444
451
|
// `workspace.ts` `createJob` off the hub mirror (so we thread the resolved
|
|
445
452
|
// `workspacesDir` + the real arbiter URL — the per-mirror tree-less
|
|
@@ -544,7 +551,7 @@ interface RunFlags extends ScanFlags {
|
|
|
544
551
|
reviewMaxRounds?: string;
|
|
545
552
|
/** `--fresh-worktree-gate` / `--no-fresh-worktree-gate` — gate the REBASED tip in a clean throwaway worktree (ON by default). */
|
|
546
553
|
freshWorktreeGate?: boolean;
|
|
547
|
-
/** `--merge-retries <n>` — the cross-job merge-serialiser CAS-retry cap (
|
|
554
|
+
/** `--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)). */
|
|
548
555
|
mergeRetries?: string;
|
|
549
556
|
}
|
|
550
557
|
|
|
@@ -583,7 +590,7 @@ function runFlagOverrides(flags: RunFlags, command?: Commander): PartialConfig {
|
|
|
583
590
|
// fleet conditional lives in `runOnce`, not in this flag mapping).
|
|
584
591
|
Object.assign(overrides, freshWorktreeGateFlagOverrides(flags));
|
|
585
592
|
// `--merge-retries <n>` rides the SAME chain: the cross-job merge-serialiser
|
|
586
|
-
// CAS-retry cap (
|
|
593
|
+
// CAS-retry cap (spec `land-time-reverify-and-parallel-merge-ceiling` Story 5 /
|
|
587
594
|
// Applied Answer q1 (a)). The `run` fleet inherits the resolved cap through
|
|
588
595
|
// the converged `performIntegration` core (config.mergeRetries threads into
|
|
589
596
|
// the merge loop, replacing the bare `DEFAULT_MERGE_RETRIES` fallback).
|
|
@@ -661,7 +668,7 @@ interface CompleteFlags {
|
|
|
661
668
|
reviewMaxRounds?: string;
|
|
662
669
|
/** `--fresh-worktree-gate` / `--no-fresh-worktree-gate` — gate the REBASED tip in a clean throwaway worktree (ON by default). */
|
|
663
670
|
freshWorktreeGate?: boolean;
|
|
664
|
-
/** `--merge-retries <n>` — the cross-job merge-serialiser CAS-retry cap (
|
|
671
|
+
/** `--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)). */
|
|
665
672
|
mergeRetries?: string;
|
|
666
673
|
/** `--isolated`: finish the slug's retained job worktree (the stranded-branch recover). */
|
|
667
674
|
isolated?: boolean;
|
|
@@ -670,7 +677,7 @@ interface CompleteFlags {
|
|
|
670
677
|
|
|
671
678
|
/**
|
|
672
679
|
* Resolve the EXPLICIT operator placement override from `--tasks-land-in <where>`
|
|
673
|
-
* (the top of the `do
|
|
680
|
+
* (the top of the `do spec:` tasking-placement precedence — task
|
|
674
681
|
* `runner-deterministic-slice-placement-policy-and-precedence`). Mirrors the
|
|
675
682
|
* `flagMode === 'merge'` ⇒ `explicitMerge: true` shape: it contributes
|
|
676
683
|
* `explicitTasksLandIn` ONLY when the operator actually typed the flag, so an
|
|
@@ -681,13 +688,13 @@ interface CompleteFlags {
|
|
|
681
688
|
*/
|
|
682
689
|
function explicitTasksLandInFromFlag(
|
|
683
690
|
raw: string | undefined,
|
|
684
|
-
): '
|
|
691
|
+
): 'backlog' | 'ready' | undefined {
|
|
685
692
|
if (raw === undefined) {
|
|
686
693
|
return undefined;
|
|
687
694
|
}
|
|
688
|
-
if (raw !== '
|
|
695
|
+
if (raw !== 'backlog' && raw !== 'ready') {
|
|
689
696
|
throw new Error(
|
|
690
|
-
`--tasks-land-in must be '
|
|
697
|
+
`--tasks-land-in must be 'backlog' or 'ready' (got '${raw}').`,
|
|
691
698
|
);
|
|
692
699
|
}
|
|
693
700
|
return raw;
|
|
@@ -726,8 +733,10 @@ interface DoFlags {
|
|
|
726
733
|
config?: string;
|
|
727
734
|
arbiter?: string;
|
|
728
735
|
remote?: string;
|
|
729
|
-
/** `--isolated`: build in a job worktree off THIS repo's arbiter (no checkout takeover). */
|
|
736
|
+
/** `--isolated`: build in a job worktree off THIS repo's arbiter (no checkout takeover). Since `make-isolated-default-build-mode` this is the DEFAULT; the flag remains accepted as a redundant explicit opt-IN alias (D3). */
|
|
730
737
|
isolated?: boolean;
|
|
738
|
+
/** `--in-place`: opt OUT of the new default (isolated off the arbiter) and build in the CURRENT checkout, restoring today's dirty-tree-refusing in-place behaviour (D3). */
|
|
739
|
+
inPlace?: boolean;
|
|
731
740
|
/** `-n <x>`: do x eligible items in sequence (auto-pick form). */
|
|
732
741
|
number?: string;
|
|
733
742
|
/** `--selection-order <order>`: a preset keyword (drain/groom) or comma-separated pool order. */
|
|
@@ -742,7 +751,7 @@ interface DoFlags {
|
|
|
742
751
|
strictMergeApproval?: boolean;
|
|
743
752
|
merge?: boolean;
|
|
744
753
|
propose?: boolean;
|
|
745
|
-
/** `--tasks-land-in <
|
|
754
|
+
/** `--tasks-land-in <backlog|ready>`: the explicit operator placement override for `do spec:` tasking output (top of the placement precedence). Resolves into the `tasksLandIn` config key. */
|
|
746
755
|
tasksLandIn?: string;
|
|
747
756
|
/** `--no-pr` ⇒ commander stores `pr === false` (the suppress-PR intent). */
|
|
748
757
|
pr?: boolean;
|
|
@@ -758,15 +767,15 @@ interface DoFlags {
|
|
|
758
767
|
review?: boolean;
|
|
759
768
|
reviewModel?: string;
|
|
760
769
|
reviewMaxRounds?: string;
|
|
761
|
-
/** `--tasker-loop` / `--no-tasker-loop` — the tasker improver loop on/off toggle (`do
|
|
770
|
+
/** `--tasker-loop` / `--no-tasker-loop` — the tasker improver loop on/off toggle (`do spec:` path). Resolves into the `taskerLoop` config key. */
|
|
762
771
|
taskerLoop?: boolean;
|
|
763
|
-
/** `--tasker-loop-max <n>` — the tasker improver loop's in-context convergence cap (`do
|
|
772
|
+
/** `--tasker-loop-max <n>` — the tasker improver loop's in-context convergence cap (`do spec:` path). Resolves into the `taskerLoopMax` config key. */
|
|
764
773
|
taskerLoopMax?: string;
|
|
765
|
-
/** `--tasker-loop-model <id>` — the tasker improver loop reviewer's de-correlated model (`do
|
|
774
|
+
/** `--tasker-loop-model <id>` — the tasker improver loop reviewer's de-correlated model (`do spec:` path). Resolves into the `taskerLoopModel` config key. */
|
|
766
775
|
taskerLoopModel?: string;
|
|
767
776
|
/** `--fresh-worktree-gate` / `--no-fresh-worktree-gate` — gate the REBASED tip in a clean throwaway worktree (ON by default). */
|
|
768
777
|
freshWorktreeGate?: boolean;
|
|
769
|
-
/** `--merge-retries <n>` — the cross-job merge-serialiser CAS-retry cap (
|
|
778
|
+
/** `--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)). */
|
|
770
779
|
mergeRetries?: string;
|
|
771
780
|
}
|
|
772
781
|
|
|
@@ -783,7 +792,7 @@ interface IntakeFlags {
|
|
|
783
792
|
proposeTask?: boolean;
|
|
784
793
|
/**
|
|
785
794
|
* `--origin-trust <trusted|untrusted>` — the author-trust verdict the CI shell
|
|
786
|
-
* passes IN so `intake` STAMPS the emitted
|
|
795
|
+
* passes IN so `intake` STAMPS the emitted spec/task (task
|
|
787
796
|
* `untrusted-origin-forces-build-propose`). `intake` does NOT resolve trust; the
|
|
788
797
|
* shell derives it from the SAME `author_association` case as the integration
|
|
789
798
|
* flags. UNSET (a local intake) ⇒ emit unstamped ⇒ human/trusted.
|
|
@@ -886,7 +895,7 @@ function printPrdToSpecReport(result: PrdToSpecResult): void {
|
|
|
886
895
|
return;
|
|
887
896
|
}
|
|
888
897
|
if (result.leaks.length === 0) {
|
|
889
|
-
console.log('Leak scan: GREEN (no surviving
|
|
898
|
+
console.log('Leak scan: GREEN (no surviving spec data ref).');
|
|
890
899
|
} else {
|
|
891
900
|
console.error(`Leak scan: FAILED (${result.leaks.length} leak(s)):`);
|
|
892
901
|
for (const leak of result.leaks) {
|
|
@@ -916,6 +925,8 @@ interface RequeueFlags {
|
|
|
916
925
|
cwd?: string;
|
|
917
926
|
arbiter?: string;
|
|
918
927
|
reset?: boolean;
|
|
928
|
+
reconcile?: boolean;
|
|
929
|
+
rebase?: boolean;
|
|
919
930
|
message?: string;
|
|
920
931
|
}
|
|
921
932
|
|
|
@@ -929,10 +940,10 @@ interface ReleaseLockFlags {
|
|
|
929
940
|
config?: string;
|
|
930
941
|
cwd?: string;
|
|
931
942
|
arbiter?: string;
|
|
943
|
+
entry?: string;
|
|
932
944
|
}
|
|
933
945
|
|
|
934
946
|
interface DropFlags {
|
|
935
|
-
config?: string;
|
|
936
947
|
cwd?: string;
|
|
937
948
|
reason?: string;
|
|
938
949
|
}
|
|
@@ -979,11 +990,12 @@ interface CloseMergedIssuesFlags {
|
|
|
979
990
|
/**
|
|
980
991
|
* Resolve a task-only command's slug argument through the §3a namespace guard
|
|
981
992
|
* (`resolveTaskOnlyArg`): accept bare (= task) + `task:` (explicit alias),
|
|
982
|
-
* REJECT `spec:` with a clear "operates on tasks, not specs" error
|
|
983
|
-
*
|
|
984
|
-
*
|
|
985
|
-
*
|
|
986
|
-
*
|
|
993
|
+
* REJECT `spec:` with a clear "operates on tasks, not specs" error. On rejection
|
|
994
|
+
* it prints the error to stderr and exits 1 (the task-only commands never act on
|
|
995
|
+
* a spec). An OMITTED slug (`start`/`complete`/`prompt` infer it from the branch)
|
|
996
|
+
* passes through untouched. (The legacy ''prd:'' prefix is a DEAD namespace token
|
|
997
|
+
* after the hard cutover — it is neither special-cased nor rejected here; a
|
|
998
|
+
* ''prd:<slug>'' arg resolves as a bare literal task slug.)
|
|
987
999
|
*
|
|
988
1000
|
* `do` is the ONE command that spans both namespaces; it consumes the full
|
|
989
1001
|
* `resolveSlug` (with the cross-namespace collision check) in the `do-in-place`
|
|
@@ -1067,14 +1079,14 @@ function buildInteractiveLauncher(
|
|
|
1067
1079
|
* switching to its `work/<slug>` branch WITHOUT claiming). The runtime
|
|
1068
1080
|
* difference is exactly the `resume` flag — `resume` forces it on (its only mode
|
|
1069
1081
|
* is to re-engage), while `start` honours the (now hidden) `--resume` alias.
|
|
1070
|
-
* Both are task-only (§3a: accept bare + `task:`, reject `
|
|
1082
|
+
* Both are task-only (§3a: accept bare + `task:`, reject `spec:`).
|
|
1071
1083
|
*/
|
|
1072
1084
|
async function runStartAction(
|
|
1073
1085
|
rawSlug: string | undefined,
|
|
1074
1086
|
flags: StartFlags,
|
|
1075
1087
|
resume: boolean,
|
|
1076
1088
|
): Promise<void> {
|
|
1077
|
-
// Task-only command (§3a): accept bare + `task:`, reject `
|
|
1089
|
+
// Task-only command (§3a): accept bare + `task:`, reject `spec:`.
|
|
1078
1090
|
const slug = resolveTaskOnlySlug(rawSlug);
|
|
1079
1091
|
const cwd = process.cwd();
|
|
1080
1092
|
|
|
@@ -1163,6 +1175,44 @@ export function buildProgram(): Command {
|
|
|
1163
1175
|
.name(brand.bin)
|
|
1164
1176
|
.description('Autonomous parallel agents over file-based work/ queues.');
|
|
1165
1177
|
|
|
1178
|
+
// `dorfl config --json` (spec `graceful-pre-timeout-wip-checkpoint` — step 5):
|
|
1179
|
+
// print the RESOLVED per-repo config as JSON, so BOTH the advance-lifecycle
|
|
1180
|
+
// workflow's `enumerate` job (which reads it to emit the dynamic
|
|
1181
|
+
// `githubTimeout` output) AND a human debugging a config-precedence question
|
|
1182
|
+
// share ONE honest primitive. A focused honest primitive — NOT overloaded
|
|
1183
|
+
// onto `scan`. Runs in the cwd (in-place), so the resolution chain reads THIS
|
|
1184
|
+
// repo's committed `dorfl.json` exactly like `scan --here`.
|
|
1185
|
+
program
|
|
1186
|
+
.command('config')
|
|
1187
|
+
.helpGroup(HEADLINE_GROUP)
|
|
1188
|
+
.description(
|
|
1189
|
+
"Print the resolved config for the current repo (flag > env > per-repo > global > default). Consumed by the advance-lifecycle workflow's enumerate job to emit the dynamic GitHub timeout backstop (agentDeadlineMinutes + checkpointHeadroomMinutes) at run time from the committed dorfl.json — so editing dorfl.json reflects everywhere (internal deadline AND GitHub cap) on the next tick, no install-ci re-run.",
|
|
1190
|
+
)
|
|
1191
|
+
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
1192
|
+
.option(
|
|
1193
|
+
'--arbiter <remote>',
|
|
1194
|
+
'name of the arbiter git remote (default: per-repo/global defaultArbiter)',
|
|
1195
|
+
)
|
|
1196
|
+
.option('--json', 'output the resolved config as JSON')
|
|
1197
|
+
.action(
|
|
1198
|
+
async (flags: {config?: string; arbiter?: string; json?: boolean}) => {
|
|
1199
|
+
if (flags.json !== true) {
|
|
1200
|
+
console.error(
|
|
1201
|
+
'error: `dorfl config` currently supports only --json output. Pass --json.',
|
|
1202
|
+
);
|
|
1203
|
+
process.exit(1);
|
|
1204
|
+
}
|
|
1205
|
+
const cwd = process.cwd();
|
|
1206
|
+
const {global, override} = loadGlobalAndOverride(flags.config);
|
|
1207
|
+
const resolved = resolveRepoConfig({
|
|
1208
|
+
repoPath: cwd,
|
|
1209
|
+
global,
|
|
1210
|
+
override,
|
|
1211
|
+
}).config;
|
|
1212
|
+
console.log(JSON.stringify(resolved, null, 2));
|
|
1213
|
+
},
|
|
1214
|
+
);
|
|
1215
|
+
|
|
1166
1216
|
program
|
|
1167
1217
|
.command('scan')
|
|
1168
1218
|
.helpGroup(HEADLINE_GROUP)
|
|
@@ -1515,7 +1565,7 @@ export function buildProgram(): Command {
|
|
|
1515
1565
|
.command('claim')
|
|
1516
1566
|
.helpGroup(ADVANCED_GROUP)
|
|
1517
1567
|
.description(
|
|
1518
|
-
|
|
1568
|
+
`Atomically claim a ${workFolderPrefix('tasks-ready')}<slug>.md item via a compare-and-swap push to the arbiter (in-process; mirrors scripts/claim.sh).`,
|
|
1519
1569
|
)
|
|
1520
1570
|
.argument('<slug>', 'the slug of the backlog item to claim')
|
|
1521
1571
|
.option(
|
|
@@ -1530,7 +1580,7 @@ export function buildProgram(): Command {
|
|
|
1530
1580
|
'override the readiness guard: claim despite an unmet blockedBy, and silence the needsAnswers warning (loud, never default)',
|
|
1531
1581
|
)
|
|
1532
1582
|
.action(async (rawSlug: string, flags: ClaimFlags) => {
|
|
1533
|
-
// Task-only command (§3a): accept bare + `task:`, reject `
|
|
1583
|
+
// Task-only command (§3a): accept bare + `task:`, reject `spec:`.
|
|
1534
1584
|
const slug = resolveTaskOnlySlug(rawSlug) as string;
|
|
1535
1585
|
// Wrap ONLY this CLI surface's `performClaim` call with the spinner
|
|
1536
1586
|
// helper (task `claim-cas-spinner`): the push can take seconds, so the
|
|
@@ -1736,7 +1786,7 @@ export function buildProgram(): Command {
|
|
|
1736
1786
|
flags.remote !== undefined && flags.remote.trim() !== ''
|
|
1737
1787
|
? flags.remote
|
|
1738
1788
|
: undefined;
|
|
1739
|
-
// Task-only command (§3a): accept bare + `task:`, reject `
|
|
1789
|
+
// Task-only command (§3a): accept bare + `task:`, reject `spec:`.
|
|
1740
1790
|
const theSlug = resolveTaskOnlySlug(rawSlug) as string;
|
|
1741
1791
|
|
|
1742
1792
|
const configPath = flags.config ?? defaultConfigPath();
|
|
@@ -1788,14 +1838,14 @@ export function buildProgram(): Command {
|
|
|
1788
1838
|
.command('prompt')
|
|
1789
1839
|
.helpGroup(ADVANCED_GROUP)
|
|
1790
1840
|
.description(
|
|
1791
|
-
|
|
1841
|
+
`Print to stdout the work-agent prompt for a task: the canonical CLAIM-PROTOCOL wrapper + the task's own ## Prompt (with <slug> and source spec substituted). Resolves work/in-progress/<slug>.md then ${workFolderPrefix('tasks-ready')}<slug>.md; infers <slug> from a work/<slug> branch when omitted. Read-only, stdout only — the same assembly the autonomous runner feeds agentCmd.`,
|
|
1792
1842
|
)
|
|
1793
1843
|
.argument(
|
|
1794
1844
|
'[slug]',
|
|
1795
1845
|
'the slug to render (inferred from a work/<slug> branch if omitted)',
|
|
1796
1846
|
)
|
|
1797
1847
|
.action((rawSlug: string | undefined) => {
|
|
1798
|
-
// Task-only command (§3a): accept bare + `task:`, reject `
|
|
1848
|
+
// Task-only command (§3a): accept bare + `task:`, reject `spec:`.
|
|
1799
1849
|
const slug = resolveTaskOnlySlug(rawSlug);
|
|
1800
1850
|
// Resolve the `promptGuidance` NUDGE namespace through the SAME chain the
|
|
1801
1851
|
// gate family uses (env > per-repo > global > default), so e.g. a
|
|
@@ -1902,7 +1952,7 @@ export function buildProgram(): Command {
|
|
|
1902
1952
|
'cross-job merge-serialiser CAS-retry cap (see `run --help`); resolved flag > env > per-repo > global > default 1000.',
|
|
1903
1953
|
)
|
|
1904
1954
|
.action(async (rawSlug: string | undefined, flags: CompleteFlags) => {
|
|
1905
|
-
// Task-only command (§3a): accept bare + `task:`, reject `
|
|
1955
|
+
// Task-only command (§3a): accept bare + `task:`, reject `spec:`.
|
|
1906
1956
|
const slug = resolveTaskOnlySlug(rawSlug);
|
|
1907
1957
|
const cwd = process.cwd();
|
|
1908
1958
|
const {global, override} = loadGlobalAndOverride(flags.config);
|
|
@@ -1926,6 +1976,11 @@ export function buildProgram(): Command {
|
|
|
1926
1976
|
override,
|
|
1927
1977
|
flags: {
|
|
1928
1978
|
...(flagMode ? {integration: flagMode} : {}),
|
|
1979
|
+
// `--merge-retries <n>` rides the SAME gate-family precedence chain on
|
|
1980
|
+
// the isolated-recovery path so a wide-matrix CI's raised cap actually
|
|
1981
|
+
// reaches the land tail here too (task
|
|
1982
|
+
// `thread-merge-retries-cross-task-and-ratify-default`).
|
|
1983
|
+
...mergeRetriesFlagOverrides(flags),
|
|
1929
1984
|
...noPRFlagOverrides(flags),
|
|
1930
1985
|
},
|
|
1931
1986
|
});
|
|
@@ -1940,6 +1995,9 @@ export function buildProgram(): Command {
|
|
|
1940
1995
|
workspacesDir: flags.workspace ?? isoConfig.workspacesDir,
|
|
1941
1996
|
integration: isoConfig.integration,
|
|
1942
1997
|
noPR: isoConfig.noPR,
|
|
1998
|
+
// The resolved cross-job CAS-retry cap threaded through so the isolated-
|
|
1999
|
+
// recovery land tail respects the per-repo cap (same chain as `complete`).
|
|
2000
|
+
mergeRetries: isoConfig.mergeRetries,
|
|
1943
2001
|
note: (message) => console.error(`>> ${message}`),
|
|
1944
2002
|
env: process.env,
|
|
1945
2003
|
});
|
|
@@ -1966,7 +2024,7 @@ export function buildProgram(): Command {
|
|
|
1966
2024
|
// `--fresh-worktree-gate`/`--no-fresh-worktree-gate` rides the SAME chain.
|
|
1967
2025
|
...freshWorktreeGateFlagOverrides(flags),
|
|
1968
2026
|
// `--merge-retries <n>` rides the SAME chain: the cross-job merge-serialiser
|
|
1969
|
-
// CAS-retry cap (
|
|
2027
|
+
// CAS-retry cap (spec `land-time-reverify-and-parallel-merge-ceiling` Story 5
|
|
1970
2028
|
// / Applied Answer q1 (a)).
|
|
1971
2029
|
...mergeRetriesFlagOverrides(flags),
|
|
1972
2030
|
// `--no-pr` (the PR-INTENT axis) rides the SAME chain.
|
|
@@ -2006,7 +2064,7 @@ export function buildProgram(): Command {
|
|
|
2006
2064
|
// a single-job path, so the resolved flag is passed UNCONDITIONALLY (no
|
|
2007
2065
|
// fleet downgrade).
|
|
2008
2066
|
freshWorktreeGate: config.freshWorktreeGate,
|
|
2009
|
-
// Cross-job merge-serialiser CAS-retry cap (
|
|
2067
|
+
// Cross-job merge-serialiser CAS-retry cap (spec `land-time-reverify-and-
|
|
2010
2068
|
// parallel-merge-ceiling` Story 5 / Applied Answer q1 (a)) — the resolved
|
|
2011
2069
|
// per-repo value reaches the merge loop via `performComplete`→
|
|
2012
2070
|
// `performIntegration`.
|
|
@@ -2039,7 +2097,7 @@ export function buildProgram(): Command {
|
|
|
2039
2097
|
.command('do')
|
|
2040
2098
|
.helpGroup(HEADLINE_GROUP)
|
|
2041
2099
|
.description(
|
|
2042
|
-
|
|
2100
|
+
"The per-repo WORKER (the CI command): claim + onboard onto work/<slug>, run the agent, gate, integrate, and exit. Builds in an ISOLATED job worktree off THIS repo's arbiter by DEFAULT (task `make-isolated-default-build-mode` — the cwd is the origin SOURCE only; the human working tree is never written). --in-place opts OUT (build in the current checkout, refusing a dirty tree — today's pre-flip behaviour). --isolated remains accepted as a redundant explicit opt-in alias. With --remote <r>: against a REGISTERED repo with NO checkout — materialise a hub mirror + job worktree in the agents\u2019 area, run the same pipeline there, then reap. do <slug> | do task:<slug> | do spec:<slug> (the tasking path) | do (auto-pick one) | do <a> <b> (those, in sequence) | do -n <x> (x eligible, in sequence). Auto-pick draws TASKS-FIRST then SPECS-to-task by default (per-repo selectionOrder reorders the pools). --propose (default) / --merge resolved at integrate-time. Supersedes ar-run.sh.",
|
|
2043
2101
|
)
|
|
2044
2102
|
// EXTENSIBLE argument grammar (the three do-* tasks grow this one block):
|
|
2045
2103
|
// `do-autopick` widens the single optional positional into a VARIADIC one so
|
|
@@ -2048,7 +2106,7 @@ export function buildProgram(): Command {
|
|
|
2048
2106
|
// for the auto-pick form. `do` stays SEQUENTIAL (parallelism is `run`).
|
|
2049
2107
|
.argument(
|
|
2050
2108
|
'[slugs...]',
|
|
2051
|
-
'the item(s) to do: bare (= the task), task:<slug>, or spec:<slug> (task the spec
|
|
2109
|
+
'the item(s) to do: bare (= the task), task:<slug>, or spec:<slug> (task the spec). Zero args = auto-pick; multiple = do them in sequence.',
|
|
2052
2110
|
)
|
|
2053
2111
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
2054
2112
|
.option(
|
|
@@ -2069,7 +2127,11 @@ export function buildProgram(): Command {
|
|
|
2069
2127
|
)
|
|
2070
2128
|
.option(
|
|
2071
2129
|
'--isolated',
|
|
2072
|
-
"build in an ISOLATED job worktree off THIS repo's arbiter (inferred from cwd) instead of taking over the current checkout, then integrate + reap \u2014
|
|
2130
|
+
"build in an ISOLATED job worktree off THIS repo's arbiter (inferred from cwd) instead of taking over the current checkout, then integrate + reap \u2014 NOW THE DEFAULT since `make-isolated-default-build-mode`; the flag remains accepted as a redundant explicit opt-IN alias (some scripts/skills pin it, and it is harmless once implied). Shares the same grammar as the no-checkout forms: a single named item, multiple named items (in sequence), AND -n/auto-pick over the mirror-side eligible-pool scan. Always SEQUENTIAL (parallelism is `run` / the CI matrix). Orthogonal to --remote (a foreign repo); with --remote, remote wins (isolation is already implied). Mutually exclusive with --in-place (contradictory intents).",
|
|
2131
|
+
)
|
|
2132
|
+
.option(
|
|
2133
|
+
'--in-place',
|
|
2134
|
+
"OPT OUT of the new isolated default: build in the CURRENT checkout (integrates in place, refuses a dirty tree) \u2014 today's pre-flip behaviour, now explicit. Mutually exclusive with --isolated (contradictory intents) and with --remote (there is no local checkout to take over). Use this for the edit-locally-then-build loop; the default (isolated off the arbiter) never writes the cwd working tree.",
|
|
2073
2135
|
)
|
|
2074
2136
|
.option(
|
|
2075
2137
|
'--merge',
|
|
@@ -2081,7 +2143,7 @@ export function buildProgram(): Command {
|
|
|
2081
2143
|
)
|
|
2082
2144
|
.option(
|
|
2083
2145
|
'--tasks-land-in <where>',
|
|
2084
|
-
'where `do
|
|
2146
|
+
'where `do spec:<slug>` tasking output lands: `backlog` (staged, not agent-eligible) or `ready` (the agent POOL). The EXPLICIT operator override at the top of the placement precedence (explicit flag > untrusted-origin forces staging > tasksLandIn default > built-in). Resolved flag > env (DORFL_TASKS_LAND_IN) > per-repo > global > built-in.',
|
|
2085
2147
|
)
|
|
2086
2148
|
.option(
|
|
2087
2149
|
'--no-pr',
|
|
@@ -2134,15 +2196,15 @@ export function buildProgram(): Command {
|
|
|
2134
2196
|
)
|
|
2135
2197
|
.option(
|
|
2136
2198
|
'--tasker-loop',
|
|
2137
|
-
'run the tasker IMPROVER loop on `do
|
|
2199
|
+
'run the tasker IMPROVER loop on `do spec:<slug>` (review→edit→converge over the produced task set). ON by default; --no-tasker-loop skips it. DISTINCT from the acceptance gate (--review).',
|
|
2138
2200
|
)
|
|
2139
2201
|
.option(
|
|
2140
2202
|
'--no-tasker-loop',
|
|
2141
|
-
'skip the tasker improver loop on `do
|
|
2203
|
+
'skip the tasker improver loop on `do spec:<slug>`',
|
|
2142
2204
|
)
|
|
2143
2205
|
.option(
|
|
2144
2206
|
'--tasker-loop-max <n>',
|
|
2145
|
-
'cap the tasker improver loop on `do
|
|
2207
|
+
'cap the tasker improver loop on `do spec:<slug>` (in-context review passes); on exhaustion with blockers, reject via needsAnswers / route the spec to needs-attention (default 3)',
|
|
2146
2208
|
)
|
|
2147
2209
|
.option(
|
|
2148
2210
|
'--tasker-loop-model <id>',
|
|
@@ -2220,14 +2282,52 @@ export function buildProgram(): Command {
|
|
|
2220
2282
|
// `remote-do-reads-per-repo-config-from-arbiter-main`). Only the whitelisted
|
|
2221
2283
|
// `REPO_ALLOWED_KEYS` are layered (host-only keys stay global/flag/env-only,
|
|
2222
2284
|
// rejected by the SAME `repo-config.ts` split).
|
|
2285
|
+
// Mutually-exclusive intent guards for the three FORM flags (task
|
|
2286
|
+
// `make-isolated-default-build-mode`, D3): `--in-place` opts OUT of the new
|
|
2287
|
+
// isolated default, `--isolated` is the redundant explicit opt-IN alias,
|
|
2288
|
+
// `--remote <r>` targets a foreign repo. `--in-place` + `--isolated` is
|
|
2289
|
+
// contradictory (build here vs build off the arbiter); `--in-place` + `--remote`
|
|
2290
|
+
// is nonsensical (there is no local checkout to take over). Reject loudly
|
|
2291
|
+
// rather than silently prefer one.
|
|
2292
|
+
if (flags.inPlace === true && flags.isolated === true) {
|
|
2293
|
+
console.error(
|
|
2294
|
+
'error: --in-place and --isolated are contradictory (build in the current ' +
|
|
2295
|
+
"checkout vs build in a worktree off this repo's arbiter). Pick one.",
|
|
2296
|
+
);
|
|
2297
|
+
process.exit(1);
|
|
2298
|
+
}
|
|
2299
|
+
if (flags.inPlace === true && flags.remote !== undefined) {
|
|
2300
|
+
console.error(
|
|
2301
|
+
'error: --in-place builds in the CURRENT checkout; --remote <r> targets a ' +
|
|
2302
|
+
'REGISTERED repo with NO checkout. Pick one.',
|
|
2303
|
+
);
|
|
2304
|
+
process.exit(1);
|
|
2305
|
+
}
|
|
2306
|
+
// The DEFAULT flip (task `make-isolated-default-build-mode`): with no
|
|
2307
|
+
// `--remote` and no `--in-place`, `do <slug>` builds in an ISOLATED job
|
|
2308
|
+
// worktree off THIS repo's arbiter — the SAME no-checkout path `--isolated`
|
|
2309
|
+
// already used. The cwd is the origin SOURCE only (arbiter-remote resolution
|
|
2310
|
+
// + per-repo config), never written. `--isolated` remains accepted as a
|
|
2311
|
+
// redundant explicit opt-IN alias (D3). `--in-place` opts out to today's
|
|
2312
|
+
// in-checkout path (the fall-through below). See ADR §3.
|
|
2223
2313
|
const isolatedNoRemote =
|
|
2224
|
-
flags.
|
|
2314
|
+
flags.remote === undefined && flags.inPlace !== true;
|
|
2315
|
+
// Whether the user DEFAULTED into isolated (no form flag typed) vs typed
|
|
2316
|
+
// `--isolated` explicitly. Only used to tune the no-arbiter error message so
|
|
2317
|
+
// the default path names `--in-place` as its natural escape (D2).
|
|
2318
|
+
const defaultedToIsolated = isolatedNoRemote && flags.isolated !== true;
|
|
2225
2319
|
if (flags.remote !== undefined || isolatedNoRemote) {
|
|
2226
2320
|
// The form's user-facing name + canonical usage, for the shared error
|
|
2227
|
-
// messages below (so
|
|
2228
|
-
const form = isolatedNoRemote
|
|
2321
|
+
// messages below (so isolated errors read in the form's own terms).
|
|
2322
|
+
const form = isolatedNoRemote
|
|
2323
|
+
? defaultedToIsolated
|
|
2324
|
+
? 'do'
|
|
2325
|
+
: '--isolated'
|
|
2326
|
+
: '--remote';
|
|
2229
2327
|
const usage = isolatedNoRemote
|
|
2230
|
-
?
|
|
2328
|
+
? defaultedToIsolated
|
|
2329
|
+
? '`do <slug>`'
|
|
2330
|
+
: '`do --isolated <slug>`'
|
|
2231
2331
|
: '`do --remote <r> <slug>`';
|
|
2232
2332
|
// The no-checkout forms now support the SAME variadic grammar the in-place
|
|
2233
2333
|
// form does: a single NAMED item, MULTIPLE named items (sequential), and
|
|
@@ -2257,12 +2357,33 @@ export function buildProgram(): Command {
|
|
|
2257
2357
|
identityEnv(bootstrapIdentity, process.env),
|
|
2258
2358
|
);
|
|
2259
2359
|
if (resolvedUrl === undefined) {
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2360
|
+
// Isolated needs an arbiter to build off. Two message variants:
|
|
2361
|
+
// - defaulted-to-isolated (no form flag typed) leads with the DEFAULT
|
|
2362
|
+
// and names `--in-place` as the natural local escape (D2). This is
|
|
2363
|
+
// the ERROR-not-silent-degrade behaviour the flip requires: a repo
|
|
2364
|
+
// with no arbiter must NOT be quietly built in-place invisibly.
|
|
2365
|
+
// - explicit `--isolated` keeps its own terms (the user asked for
|
|
2366
|
+
// isolation deliberately) but ALSO surfaces `--in-place` alongside
|
|
2367
|
+
// `--remote <url>` as the two escapes.
|
|
2368
|
+
if (defaultedToIsolated) {
|
|
2369
|
+
console.error(
|
|
2370
|
+
`error: no arbiter is configured for this repo ` +
|
|
2371
|
+
`(no '${arbiterName}' remote), and \`do\` now defaults to ` +
|
|
2372
|
+
`building in an ISOLATED worktree off THIS repo's arbiter ` +
|
|
2373
|
+
`(task \`make-isolated-default-build-mode\`). Configure an ` +
|
|
2374
|
+
`arbiter (e.g. \`dorfl remote add <url>\`), pass --in-place ` +
|
|
2375
|
+
`to build in the current checkout, or use --remote <url> to ` +
|
|
2376
|
+
`target another repo.`,
|
|
2377
|
+
);
|
|
2378
|
+
} else {
|
|
2379
|
+
console.error(
|
|
2380
|
+
`error: --isolated builds in a worktree off this repo's arbiter ` +
|
|
2381
|
+
`('${arbiterName}'), but no such arbiter remote is configured/found ` +
|
|
2382
|
+
`here. Run inside a participating repo (a clone with an arbiter ` +
|
|
2383
|
+
`remote), pass --in-place to build in the current checkout, or ` +
|
|
2384
|
+
`use --remote <url> to target another repo.`,
|
|
2385
|
+
);
|
|
2386
|
+
}
|
|
2266
2387
|
process.exit(1);
|
|
2267
2388
|
}
|
|
2268
2389
|
effectiveRemote = resolvedUrl;
|
|
@@ -2310,7 +2431,7 @@ export function buildProgram(): Command {
|
|
|
2310
2431
|
// Host-only runner IDENTITY — scopes git/provider ops only (not the
|
|
2311
2432
|
// agent launch); absent ⇒ ambient.
|
|
2312
2433
|
identity: remoteConfig.identity,
|
|
2313
|
-
// `do --remote
|
|
2434
|
+
// `do --remote spec:<slug>` tasking-gate policy (task-build path ignores it).
|
|
2314
2435
|
autoTask: remoteConfig.autoTask,
|
|
2315
2436
|
// The resolved `promptGuidance` nudge — threaded into the remote worker
|
|
2316
2437
|
// prompt (runRemotePipeline → buildAgentPrompt), mirroring in-place `do`.
|
|
@@ -2318,7 +2439,7 @@ export function buildProgram(): Command {
|
|
|
2318
2439
|
integration: remoteConfig.integration,
|
|
2319
2440
|
// EXPLICIT `--merge` override for the untrusted-origin build-propose rule.
|
|
2320
2441
|
explicitMerge: flagMode === 'merge',
|
|
2321
|
-
// Per-TRANSITION TASKING override (the `do --remote
|
|
2442
|
+
// Per-TRANSITION TASKING override (the `do --remote spec:` tasking path).
|
|
2322
2443
|
taskingIntegration: remoteConfig.taskingIntegration,
|
|
2323
2444
|
// TASK-PLACEMENT: the configured default + the EXPLICIT operator override
|
|
2324
2445
|
// (`--tasks-land-in`), the top of the placement precedence — mirrors
|
|
@@ -2330,7 +2451,7 @@ export function buildProgram(): Command {
|
|
|
2330
2451
|
// Single-job build path: gate the REBASED tip (the default) unconditionally.
|
|
2331
2452
|
freshWorktreeGate: remoteConfig.freshWorktreeGate,
|
|
2332
2453
|
// Cross-job merge-serialiser CAS-retry cap (resolved through the per-repo
|
|
2333
|
-
// chain on the arbiter-side `.dorfl.json` too) —
|
|
2454
|
+
// chain on the arbiter-side `.dorfl.json` too) — spec
|
|
2334
2455
|
// `land-time-reverify-and-parallel-merge-ceiling` Story 5.
|
|
2335
2456
|
mergeRetries: remoteConfig.mergeRetries,
|
|
2336
2457
|
noPR: remoteConfig.noPR,
|
|
@@ -2347,7 +2468,7 @@ export function buildProgram(): Command {
|
|
|
2347
2468
|
agentCmd: remoteConfig.agentCmd,
|
|
2348
2469
|
})
|
|
2349
2470
|
: undefined,
|
|
2350
|
-
// The tasker IMPROVER loop on the `do --remote
|
|
2471
|
+
// The tasker IMPROVER loop on the `do --remote spec:` path is ON by default
|
|
2351
2472
|
// (auto-tasking has no `verify` floor, so the loop is the task path's
|
|
2352
2473
|
// quality engine). `--tasker-loop`/`--no-tasker-loop` gates wiring the seam;
|
|
2353
2474
|
// `taskerLoopMax`/`taskerLoopModel` resolve per-repo (flag > env > per-repo
|
|
@@ -2360,7 +2481,7 @@ export function buildProgram(): Command {
|
|
|
2360
2481
|
: undefined,
|
|
2361
2482
|
taskerLoopMax: remoteConfig.taskerLoopMax,
|
|
2362
2483
|
taskerLoopModel: remoteConfig.taskerLoopModel,
|
|
2363
|
-
// The task-SET ACCEPTANCE GATE on the `do --remote
|
|
2484
|
+
// The task-SET ACCEPTANCE GATE on the `do --remote spec:` path too.
|
|
2364
2485
|
taskReviewGate: remoteConfig.review
|
|
2365
2486
|
? harnessTaskAcceptanceGate({
|
|
2366
2487
|
harness: remoteHarness,
|
|
@@ -2368,6 +2489,11 @@ export function buildProgram(): Command {
|
|
|
2368
2489
|
})
|
|
2369
2490
|
: undefined,
|
|
2370
2491
|
watch: flags.watch === true,
|
|
2492
|
+
// The dorfl-internal agent deadline + anti-loop ceiling (spec
|
|
2493
|
+
// `graceful-pre-timeout-wip-checkpoint`). Threaded from the RESOLVED
|
|
2494
|
+
// remote-repo config (its committed `dorfl.json` on <arbiter>/main).
|
|
2495
|
+
agentDeadlineMinutes: remoteConfig.agentDeadlineMinutes,
|
|
2496
|
+
maxAutoCheckpoints: remoteConfig.maxAutoCheckpoints,
|
|
2371
2497
|
color: shouldUseColor(process.stdout),
|
|
2372
2498
|
note: (message) => console.error(`>> ${message}`),
|
|
2373
2499
|
noteBlock: (message) => console.error(message),
|
|
@@ -2472,7 +2598,7 @@ export function buildProgram(): Command {
|
|
|
2472
2598
|
// ops (claim, push, integrate, `gh`) — NEVER the agent launch. Absent ⇒
|
|
2473
2599
|
// ambient (today's behaviour). Mapped Config → DoOptions like model/agentCmd.
|
|
2474
2600
|
identity: config.identity,
|
|
2475
|
-
// `do
|
|
2601
|
+
// `do spec:<slug>` tasking-gate policy (the task-build path ignores it).
|
|
2476
2602
|
autoTask: config.autoTask,
|
|
2477
2603
|
// The resolved `promptGuidance` NUDGE namespace (e.g. `testFirst`),
|
|
2478
2604
|
// threaded into the worker prompt by performDo → buildAgentPrompt so a
|
|
@@ -2485,11 +2611,11 @@ export function buildProgram(): Command {
|
|
|
2485
2611
|
// typed `--merge` (`flagMode`), never when `merge` came from config — so an
|
|
2486
2612
|
// untrusted-origin task still forces propose under a config `merge`.
|
|
2487
2613
|
explicitMerge: flagMode === 'merge',
|
|
2488
|
-
// Per-TRANSITION TASKING override: the `do
|
|
2614
|
+
// Per-TRANSITION TASKING override: the `do spec:` tasking path threads
|
|
2489
2615
|
// `taskingIntegration ?? integration`; the task-build path stays on
|
|
2490
2616
|
// `integration`. Unset ⇒ tasking falls back to `integration` (today's behaviour).
|
|
2491
2617
|
taskingIntegration: config.taskingIntegration,
|
|
2492
|
-
// TASK-PLACEMENT (`do
|
|
2618
|
+
// TASK-PLACEMENT (`do spec:` tasking output): the configured default rung +
|
|
2493
2619
|
// the EXPLICIT operator override `--tasks-land-in` (top of the precedence).
|
|
2494
2620
|
// `explicitTasksLandIn` is set ONLY when the flag was typed (mirrors
|
|
2495
2621
|
// `explicitMerge`), so an untrusted-origin staging force still wins under a
|
|
@@ -2502,7 +2628,7 @@ export function buildProgram(): Command {
|
|
|
2502
2628
|
verify: config.verify,
|
|
2503
2629
|
// Single-job build path: gate the REBASED tip (the default) unconditionally.
|
|
2504
2630
|
freshWorktreeGate: config.freshWorktreeGate,
|
|
2505
|
-
// Cross-job merge-serialiser CAS-retry cap (
|
|
2631
|
+
// Cross-job merge-serialiser CAS-retry cap (spec `land-time-reverify-and-
|
|
2506
2632
|
// parallel-merge-ceiling` Story 5 / Applied Answer q1 (a)) — resolved per-repo
|
|
2507
2633
|
// and threaded to `performComplete`→`performIntegration`.
|
|
2508
2634
|
mergeRetries: config.mergeRetries,
|
|
@@ -2526,7 +2652,7 @@ export function buildProgram(): Command {
|
|
|
2526
2652
|
reviewGate: config.review
|
|
2527
2653
|
? harnessReviewGate({harness, agentCmd: config.agentCmd})
|
|
2528
2654
|
: undefined,
|
|
2529
|
-
// The tasker IMPROVER loop on the `do
|
|
2655
|
+
// The tasker IMPROVER loop on the `do spec:` tasking path is ON by default
|
|
2530
2656
|
// (auto-tasking has no `verify` floor — the loop is the task path's quality
|
|
2531
2657
|
// engine). `--tasker-loop`/`--no-tasker-loop` gates wiring the seam;
|
|
2532
2658
|
// `taskerLoopMax`/`taskerLoopModel` resolve per-repo (flag > env > per-repo
|
|
@@ -2549,12 +2675,17 @@ export function buildProgram(): Command {
|
|
|
2549
2675
|
// `--watch`: tail the pi session log live (pi harness only; the
|
|
2550
2676
|
// performDo guard errors clearly on any other adapter). READ-ONLY.
|
|
2551
2677
|
watch: flags.watch === true,
|
|
2678
|
+
// The dorfl-internal agent deadline + anti-loop ceiling (spec
|
|
2679
|
+
// `graceful-pre-timeout-wip-checkpoint`). Threaded from the resolved
|
|
2680
|
+
// per-repo config so an edit to dorfl.json reflects on the NEXT tick.
|
|
2681
|
+
agentDeadlineMinutes: config.agentDeadlineMinutes,
|
|
2682
|
+
maxAutoCheckpoints: config.maxAutoCheckpoints,
|
|
2552
2683
|
color: shouldUseColor(process.stdout),
|
|
2553
2684
|
note: (message) => console.error(`>> ${message}`),
|
|
2554
2685
|
noteBlock: (message) => console.error(message),
|
|
2555
2686
|
};
|
|
2556
2687
|
|
|
2557
|
-
// `--allow-backlog` is EXPLICIT-SINGLE-TASK-ONLY (
|
|
2688
|
+
// `--allow-backlog` is EXPLICIT-SINGLE-TASK-ONLY (spec
|
|
2558
2689
|
// `do-allow-backlog-drive-staged-tasks-without-promotion`, decision 4): it
|
|
2559
2690
|
// drives ONE named staged task in place. It must NOT combine with the
|
|
2560
2691
|
// AUTO-PICK (zero-args / -n) or MULTI-ITEM forms — those select FROM the
|
|
@@ -2615,11 +2746,11 @@ export function buildProgram(): Command {
|
|
|
2615
2746
|
});
|
|
2616
2747
|
|
|
2617
2748
|
// `advance` — the SIBLING top-level verb (NOT a `do` subcommand; `do`
|
|
2618
|
-
// subcommands + a standalone `task` verb are REJECTED in
|
|
2749
|
+
// subcommands + a standalone `task` verb are REJECTED in spec `advance-loop`).
|
|
2619
2750
|
// It reuses the SAME shared `prefix:arg` resolver `do` uses, EXTENDED with the
|
|
2620
2751
|
// `obs:` namespace, and wires the classify → lock → execute SKELETON: classify
|
|
2621
2752
|
// the rung (read-only, no model, no lock), take the `advancing` CAS borrow, then
|
|
2622
|
-
// dispatch winner-only — build/task rungs ORCHESTRATE `do`/`do
|
|
2753
|
+
// dispatch winner-only — build/task rungs ORCHESTRATE `do`/`do spec:` (never a
|
|
2623
2754
|
// duplicate), surface/apply/triage dispatch to a named executor seam later
|
|
2624
2755
|
// tasks fill. The DRIVERS (one-shot/loop) + `-n` + per-action gates and the
|
|
2625
2756
|
// rung BODIES are LATER tasks; the bare eligible-SET form errors clearly here.
|
|
@@ -2627,11 +2758,11 @@ export function buildProgram(): Command {
|
|
|
2627
2758
|
.command('advance')
|
|
2628
2759
|
.helpGroup(HEADLINE_GROUP)
|
|
2629
2760
|
.description(
|
|
2630
|
-
'Advance work/ item(s) one lifecycle rung toward ready/built (
|
|
2761
|
+
'Advance work/ item(s) one lifecycle rung toward ready/built (SPEC advance-loop), the SEQUENTIAL one-shot driver over the advance tick. advance <slug> (bare = the task) | advance spec:<slug> (the spec tasking rung) | advance obs:<slug> (triage an observation) | advance (auto-pick one eligible) | advance <a> <b> (those, in sequence) | advance -n <x> (x eligible, in sequence). Each item: classify (read-only, no model, no lock) → take the `advancing` CAS lock → dispatch winner-only — build/task rungs ORCHESTRATE `do`/`do spec:`, surface/apply always run, triage respects observationTriage (off|ask|auto). The bare/`-n` selection respects the per-action gates (build→autoBuild, task→autoTask, triage→observationTriage); `-n` is ALWAYS sequential (parallelism is `run` / the CI matrix).',
|
|
2631
2762
|
)
|
|
2632
2763
|
.argument(
|
|
2633
2764
|
'[slugs...]',
|
|
2634
|
-
'the item(s) to advance: bare (= the task), task:<slug>, spec:<slug
|
|
2765
|
+
'the item(s) to advance: bare (= the task), task:<slug>, spec:<slug>, or obs:<slug> (an observation). Zero args = auto-pick one eligible; multiple = advance them in sequence.',
|
|
2635
2766
|
)
|
|
2636
2767
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
2637
2768
|
.option(
|
|
@@ -2656,15 +2787,15 @@ export function buildProgram(): Command {
|
|
|
2656
2787
|
)
|
|
2657
2788
|
.option(
|
|
2658
2789
|
'--surface-blockers',
|
|
2659
|
-
'the declared-blocked-work gate (the orthogonal peer of --observation-triage): render a task/
|
|
2790
|
+
'the declared-blocked-work gate (the orthogonal peer of --observation-triage): render a task/spec carrying needsAnswers:true into an answerable question sidecar (the needsAnswers-blocked pool is enumerated into auto-pick). Resolved flag > env > per-repo > global > default off. An explicit `advance <slug>`/`advance spec:<slug>` bypasses this selection gate and surfaces regardless. Does NOT gate apply (an answered sidecar still applies) or needs-attention (always on).',
|
|
2660
2791
|
)
|
|
2661
2792
|
.option(
|
|
2662
2793
|
'--no-surface-blockers',
|
|
2663
|
-
'leave a needsAnswers:true task/
|
|
2794
|
+
'leave a needsAnswers:true task/spec silently blocked (default; the blocked pool is dropped from auto-pick)',
|
|
2664
2795
|
)
|
|
2665
2796
|
.option(
|
|
2666
2797
|
'--strict-merge-approval',
|
|
2667
|
-
'opt in to the host-agnostic "dismiss stale approvals on base change" discipline (
|
|
2798
|
+
'opt in to the host-agnostic "dismiss stale approvals on base change" discipline (spec `land-time-reverify-and-parallel-merge-ceiling` sidecar OQ6): when the merge-base CHANGED between the human’s merge-answer and the apply step, RE-SURFACE the merge-question (clear the answer back to no-answer; re-author the question on main/runner under the advancing lock) instead of auto-landing on a green re-verify. Default OFF (a green re-verify is trusted as sufficient; honour the prior answer). Story #16’s RED-re-verify refusal is UNCHANGED and independent of this flag. Resolved flag > env > per-repo > global > default off.',
|
|
2668
2799
|
)
|
|
2669
2800
|
.option(
|
|
2670
2801
|
'--no-strict-merge-approval',
|
|
@@ -2684,7 +2815,7 @@ export function buildProgram(): Command {
|
|
|
2684
2815
|
)
|
|
2685
2816
|
.option(
|
|
2686
2817
|
'--tasks-land-in <where>',
|
|
2687
|
-
'where `advance
|
|
2818
|
+
'where `advance spec:<slug>` tasking output lands: `backlog` (staged) or `ready` (the agent POOL). The EXPLICIT operator override at the top of the placement precedence. Resolved flag > env (DORFL_TASKS_LAND_IN) > per-repo > global > built-in.',
|
|
2688
2819
|
)
|
|
2689
2820
|
.option(
|
|
2690
2821
|
'--watch',
|
|
@@ -2820,7 +2951,7 @@ export function buildProgram(): Command {
|
|
|
2820
2951
|
integration: remoteConfig.integration,
|
|
2821
2952
|
// EXPLICIT `--merge` override for the untrusted-origin build-propose rule.
|
|
2822
2953
|
explicitMerge: flagMode === 'merge',
|
|
2823
|
-
// Per-TRANSITION TASKING override (the isolated `do --remote
|
|
2954
|
+
// Per-TRANSITION TASKING override (the isolated `do --remote spec:` path).
|
|
2824
2955
|
taskingIntegration: remoteConfig.taskingIntegration,
|
|
2825
2956
|
// TASK-PLACEMENT: configured default + EXPLICIT `--tasks-land-in` override
|
|
2826
2957
|
// (set only when typed, mirroring `explicitMerge`).
|
|
@@ -2830,7 +2961,7 @@ export function buildProgram(): Command {
|
|
|
2830
2961
|
verify: remoteConfig.verify,
|
|
2831
2962
|
// Single-job build path: gate the REBASED tip (the default) unconditionally.
|
|
2832
2963
|
freshWorktreeGate: remoteConfig.freshWorktreeGate,
|
|
2833
|
-
// Cross-job merge-serialiser CAS-retry cap —
|
|
2964
|
+
// Cross-job merge-serialiser CAS-retry cap — spec
|
|
2834
2965
|
// `land-time-reverify-and-parallel-merge-ceiling` Story 5.
|
|
2835
2966
|
mergeRetries: remoteConfig.mergeRetries,
|
|
2836
2967
|
noPR: remoteConfig.noPR,
|
|
@@ -2962,7 +3093,7 @@ export function buildProgram(): Command {
|
|
|
2962
3093
|
// EXPLICIT `--merge` override for the untrusted-origin build-propose rule (a
|
|
2963
3094
|
// bare `advance` auto-pick passes no flag ⇒ unset ⇒ untrusted forces propose).
|
|
2964
3095
|
explicitMerge: flagMode === 'merge',
|
|
2965
|
-
// Per-TRANSITION TASKING override (the `do
|
|
3096
|
+
// Per-TRANSITION TASKING override (the `do spec:` tasking path threads
|
|
2966
3097
|
// `taskingIntegration ?? integration`; the build path stays on `integration`).
|
|
2967
3098
|
taskingIntegration: config.taskingIntegration,
|
|
2968
3099
|
// TASK-PLACEMENT: configured default + EXPLICIT `--tasks-land-in` override
|
|
@@ -2973,7 +3104,7 @@ export function buildProgram(): Command {
|
|
|
2973
3104
|
verify: config.verify,
|
|
2974
3105
|
// Single-job build path: gate the REBASED tip (the default) unconditionally.
|
|
2975
3106
|
freshWorktreeGate: config.freshWorktreeGate,
|
|
2976
|
-
// Cross-job merge-serialiser CAS-retry cap (
|
|
3107
|
+
// Cross-job merge-serialiser CAS-retry cap (spec `land-time-reverify-and-
|
|
2977
3108
|
// parallel-merge-ceiling` Story 5 / Applied Answer q1 (a)).
|
|
2978
3109
|
mergeRetries: config.mergeRetries,
|
|
2979
3110
|
noPR: config.noPR,
|
|
@@ -2995,6 +3126,10 @@ export function buildProgram(): Command {
|
|
|
2995
3126
|
taskReviewGate: config.review
|
|
2996
3127
|
? harnessTaskAcceptanceGate({harness, agentCmd: config.agentCmd})
|
|
2997
3128
|
: undefined,
|
|
3129
|
+
// The dorfl-internal agent deadline + anti-loop ceiling (spec
|
|
3130
|
+
// `graceful-pre-timeout-wip-checkpoint`). Threaded from resolved config.
|
|
3131
|
+
agentDeadlineMinutes: config.agentDeadlineMinutes,
|
|
3132
|
+
maxAutoCheckpoints: config.maxAutoCheckpoints,
|
|
2998
3133
|
color: shouldUseColor(process.stdout),
|
|
2999
3134
|
note: (message) => console.error(`>> ${message}`),
|
|
3000
3135
|
noteBlock: (message) => console.error(message),
|
|
@@ -3114,7 +3249,7 @@ export function buildProgram(): Command {
|
|
|
3114
3249
|
)
|
|
3115
3250
|
.option(
|
|
3116
3251
|
'--reap-stale-locks',
|
|
3117
|
-
'(with --ledger) OPT-IN: also CLEAR every
|
|
3252
|
+
'(with --ledger) OPT-IN: also CLEAR every ORPHAN lock the report finds via the SAME leased delete `release-lock` uses, so one command sweeps all orphaned locks instead of N hand-run release-locks. Reaps the `cleared-stale` class: a held `active` lock that is EITHER terminal on `<arbiter>/main` (stranded between the durable move and the release) OR non-terminal + SURFACED (`needsAnswers:true` + sidecar on `<arbiter>/main`) = the crash-window orphan the ordered bounce transition leaves when step 1 lands but step 2 never runs. A `kept-in-flight` (active + non-terminal + NOT surfaced — a healthy build) is NEVER reaped, even with this flag. A concurrent change to a lock ref makes its leased delete REJECT (reported), never --force. WITHOUT this flag `gc --ledger` stays report-only (fail-loud, deletes nothing).',
|
|
3118
3253
|
)
|
|
3119
3254
|
.option(
|
|
3120
3255
|
'--remote-branches',
|
|
@@ -3137,7 +3272,7 @@ export function buildProgram(): Command {
|
|
|
3137
3272
|
const config = resolveGlobalConfig(loadConfig(flags.config), {});
|
|
3138
3273
|
const workspacesDir = flags.workspace ?? config.workspacesDir;
|
|
3139
3274
|
|
|
3140
|
-
// The `gc`-STYLE ledger SWEEP (
|
|
3275
|
+
// The `gc`-STYLE ledger SWEEP (spec `ledger-integrity` story 3): a SEPARATE
|
|
3141
3276
|
// surface from the worktree reaper below — it REPORTS one-slug-one-folder
|
|
3142
3277
|
// violations in a repo's `work/` lifecycle ledger and NEVER deletes (a human
|
|
3143
3278
|
// resolves each). Distinct `work/`: the ledger, not the execution substrate.
|
|
@@ -3146,7 +3281,7 @@ export function buildProgram(): Command {
|
|
|
3146
3281
|
typeof flags.ledger === 'string' ? flags.ledger : process.cwd();
|
|
3147
3282
|
const result = sweepLedgerDuplicates(repoPath);
|
|
3148
3283
|
// The UNIFIED-LOCK stuck/orphaned-lock REPORT (task
|
|
3149
|
-
// `release-lock-verb-and-gc-stuck-report`,
|
|
3284
|
+
// `release-lock-verb-and-gc-stuck-report`, spec
|
|
3150
3285
|
// `ledger-status-per-item-lock-refs` US #12/#13/#14): generalises the
|
|
3151
3286
|
// advancing-marker report from advancing-only to the unified per-item
|
|
3152
3287
|
// lock. The locks live on the ARBITER ref (`refs/dorfl/lock/*`),
|
|
@@ -3159,13 +3294,16 @@ export function buildProgram(): Command {
|
|
|
3159
3294
|
// clearing (no auto-sweep; a human asserts a lock is dead via
|
|
3160
3295
|
// `release-lock`).
|
|
3161
3296
|
// OPT-IN SWEEP (`--reap-stale-locks`): the WRITE twin of the report. A
|
|
3162
|
-
// human asserting "clear the dead TERMINAL locks now": for
|
|
3163
|
-
//
|
|
3164
|
-
//
|
|
3165
|
-
//
|
|
3166
|
-
//
|
|
3167
|
-
//
|
|
3168
|
-
//
|
|
3297
|
+
// human asserting "clear the dead TERMINAL locks now": for BOTH
|
|
3298
|
+
// terminal-on-main orphan classes (task `reaper-reap-terminal-stuck-lock-orphans`;
|
|
3299
|
+
// ADR `ledger-status-on-per-item-lock-refs` § Addendum 2026-07-10) — the
|
|
3300
|
+
// stranded `cleared-stale` AND the stuck-terminal `cleared-stuck-terminal`
|
|
3301
|
+
// crash-orphan — perform the SAME leased delete `release-lock` / the
|
|
3302
|
+
// recovery use, so one command sweeps every orphaned terminal lock. A
|
|
3303
|
+
// `kept-stuck` (STUCK + NON-terminal) / `kept-in-flight` lock is NEVER
|
|
3304
|
+
// reaped (scope fence); a concurrent change makes a clear REJECT (reported
|
|
3305
|
+
// `lost`), never --force. WITHOUT the flag the surface below stays
|
|
3306
|
+
// report-only (fail-loud, deletes nothing).
|
|
3169
3307
|
if (flags.reapStaleLocks) {
|
|
3170
3308
|
const reap = await reapStaleItemLocks(
|
|
3171
3309
|
flags.cwd ?? repoPath,
|
|
@@ -3189,10 +3327,11 @@ export function buildProgram(): Command {
|
|
|
3189
3327
|
: formatLedgerSweep(result),
|
|
3190
3328
|
);
|
|
3191
3329
|
}
|
|
3192
|
-
// Fail-loud AFTER the sweep: a `kept-stuck` (
|
|
3193
|
-
// a `lost`/`error` (
|
|
3194
|
-
// needs attention; a clean sweep
|
|
3195
|
-
//
|
|
3330
|
+
// Fail-loud AFTER the sweep: a `kept-stuck` (STUCK + non-terminal —
|
|
3331
|
+
// rightly left for a human) or a `lost`/`error` (an orphan lock whose
|
|
3332
|
+
// leased delete lost the race) still needs attention; a clean sweep
|
|
3333
|
+
// that reaped every orphan lock (stale OR stuck-terminal) and left only
|
|
3334
|
+
// healthy in-flight holds exits 0.
|
|
3196
3335
|
process.exit(
|
|
3197
3336
|
result.duplicates.length > 0 || reapReportNeedsAttention(reap)
|
|
3198
3337
|
? 1
|
|
@@ -3231,14 +3370,16 @@ export function buildProgram(): Command {
|
|
|
3231
3370
|
// ALL are REPORTED here (never auto-deleted — no automatic sweep exists; a
|
|
3232
3371
|
// human clears a NAMED unified lock via `release-lock`).
|
|
3233
3372
|
//
|
|
3234
|
-
// SCOPED to the ATTENTION verdicts only (
|
|
3235
|
-
// `ledger-status-on-per-item-lock-refs
|
|
3236
|
-
// crash-orphaned lock, NOT every held one): a `kept-stuck`
|
|
3237
|
-
//
|
|
3238
|
-
//
|
|
3239
|
-
//
|
|
3240
|
-
//
|
|
3241
|
-
//
|
|
3373
|
+
// SCOPED to the ATTENTION verdicts only (spec US#14/#21, ADR
|
|
3374
|
+
// `ledger-status-on-per-item-lock-refs` § Addendum 2026-07-10: this surface
|
|
3375
|
+
// is the STUCK / crash-orphaned lock, NOT every held one): a `kept-stuck`
|
|
3376
|
+
// (STUCK + NON-terminal — the genuine human-attention case), a
|
|
3377
|
+
// `cleared-stale`-eligible (terminal + stale active = stranded orphan), or
|
|
3378
|
+
// a `cleared-stuck-terminal`-eligible (terminal + stuck = crash-orphan the
|
|
3379
|
+
// reaper can now auto-clear) fails loud, but a `kept-in-flight` (active,
|
|
3380
|
+
// non-terminal) lock is the NORMAL in-flight state of a healthy concurrent
|
|
3381
|
+
// build (read by `status` as healthy) — it is reported informationally
|
|
3382
|
+
// and does NOT make a routine `gc --ledger` health check exit non-zero.
|
|
3242
3383
|
process.exit(
|
|
3243
3384
|
result.duplicates.length > 0 ||
|
|
3244
3385
|
itemLockReportNeedsAttention(lockReport)
|
|
@@ -3261,7 +3402,7 @@ export function buildProgram(): Command {
|
|
|
3261
3402
|
dryRun: flags.dryRun === true,
|
|
3262
3403
|
note: (message) => console.error(`>> ${message}`),
|
|
3263
3404
|
});
|
|
3264
|
-
// The ORPHAN-SIDECAR sweep (
|
|
3405
|
+
// The ORPHAN-SIDECAR sweep (spec
|
|
3265
3406
|
// `agentic-question-resolution-retire-disposition-vocabulary`, US #10) rides
|
|
3266
3407
|
// the SAME `--remote-branches` invocation the SCHEDULED CI lifecycle workflow
|
|
3267
3408
|
// runs (`dorfl gc --remote-branches --arbiter origin`) — so the reap of a
|
|
@@ -3385,7 +3526,7 @@ export function buildProgram(): Command {
|
|
|
3385
3526
|
}
|
|
3386
3527
|
|
|
3387
3528
|
// Exit non-zero on a REFUSAL (quiescence gate) or a non-green leak scan
|
|
3388
|
-
// (a converted tree that still carries a dangling
|
|
3529
|
+
// (a converted tree that still carries a dangling ''prd'' ref). A clean
|
|
3389
3530
|
// dry-run or a green migration exits 0.
|
|
3390
3531
|
if (result.refused || result.leaks.length > 0) {
|
|
3391
3532
|
process.exit(1);
|
|
@@ -3497,7 +3638,7 @@ export function buildProgram(): Command {
|
|
|
3497
3638
|
.command('requeue <slug>')
|
|
3498
3639
|
.helpGroup(HEADLINE_GROUP)
|
|
3499
3640
|
.description(
|
|
3500
|
-
'Requeue a STUCK task to the backlog for re-claiming (ADR §12/§14). Recovers a task whose per-item lock is held — stuck (the resolved-recovery path: a previously-routed needs-attention item, now lock `state: stuck`) OR active (a claim that never surfaced — an un-surfaced abort, a killed run, or an in-place requeue note). The body rests in work/tasks/backlog/<slug>.md (claim never moves it under the per-item-lock model); requeue releases the lock so the item is claimable again. The release is published as a TREE-LESS compare-and-swap to the arbiter ref, EXACTLY like claim — it NEVER stages or commits in the cwd working tree, so a requeue in a shared checkout can never sweep up a concurrent writer’s uncommitted files. DEFAULT = keep + continue: leave the work/<slug> branch UNTOUCHED so the next claim CONTINUES from its tip (rebased onto fresh main at onboard-time). --reset =
|
|
3641
|
+
'Requeue a STUCK task to the backlog for re-claiming (ADR §12/§14). Recovers a task whose per-item lock is held — stuck (the resolved-recovery path: a previously-routed needs-attention item, now lock `state: stuck`) OR active (a claim that never surfaced — an un-surfaced abort, a killed run, or an in-place requeue note). The body rests in work/tasks/backlog/<slug>.md (claim never moves it under the per-item-lock model); requeue releases the lock so the item is claimable again. The release is published as a TREE-LESS compare-and-swap to the arbiter ref, EXACTLY like claim — it NEVER stages or commits in the cwd working tree, so a requeue in a shared checkout can never sweep up a concurrent writer’s uncommitted files. Escalation ladder (least to most destructive): (1) DEFAULT = keep + continue: leave the work/<slug> branch UNTOUCHED so the next claim CONTINUES from its tip (rebased onto fresh main at onboard-time); if the work/<slug> branch is not on the arbiter (never pushed, or a prior --reset already deleted it), default requeue succeeds anyway with a fresh-claim note. (2) --reconcile (alias --rebase) = NON-DESTRUCTIVE recovery: re-sync the mirror to the arbiter (prune-fetch) and RETRY the rebase of the kept branch onto latest arbiter/main in a scratch worktree; on a clean rebase, push the reconciled tip back (--force-with-lease, work branch only) and release the lock; on a genuine content conflict AFTER the clean re-sync, leave the item stuck and the branch UNTOUCHED (nothing deleted) so the human can retry when the churn settles. NEVER deletes the remote branch. (3) --reset = DESTRUCTIVE last resort: delete the remote work/<slug> branch FIRST (then release the lock) so the next claim starts fresh (guarded; never the default). -m/--message appends a dated handoff note to the item body (all modes; append-only).',
|
|
3501
3642
|
)
|
|
3502
3643
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
3503
3644
|
.option(
|
|
@@ -3508,16 +3649,21 @@ export function buildProgram(): Command {
|
|
|
3508
3649
|
'--arbiter <remote>',
|
|
3509
3650
|
'the arbiter git remote the tree-less move is CAS-published to (default: origin). --cwd resolves this remote; the move is never written to the cwd tree.',
|
|
3510
3651
|
)
|
|
3652
|
+
.option(
|
|
3653
|
+
'--reconcile',
|
|
3654
|
+
'NON-DESTRUCTIVE recovery (middle rung, between the default keep+continue and the destructive --reset): re-sync the mirror to the arbiter (prune-fetch) and RETRY the rebase of the kept work/<slug> onto latest arbiter/main in a scratch worktree; on success, push the reconciled tip back with --force-with-lease and release the lock; on a genuine content conflict after the clean re-sync, leave the item stuck and the branch UNTOUCHED (nothing deleted). NEVER deletes the remote branch. Incompatible with --reset.',
|
|
3655
|
+
)
|
|
3656
|
+
.option('--rebase', 'alias of --reconcile (canonical name)')
|
|
3511
3657
|
.option(
|
|
3512
3658
|
'--reset',
|
|
3513
|
-
'DISCARD the kept work
|
|
3659
|
+
'DESTRUCTIVE last resort: DISCARD the kept work by deleting the remote work/<slug> branch FIRST, then move to backlog so the next claim starts FRESH (guarded; a deliberate departure from the never-delete-the-remote-branch invariant). Never the default; try --reconcile first.',
|
|
3514
3660
|
)
|
|
3515
3661
|
.option(
|
|
3516
3662
|
'-m, --message <note>',
|
|
3517
3663
|
'append a dated handoff note to the item body for the next agent (append-only; applies to both default and --reset)',
|
|
3518
3664
|
)
|
|
3519
3665
|
.action(async (rawSlug: string, flags: RequeueFlags) => {
|
|
3520
|
-
// Task-only command (§3a): accept bare + `task:`, reject `
|
|
3666
|
+
// Task-only command (§3a): accept bare + `task:`, reject `spec:`.
|
|
3521
3667
|
const slug = resolveTaskOnlySlug(rawSlug) as string;
|
|
3522
3668
|
const cwd = flags.cwd ?? process.cwd();
|
|
3523
3669
|
// Route the requeue (default keep+continue / --reset discard / -m handoff)
|
|
@@ -3532,6 +3678,14 @@ export function buildProgram(): Command {
|
|
|
3532
3678
|
// call site, rather than relying on the seam's silent `?? process.env`
|
|
3533
3679
|
// default by omission (the implicit fallback that made `requeue`'s human
|
|
3534
3680
|
// attribution accidental rather than declared).
|
|
3681
|
+
const reconcile = flags.reconcile === true || flags.rebase === true;
|
|
3682
|
+
if (reconcile && flags.reset) {
|
|
3683
|
+
console.error(
|
|
3684
|
+
'error: --reconcile and --reset are mutually exclusive ' +
|
|
3685
|
+
'(non-destructive recovery vs destructive last resort). Pick one.',
|
|
3686
|
+
);
|
|
3687
|
+
process.exit(1);
|
|
3688
|
+
}
|
|
3535
3689
|
const result = await ledgerWrite.applyReturnToBacklogTransition({
|
|
3536
3690
|
cwd,
|
|
3537
3691
|
slug,
|
|
@@ -3540,6 +3694,7 @@ export function buildProgram(): Command {
|
|
|
3540
3694
|
// `--cwd` is purely the ORIGIN SOURCE the remote is resolved from.
|
|
3541
3695
|
arbiter: flags.arbiter ?? 'origin',
|
|
3542
3696
|
reset: flags.reset,
|
|
3697
|
+
reconcile,
|
|
3543
3698
|
message: flags.message,
|
|
3544
3699
|
env: process.env,
|
|
3545
3700
|
note: (message) => console.error(`>> ${message}`),
|
|
@@ -3550,16 +3705,18 @@ export function buildProgram(): Command {
|
|
|
3550
3705
|
}
|
|
3551
3706
|
const how = result.deletedRemoteBranch
|
|
3552
3707
|
? ` (--reset: deleted the remote ${workBranchRef('task', slug)} branch; next claim starts fresh)`
|
|
3553
|
-
:
|
|
3708
|
+
: result.reconciled
|
|
3709
|
+
? ` (--reconcile: re-synced mirror + rebased ${workBranchRef('task', slug)} onto latest main and pushed the reconciled tip back; next claim continues from it)`
|
|
3710
|
+
: ' (kept the work branch; next claim continues from its tip)';
|
|
3554
3711
|
console.log(`Requeued '${slug}' to backlog for re-claiming.${how}`);
|
|
3555
3712
|
});
|
|
3556
3713
|
|
|
3557
|
-
// `promote [item]` (
|
|
3714
|
+
// `promote [item]` (spec `staging-pool-position-gate-and-trust-model`, tasks
|
|
3558
3715
|
// `pre-backlog-staging-folder-and-promote-step-a` /
|
|
3559
3716
|
// `pre-prd-staging-pool-split-and-untrusted-prd-placement`): the HUMAN/runner-
|
|
3560
3717
|
// owned verb that moves a STAGED item into its agent-eligible POOL — a task
|
|
3561
|
-
// `work/
|
|
3562
|
-
// `work/
|
|
3718
|
+
// `work/tasks/backlog/<slug>.md → work/tasks/ready/<slug>.md`, a spec
|
|
3719
|
+
// `work/specs/proposed/<slug>.md → work/specs/ready/<slug>.md` — as a tree-less CAS on the
|
|
3563
3720
|
// arbiter, the SAME trust model + mechanism as `requeue`. The agent emits STAGED;
|
|
3564
3721
|
// only this verb (a human, or the runner) admits it to the pool. With NO argument
|
|
3565
3722
|
// it LISTS what is promotable (the "what is staged waiting for me?" discovery), so
|
|
@@ -3568,7 +3725,7 @@ export function buildProgram(): Command {
|
|
|
3568
3725
|
.command('promote [item]')
|
|
3569
3726
|
.helpGroup(HEADLINE_GROUP)
|
|
3570
3727
|
.description(
|
|
3571
|
-
|
|
3728
|
+
`Admit a STAGED item into its agent-eligible POOL (the runner/human side of the staging gate): a task \`${workFolderPrefix('tasks-backlog')}<slug>.md → ${workFolderPrefix('tasks-ready')}<slug>.md\`, a spec \`${workFolderPrefix('specs-proposed')}<slug>.md → ${workFolderPrefix('specs-ready')}<slug>.md\`, published as a TREE-LESS compare-and-swap to the arbiter ref (EXACTLY like requeue/claim — it never stages/commits in the cwd tree). The agent only ever CREATES staged; this verb is the gate a human (or the runner) opens. Accepts \`task:<slug>\` / \`spec:<slug>\` / a bare \`<slug>\` (= task). With NO argument, LISTS every promotable item (the tasks in ${workFolderPrefix('tasks-backlog')} + the specs in ${workFolderPrefix('specs-proposed')} on the arbiter) so you can see what is staged waiting for promotion. Idempotent: promoting an already-pooled slug is a clean no-op success.`,
|
|
3572
3729
|
)
|
|
3573
3730
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
3574
3731
|
.option(
|
|
@@ -3596,7 +3753,7 @@ export function buildProgram(): Command {
|
|
|
3596
3753
|
}
|
|
3597
3754
|
if (listed.items.length === 0) {
|
|
3598
3755
|
console.log(
|
|
3599
|
-
`Nothing staged to promote on ${arbiter}/main (
|
|
3756
|
+
`Nothing staged to promote on ${arbiter}/main (${workFolderPrefix('tasks-backlog')} and ${workFolderPrefix('specs-proposed')} are empty).`,
|
|
3600
3757
|
);
|
|
3601
3758
|
return;
|
|
3602
3759
|
}
|
|
@@ -3607,11 +3764,10 @@ export function buildProgram(): Command {
|
|
|
3607
3764
|
return;
|
|
3608
3765
|
}
|
|
3609
3766
|
|
|
3610
|
-
// AN ITEM → promote it. `task:`/`spec:` are explicit
|
|
3611
|
-
//
|
|
3612
|
-
//
|
|
3613
|
-
//
|
|
3614
|
-
// no pool).
|
|
3767
|
+
// AN ITEM → promote it. `task:`/`spec:` are explicit; a bare slug defaults to
|
|
3768
|
+
// a task (mirrors `requeue`). An `obs:`/`observation:` prefix is rejected
|
|
3769
|
+
// (observations have no pool). A legacy ''prd:'' prefix is a DEAD namespace token
|
|
3770
|
+
// after the hard cutover — it resolves as a bare literal task slug, not a spec.
|
|
3615
3771
|
const parsed = parseSlugArg(rawItem);
|
|
3616
3772
|
if (parsed.explicit === 'observation') {
|
|
3617
3773
|
console.error(
|
|
@@ -3648,7 +3804,7 @@ export function buildProgram(): Command {
|
|
|
3648
3804
|
// `action: advance` on the UNIFIED per-item lock, so `release-lock <item>` (below)
|
|
3649
3805
|
// is the SOLE named human release for ALL holds (implement/task/advance).
|
|
3650
3806
|
|
|
3651
|
-
// `release-lock <item>` (task `release-lock-verb-and-gc-stuck-report`,
|
|
3807
|
+
// `release-lock <item>` (task `release-lock-verb-and-gc-stuck-report`, spec
|
|
3652
3808
|
// `ledger-status-per-item-lock-refs` US #14): the HUMAN-invoked named release of
|
|
3653
3809
|
// a stuck/orphaned UNIFIED per-item lock (`refs/dorfl/lock/<entry>`) —
|
|
3654
3810
|
// the GENERALISATION of `release-advancing` from the advancing-only marker to
|
|
@@ -3661,13 +3817,29 @@ export function buildProgram(): Command {
|
|
|
3661
3817
|
// Idempotent: deleting an absent ref is a clean exit-0 "nothing to clear"
|
|
3662
3818
|
// (`not-held`), NOT a failure — deleting the lock ref(s) is "all locks released"
|
|
3663
3819
|
// and recoverable (the work is safe on the `work/<slug>` branches + `main`).
|
|
3820
|
+
//
|
|
3821
|
+
// ESCAPE HATCH (`--entry <literal>`, task
|
|
3822
|
+
// `release-lock-entry-escape-hatch-and-literal-entry-reporting`): the item
|
|
3823
|
+
// positional is OPTIONAL because a lock whose entry name is NOT derivable from
|
|
3824
|
+
// any current item-form (a PRE-CUTOVER `slice-<slug>` / `prd-<slug>` entry minted
|
|
3825
|
+
// before the slice→task / `prd-to-spec` vocabulary cutover) is UN-NAMEABLE through
|
|
3826
|
+
// the item-form path. `--entry` takes the literal `<entry>` VERBATIM (bypassing
|
|
3827
|
+
// the namespace mapping) so a human can still NAME + clear such an orphan without
|
|
3828
|
+
// the raw `git push origin --delete refs/dorfl/lock/…` plumbing. It reuses the
|
|
3829
|
+
// SAME leased-delete path (`releaseLiteralLockEntry` → the shared entry-keyed
|
|
3830
|
+
// core), so the trust model is unchanged: the human still asserts liveness.
|
|
3831
|
+
// EXACTLY ONE of {item positional, --entry} must be given.
|
|
3664
3832
|
program
|
|
3665
|
-
.command('release-lock
|
|
3833
|
+
.command('release-lock [item]')
|
|
3666
3834
|
.helpGroup(HEADLINE_GROUP)
|
|
3667
3835
|
.description(
|
|
3668
|
-
'Clear a NAMED stuck/orphaned UNIFIED per-item lock (refs/dorfl/lock/<entry>) by DELETING the ref on the arbiter — the recovery verb for a lock the system orphaned (a crashed build/task/advance that left the hold behind). The generalisation of `release-advancing` from the advancing marker to the ONE lock per item. Same trust model as `requeue`: a HUMAN asserts the lock is dead by NAMING it; the tool never guesses liveness (the lock has NO heartbeat, so there is NO automatic sweep / age-based reaper anywhere). Accepts the same item forms as the lock API: `task:<slug>` / `
|
|
3836
|
+
'Clear a NAMED stuck/orphaned UNIFIED per-item lock (refs/dorfl/lock/<entry>) by DELETING the ref on the arbiter — the recovery verb for a lock the system orphaned (a crashed build/task/advance that left the hold behind). The generalisation of `release-advancing` from the advancing marker to the ONE lock per item. Same trust model as `requeue`: a HUMAN asserts the lock is dead by NAMING it; the tool never guesses liveness (the lock has NO heartbeat, so there is NO automatic sweep / age-based reaper anywhere). Accepts the same item forms as the lock API: `task:<slug>` / `spec:<slug>` / `obs:<slug>` / a bare `<slug>` (= task). ESCAPE HATCH: `--entry <literal>` (no item positional) targets a LITERAL entry name directly, bypassing the namespace mapping — for a lock whose entry name is not derivable from any current item-form (a pre-vocabulary-cutover `slice-<slug>` / `prd-<slug>` prefix, or a future rename); this is the supported way to clear such orphans, so the raw `git push origin --delete refs/dorfl/lock/…` plumbing is no longer required. Idempotent — re-running on an already-cleared lock is a clean exit-0 no-op (deleting the lock ref is “all locks released”, recoverable). NEVER `--force`. Discoverable via `gc --ledger` (it REPORTS every lingering lock and, for a pre-cutover entry, the exact `release-lock --entry <literal>` invocation).',
|
|
3669
3837
|
)
|
|
3670
3838
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
3839
|
+
.option(
|
|
3840
|
+
'--entry <literal>',
|
|
3841
|
+
'ESCAPE HATCH: the LITERAL lock-entry name (`refs/dorfl/lock/<literal>`) to release VERBATIM, bypassing the namespace mapping — for a lock whose entry name is not derivable from any current item-form (a pre-cutover `slice-<slug>` / `prd-<slug>` entry). Mutually exclusive with the item positional (give EXACTLY one). Validated to `[A-Za-z0-9._-]+` (no slashes/whitespace) so it cannot escape the lock namespace.',
|
|
3842
|
+
)
|
|
3671
3843
|
.option(
|
|
3672
3844
|
'--cwd <dir>',
|
|
3673
3845
|
'the repo/working clone whose arbiter remote the lock ref is DELETED on (default: cwd)',
|
|
@@ -3676,26 +3848,54 @@ export function buildProgram(): Command {
|
|
|
3676
3848
|
'--arbiter <remote>',
|
|
3677
3849
|
'the arbiter git remote the lock ref is deleted on (default: origin)',
|
|
3678
3850
|
)
|
|
3679
|
-
.action(async (item: string, flags: ReleaseLockFlags) => {
|
|
3851
|
+
.action(async (item: string | undefined, flags: ReleaseLockFlags) => {
|
|
3680
3852
|
const cwd = flags.cwd ?? process.cwd();
|
|
3681
3853
|
const arbiter = flags.arbiter ?? 'origin';
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3854
|
+
// MUTUAL EXCLUSION: exactly one of {item positional, --entry}. BOTH or
|
|
3855
|
+
// NEITHER is a usage error (non-zero exit, actionable message) — the
|
|
3856
|
+
// item-form and the literal escape hatch are two ways to name ONE lock, not
|
|
3857
|
+
// combinable.
|
|
3858
|
+
if (item !== undefined && flags.entry !== undefined) {
|
|
3859
|
+
console.error(
|
|
3860
|
+
'error: give EITHER an item (task:<slug> / spec:<slug> / obs:<slug> / <slug>) OR --entry <literal>, not both.',
|
|
3861
|
+
);
|
|
3862
|
+
process.exit(1);
|
|
3863
|
+
}
|
|
3864
|
+
if (item === undefined && flags.entry === undefined) {
|
|
3865
|
+
console.error(
|
|
3866
|
+
'error: name the lock to release — an item (task:<slug> / spec:<slug> / obs:<slug> / <slug>) OR --entry <literal> for a pre-cutover entry with no current item-form.',
|
|
3867
|
+
);
|
|
3868
|
+
process.exit(1);
|
|
3869
|
+
}
|
|
3870
|
+
// The LITERAL escape-hatch path (`--entry`): take the entry name verbatim,
|
|
3871
|
+
// bypassing the namespace mapping, and reuse the SAME leased-delete core.
|
|
3872
|
+
const result =
|
|
3873
|
+
flags.entry !== undefined
|
|
3874
|
+
? await releaseLiteralLockEntry({
|
|
3875
|
+
entry: flags.entry,
|
|
3876
|
+
cwd,
|
|
3877
|
+
arbiter,
|
|
3878
|
+
env: process.env,
|
|
3879
|
+
})
|
|
3880
|
+
: await releaseItemLock({
|
|
3881
|
+
item: item as string,
|
|
3882
|
+
cwd,
|
|
3883
|
+
arbiter,
|
|
3884
|
+
env: process.env,
|
|
3885
|
+
});
|
|
3688
3886
|
if (result.outcome === 'released') {
|
|
3689
3887
|
console.log(
|
|
3690
3888
|
`Released lock '${result.entry}' (${result.ref} deleted on ${arbiter}; the item itself was untouched — it rests on main / its work/<slug> branch).`,
|
|
3691
3889
|
);
|
|
3692
3890
|
return;
|
|
3693
3891
|
}
|
|
3694
|
-
// IDEMPOTENT exit semantics:
|
|
3892
|
+
// IDEMPOTENT exit semantics: the release returns `not-held` when the
|
|
3695
3893
|
// ref is ALREADY absent. For a HUMAN re-running the verb on an
|
|
3696
3894
|
// already-cleared lock that is the CORRECT "nothing to clear" outcome —
|
|
3697
3895
|
// deleting the lock ref(s) is "all locks released" and recoverable — so map
|
|
3698
|
-
// it to a clean exit-0 with an honest message (NOT a failure).
|
|
3896
|
+
// it to a clean exit-0 with an honest message (NOT a failure). The message
|
|
3897
|
+
// names the LITERAL entry (`refs/dorfl/lock/<entry>`) so an operator can
|
|
3898
|
+
// copy-paste it straight back into a `--entry` invocation.
|
|
3699
3899
|
if (result.outcome === 'not-held') {
|
|
3700
3900
|
console.log(
|
|
3701
3901
|
`No lock to release for '${result.entry}' (${result.ref} is already absent on ${arbiter} — “all locks released”, recoverable).`,
|
|
@@ -3706,12 +3906,56 @@ export function buildProgram(): Command {
|
|
|
3706
3906
|
process.exit(1);
|
|
3707
3907
|
});
|
|
3708
3908
|
|
|
3709
|
-
// `
|
|
3909
|
+
// `migrate-stuck-locks` (spec `surface-stuck-as-questions-and-retire-stuck-lock-state`,
|
|
3910
|
+
// resolved decision #3, user story 5; task `migrate-existing-stuck-locks-one-shot`):
|
|
3911
|
+
// the ONE-SHOT ROLLOUT migration that converts every pre-existing `stuck` lock
|
|
3912
|
+
// ref (an item bounced under the OLD model, before `retire-stuck-lock-state`)
|
|
3913
|
+
// into the new resting shape (`needsAnswers:true` + `stuck`-kind sidecar on
|
|
3914
|
+
// `<arbiter>/main` + lock released) via the SAME surface-first-release-second
|
|
3915
|
+
// transition the new bounce uses. Idempotent (a re-run finds no legacy stuck
|
|
3916
|
+
// ref → clean no-op) and bare-arbiter-safe (a ref is a ref). Its own verb (not
|
|
3917
|
+
// folded into `gc --ledger`) because it is a one-shot rollout event with WRITE
|
|
3918
|
+
// semantics on `main`, distinct from `gc`'s report-oriented + terminal-orphan
|
|
3919
|
+
// reap surface — the two are different natures and their exit-code contracts
|
|
3920
|
+
// stay separate.
|
|
3921
|
+
program
|
|
3922
|
+
.command('migrate-stuck-locks')
|
|
3923
|
+
.helpGroup(ADVANCED_GROUP)
|
|
3924
|
+
.description(
|
|
3925
|
+
'ONE-SHOT rollout migration: convert every pre-existing `stuck` per-item lock ref (`refs/dorfl/lock/<entry>` written under the retired `stuck` lock state) into the new resting shape — a `needsAnswers:true` item body + a `stuck`-kind `work/questions/<entry>.md` sidecar on `<arbiter>/main`, with the lock ref released — via the SAME surface-first-release-second transition a fresh bounce uses. Idempotent: re-running finds no legacy `stuck` ref (the current lock module never writes `state: stuck`) and is a clean no-op. Bare-arbiter safe (a ref is a ref). Skips a pre-cutover `slice-*` / `prd-*` entry with no current item-form (clear those via `release-lock --entry <literal>`) and NEVER --force-deletes a lock. Exits non-zero only when a surface CAS lost the race or a read/plumbing fault prevented migration.',
|
|
3926
|
+
)
|
|
3927
|
+
.option(
|
|
3928
|
+
'--cwd <dir>',
|
|
3929
|
+
'the local repo/clone whose --arbiter remote the lock refs are read + mutated on (default: cwd); the working tree is never touched (the surface transition is tree-less)',
|
|
3930
|
+
)
|
|
3931
|
+
.option(
|
|
3932
|
+
'--arbiter <remote>',
|
|
3933
|
+
'the arbiter git remote the migration reads + mutates (default: origin)',
|
|
3934
|
+
)
|
|
3935
|
+
.action(async (flags: {cwd?: string; arbiter?: string}) => {
|
|
3936
|
+
const cwd = flags.cwd ?? process.cwd();
|
|
3937
|
+
const arbiter = flags.arbiter ?? 'origin';
|
|
3938
|
+
const note = (message: string) => console.error(`>> ${message}`);
|
|
3939
|
+
const report = await migrateStuckLocks({
|
|
3940
|
+
cwd,
|
|
3941
|
+
arbiter,
|
|
3942
|
+
env: process.env,
|
|
3943
|
+
note,
|
|
3944
|
+
});
|
|
3945
|
+
for (const line of formatMigrateStuckLocksReport(report)) {
|
|
3946
|
+
console.log(line);
|
|
3947
|
+
}
|
|
3948
|
+
if (migrateStuckLocksNeedsAttention(report)) {
|
|
3949
|
+
process.exit(1);
|
|
3950
|
+
}
|
|
3951
|
+
});
|
|
3952
|
+
|
|
3953
|
+
// `drop <slug>` (spec `agentic-question-resolution-retire-disposition-vocabulary`,
|
|
3710
3954
|
// US #5/#11; task `direct-delete-question-cli-helper`): the DIRECT "throw it
|
|
3711
3955
|
// away" verb — `git rm` a source item AND its question sidecar (when present) in
|
|
3712
3956
|
// ONE revertible commit, the reason in the commit MESSAGE (git history is the
|
|
3713
3957
|
// archive). It does NOT round-trip through the decision engine or spawn an agent
|
|
3714
|
-
// (that is the SEPARATE agentic `
|
|
3958
|
+
// (that is the SEPARATE agentic `dispose` verdict in apply-persist.ts);
|
|
3715
3959
|
// this is the human/skill/CLI no-ceremony delete of decision 7. DISTINCT from
|
|
3716
3960
|
// the existing `remote rm` (the hub-MIRROR deleter) — different concern, no
|
|
3717
3961
|
// collision. A LOCAL one-commit primitive over the working tree (like apply): it
|
|
@@ -3721,9 +3965,8 @@ export function buildProgram(): Command {
|
|
|
3721
3965
|
.command('drop <slug>')
|
|
3722
3966
|
.helpGroup(HEADLINE_GROUP)
|
|
3723
3967
|
.description(
|
|
3724
|
-
'DIRECTLY delete a source item + its question sidecar (when present) in ONE revertible commit — the "I just want to throw this away" path that does NOT round-trip through the decision engine or any agent. Resolves the source by its namespaced identity (`task:<slug>` / `
|
|
3968
|
+
'DIRECTLY delete a source item + its question sidecar (when present) in ONE revertible commit — the "I just want to throw this away" path that does NOT round-trip through the decision engine or any agent. Resolves the source by its namespaced identity (`task:<slug>` / `spec:<slug>` / `obs:<slug>` / a bare `<slug>` = task), `git rm`s the source AND its sidecar together, and records your --reason in the commit MESSAGE (git history is the archive). A single revertible commit, so a wrong delete is recoverable via `git revert`. DISTINCT from `remote rm` (the hub-mirror deleter). A LOCAL working-tree commit (like the apply rung); it does not touch the arbiter — push/integrate it as you normally would. If the named source is already gone it is a clean no-op (nothing to throw away).',
|
|
3725
3969
|
)
|
|
3726
|
-
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
3727
3970
|
.option(
|
|
3728
3971
|
'--cwd <dir>',
|
|
3729
3972
|
'the working clone the revertible delete commit is made in (default: cwd)',
|
|
@@ -3767,7 +4010,7 @@ export function buildProgram(): Command {
|
|
|
3767
4010
|
.command('intake')
|
|
3768
4011
|
.helpGroup(HEADLINE_GROUP)
|
|
3769
4012
|
.description(
|
|
3770
|
-
|
|
4013
|
+
`Front-of-funnel: turn a GitHub issue into the right work/ artifact. Reads issue #N + its comment thread via the issue seam (gh), runs a prompt→verdict decision, and dispatches it: a clear, small issue → a proposed ${workFolderPrefix('tasks-ready')}<slug>.md PR carrying an \`issue: N\` closure link (read by a future CI close-job; not \`Fixes #N\`). GATE-FREE — your explicit invocation IS the authorization (autoTask/autoBuild do NOT apply), exactly as \`do\`. A LOCAL one-shot AND the SAME command CI schedules. PER-OUTCOME integration modes (the artifact TYPE is decided at runtime): --merge/--propose set BOTH; --merge-spec/--propose-spec and --merge-task/--propose-task override per type; granular overrides the aggregate; unset ⇒ propose for both.`,
|
|
3771
4014
|
)
|
|
3772
4015
|
.argument(
|
|
3773
4016
|
'<number>',
|
|
@@ -3808,7 +4051,7 @@ export function buildProgram(): Command {
|
|
|
3808
4051
|
)
|
|
3809
4052
|
.option(
|
|
3810
4053
|
'--origin-trust <trusted|untrusted>',
|
|
3811
|
-
"the author-trust verdict to STAMP onto the emitted
|
|
4054
|
+
"the author-trust verdict to STAMP onto the emitted spec/task (origin: issue + originTrust: <value>), so an untrusted origin survives the merge boundary and later forces the task's BUILD transition to propose. CI's intake.yml derives it from the SAME author_association case as the integration flags. UNSET (a local intake) ⇒ emitted unstamped (human/trusted) — the human running intake IS the checkpoint.",
|
|
3812
4055
|
)
|
|
3813
4056
|
.option(
|
|
3814
4057
|
'--specs-land-in <where>',
|
|
@@ -3859,7 +4102,7 @@ export function buildProgram(): Command {
|
|
|
3859
4102
|
console.error(`>> ${resolved.message}`);
|
|
3860
4103
|
}
|
|
3861
4104
|
const config = resolved.config;
|
|
3862
|
-
// Resolve the PER-OUTCOME integration modes (
|
|
4105
|
+
// Resolve the PER-OUTCOME integration modes (spec US #9): `intake` decides
|
|
3863
4106
|
// the artifact TYPE at runtime, so a single --merge/--propose can't express
|
|
3864
4107
|
// a type-conditional policy. The granular flags override the aggregate; an
|
|
3865
4108
|
// UNSET type falls back to the per-repo/global `integration` (the SAME chain
|
|
@@ -3916,6 +4159,13 @@ export function buildProgram(): Command {
|
|
|
3916
4159
|
cwd,
|
|
3917
4160
|
arbiter: flags.arbiter ?? config.defaultArbiter,
|
|
3918
4161
|
integration: modes,
|
|
4162
|
+
// The resolved cross-job CAS-retry cap (config `mergeRetries`). `intake`
|
|
4163
|
+
// has no `--merge-retries` flag today (task
|
|
4164
|
+
// `thread-merge-retries-cross-task-and-ratify-default` deliberately does
|
|
4165
|
+
// NOT add one — intake is unattended-CI-first, env + per-repo cover the
|
|
4166
|
+
// need); env + per-repo + global fold into `config.mergeRetries` via the
|
|
4167
|
+
// SAME chain, and unset falls through to the engine default.
|
|
4168
|
+
mergeRetries: config.mergeRetries,
|
|
3919
4169
|
// The origin-trust stamp the CI shell passes IN (unset ⇒ unstamped).
|
|
3920
4170
|
originTrust,
|
|
3921
4171
|
noPR: config.noPR,
|
|
@@ -3941,10 +4191,10 @@ export function buildProgram(): Command {
|
|
|
3941
4191
|
process.exit(result.exitCode);
|
|
3942
4192
|
});
|
|
3943
4193
|
|
|
3944
|
-
// The CI CLOSE-JOB driver (
|
|
4194
|
+
// The CI CLOSE-JOB driver (spec `runner-in-ci`, capability E; task
|
|
3945
4195
|
// `install-ci-close-job-workflow`). The thin JOB the emitted close-job workflow
|
|
3946
4196
|
// invokes on a merge to main: resolve which source issue(s) the landed work
|
|
3947
|
-
// closes (resolveClosingIssue), run the "
|
|
4197
|
+
// closes (resolveClosingIssue), run the "spec complete?" query for the spec case
|
|
3948
4198
|
// (prd-complete-query, done), and close via the IssueProvider seam — all
|
|
3949
4199
|
// UNCHANGED engine pieces, CONSUMED not re-built (the Out-of-Scope fence). CI
|
|
3950
4200
|
// owns ONLY the job + trigger. Local-runnable too (a manual catch-up close).
|
|
@@ -3952,7 +4202,7 @@ export function buildProgram(): Command {
|
|
|
3952
4202
|
.command('close-merged-issues')
|
|
3953
4203
|
.helpGroup(ADVANCED_GROUP)
|
|
3954
4204
|
.description(
|
|
3955
|
-
'Close source issues whose work has landed on main (CI capability E,
|
|
4205
|
+
'Close source issues whose work has landed on main (CI capability E, spec runner-in-ci). Resolves each closing issue from the work/ tree (resolveClosingIssue: a lone task closes its own `issue:`; a fanned task reaches the number via `task.spec: → spec issue:`), runs the existing "spec complete?" query for the spec case (closes ONLY when ALL its spec:<slug> tasks are in work/done/), and closes via the IssueProvider seam (atomic comment+close; NO direct gh). Re-implements NONE of the resolution/query/close — it WIRES them. Invoked by the emitted close-job workflow on a merge to main; DEGRADES (never crashes) on a missing/unauthenticated gh.',
|
|
3956
4206
|
)
|
|
3957
4207
|
.option(
|
|
3958
4208
|
'--cwd <dir>',
|
|
@@ -4108,7 +4358,7 @@ export function buildProgram(): Command {
|
|
|
4108
4358
|
.command('find <folder>')
|
|
4109
4359
|
.helpGroup(HEADLINE_GROUP)
|
|
4110
4360
|
.description(
|
|
4111
|
-
|
|
4361
|
+
`Discover work/-participating repos under <folder> (a populated ${workFolderPrefix('tasks-ready')}), then toggle-add the chosen ones via \`remote add\`. Interactive multi-select by default; --yes adds ALL discovered repos non-interactively.`,
|
|
4112
4362
|
)
|
|
4113
4363
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
4114
4364
|
.option('--yes', 'add all discovered participating repos (no prompt)')
|
|
@@ -4184,7 +4434,7 @@ export function buildProgram(): Command {
|
|
|
4184
4434
|
)
|
|
4185
4435
|
.option(
|
|
4186
4436
|
'--max-parallel <n>',
|
|
4187
|
-
'cap on CONCURRENT advance-lifecycle matrix legs (the propose/merge `max-parallel`). Each leg is a full agent session, so a large fan-out can exhaust the model provider rate limit + thrash the CAS. Default
|
|
4437
|
+
'cap on CONCURRENT advance-lifecycle matrix legs (the propose/merge `max-parallel`). Each leg is a full agent session, so a large fan-out can exhaust the model provider rate limit + thrash the CAS. Default 2.',
|
|
4188
4438
|
)
|
|
4189
4439
|
.action(async (flags: InstallCiFlags) => {
|
|
4190
4440
|
const workDir = flags.cwd ?? process.cwd();
|
|
@@ -4211,6 +4461,7 @@ export function buildProgram(): Command {
|
|
|
4211
4461
|
}
|
|
4212
4462
|
maxParallel = n;
|
|
4213
4463
|
}
|
|
4464
|
+
|
|
4214
4465
|
const ctx = new GitHubCIContext({
|
|
4215
4466
|
workDir,
|
|
4216
4467
|
repo: flags.repo,
|
|
@@ -4241,9 +4492,89 @@ export function buildProgram(): Command {
|
|
|
4241
4492
|
});
|
|
4242
4493
|
});
|
|
4243
4494
|
|
|
4495
|
+
// The `skills` group (ADR `skill-install-vendors-incur-agents-map`): install the
|
|
4496
|
+
// packaged dorfl skills (`from-idea`, `setup`, ...) into the OPERATOR's OWN
|
|
4497
|
+
// harness dirs. Distinct from any target-repo propagation (ADR
|
|
4498
|
+
// `methodology-and-skills` §6 — skills don't travel into target repos; only
|
|
4499
|
+
// `work/protocol/` does). Only `add` is exposed in this pass — `list`/`remove`
|
|
4500
|
+
// are deferred until we have a real use case (the vendored `install()` is
|
|
4501
|
+
// idempotent, so `add` alone covers upgrade + drift-repair; a future `remove`
|
|
4502
|
+
// would map onto the vendored `remove()` and is intentionally out of scope
|
|
4503
|
+
// here to keep the CLI surface minimal).
|
|
4504
|
+
const skills = program
|
|
4505
|
+
.command('skills')
|
|
4506
|
+
.helpGroup(HEADLINE_GROUP)
|
|
4507
|
+
.description(
|
|
4508
|
+
"Install the packaged dorfl skills (`from-idea`, `setup`, ...) into the operator's own agent harness(es). Installs into your OWN harness dirs (~/.agents/skills/ + per-harness symlinks) — this is operator tooling, NOT target-repo propagation (skills don't travel into `work/` repos; only `work/protocol/` does).",
|
|
4509
|
+
);
|
|
4510
|
+
|
|
4511
|
+
skills
|
|
4512
|
+
.command('add')
|
|
4513
|
+
.helpGroup(HEADLINE_GROUP)
|
|
4514
|
+
.description(
|
|
4515
|
+
'Copy the packaged dorfl skills into the canonical `~/.agents/skills/` (global; `--local` scopes to <cwd>/.agents/skills/) and symlink each detected non-universal harness (Claude Code, Cursor, Windsurf, ...) to it. Idempotent; re-run to pick up new/updated skills.',
|
|
4516
|
+
)
|
|
4517
|
+
.option(
|
|
4518
|
+
'--local',
|
|
4519
|
+
'install project-locally into <cwd>/.agents/skills/ (and each detected harness\u2019s PROJECT dir) instead of globally into ~/.agents/skills/. Default: global.',
|
|
4520
|
+
)
|
|
4521
|
+
.action((flags: SkillsAddFlags) => {
|
|
4522
|
+
const result = installSkills({global: flags.local !== true});
|
|
4523
|
+
console.log(formatSkillsAddReport(result, flags.local === true));
|
|
4524
|
+
});
|
|
4525
|
+
|
|
4244
4526
|
return program;
|
|
4245
4527
|
}
|
|
4246
4528
|
|
|
4529
|
+
interface SkillsAddFlags {
|
|
4530
|
+
/**
|
|
4531
|
+
* `--local`: project-local install (canonical base `<cwd>/.agents/skills/`)
|
|
4532
|
+
* instead of the global `~/.agents/skills/` default. Named to mirror the
|
|
4533
|
+
* `remote add --local` sibling ('local' = "scope to here") and the vendored
|
|
4534
|
+
* `install()`'s `global: false` semantic. Chosen over `--project` /
|
|
4535
|
+
* `--here` / `--global` inversion for consistency with the existing verb.
|
|
4536
|
+
*/
|
|
4537
|
+
local?: boolean;
|
|
4538
|
+
}
|
|
4539
|
+
|
|
4540
|
+
/**
|
|
4541
|
+
* Human-readable report for `dorfl skills add`. Prints the SOURCE the skills
|
|
4542
|
+
* were read from, each canonical install path (one per skill), and each
|
|
4543
|
+
* non-universal harness placement (symlink | copy) with its absolute path.
|
|
4544
|
+
* Universal harnesses read from the canonical dir directly and need no line.
|
|
4545
|
+
* Output goes to stdout; the shape is stable enough for a human to audit "what
|
|
4546
|
+
* landed where" but is NOT a machine contract (no JSON mode in this pass).
|
|
4547
|
+
*/
|
|
4548
|
+
export function formatSkillsAddReport(
|
|
4549
|
+
result: InstallSkillsResult,
|
|
4550
|
+
local: boolean,
|
|
4551
|
+
): string {
|
|
4552
|
+
const lines: string[] = [];
|
|
4553
|
+
const scope = local ? 'project-local' : 'global';
|
|
4554
|
+
lines.push(`Installed dorfl skills (${scope}) from ${result.sourceDir}:`);
|
|
4555
|
+
if (result.paths.length === 0) {
|
|
4556
|
+
lines.push(' (no skills found in source)');
|
|
4557
|
+
} else {
|
|
4558
|
+
for (const p of result.paths.slice().sort()) {
|
|
4559
|
+
lines.push(` ${p}`);
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4562
|
+
if (result.agents.length === 0) {
|
|
4563
|
+
lines.push(
|
|
4564
|
+
'Harnesses: no non-universal harness detected (universal harnesses read from the canonical dir directly).',
|
|
4565
|
+
);
|
|
4566
|
+
} else {
|
|
4567
|
+
lines.push('Harnesses:');
|
|
4568
|
+
for (const a of result.agents.slice().sort((x, y) => {
|
|
4569
|
+
const byAgent = x.agent.localeCompare(y.agent);
|
|
4570
|
+
return byAgent !== 0 ? byAgent : x.path.localeCompare(y.path);
|
|
4571
|
+
})) {
|
|
4572
|
+
lines.push(` ${a.agent}: ${a.mode} -> ${a.path}`);
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
return lines.join('\n');
|
|
4576
|
+
}
|
|
4577
|
+
|
|
4247
4578
|
/**
|
|
4248
4579
|
* A readline-backed {@link WizardPrompts} for the interactive `install-ci`
|
|
4249
4580
|
* wizard. Prompts go to stderr (stdout is reserved for any machine output); a
|