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/dist/cli.js
CHANGED
|
@@ -51,7 +51,8 @@ import { sweepOrphanSidecars } from './orphan-sidecar.js';
|
|
|
51
51
|
import { sweepLedgerDuplicates, formatLedgerSweep } from './ledger-lint.js';
|
|
52
52
|
import { status, formatStatus } from './status.js';
|
|
53
53
|
import { ledgerWrite } from './ledger-write.js';
|
|
54
|
-
import { releaseItemLock, reportItemLocks, formatItemLockReport, itemLockReportNeedsAttention, reapStaleItemLocks, formatReapReport, reapReportNeedsAttention, } from './item-lock.js';
|
|
54
|
+
import { releaseItemLock, releaseLiteralLockEntry, reportItemLocks, formatItemLockReport, itemLockReportNeedsAttention, reapStaleItemLocks, formatReapReport, reapReportNeedsAttention, } from './item-lock.js';
|
|
55
|
+
import { migrateStuckLocks, formatMigrateStuckLocksReport, migrateStuckLocksNeedsAttention, } from './migrate-stuck-locks.js';
|
|
55
56
|
import { promoteFromPreBacklog, promoteFromPreSpec, listPromotable, } from './needs-attention.js';
|
|
56
57
|
import { parseSlugArg } from './slug-namespace.js';
|
|
57
58
|
import { dropSource } from './drop-source.js';
|
|
@@ -62,6 +63,7 @@ import { installCI } from './install-ci.js';
|
|
|
62
63
|
import { GitHubCIContext } from './install-ci-github.js';
|
|
63
64
|
import { loadCapabilityRegistry } from './install-ci-core.js';
|
|
64
65
|
import { performCloseMergedIssues } from './close-job.js';
|
|
66
|
+
import { installSkills } from './install-skills.js';
|
|
65
67
|
/**
|
|
66
68
|
* Whether `--auto-build` / `--no-auto-build` was explicitly passed on the command
|
|
67
69
|
* line. Commander gives a negatable boolean option a default of `true`, so we
|
|
@@ -218,10 +220,10 @@ function buildRegistrySetAdvanceTick(options) {
|
|
|
218
220
|
lifecycleGates: {
|
|
219
221
|
triage: config.observationTriage !== 'off',
|
|
220
222
|
surface: config.surfaceBlockers,
|
|
221
|
-
// `surfaceStaging` widens the SURFACE candidate set into STAGING (
|
|
223
|
+
// `surfaceStaging` widens the SURFACE candidate set into STAGING (spec
|
|
222
224
|
// `staging-surface-and-apply-promote-safety` F2). Default `true` — a
|
|
223
|
-
// tasked `needsAnswers` task in `tasks/backlog/` (or
|
|
224
|
-
// `
|
|
225
|
+
// tasked `needsAnswers` task in `tasks/backlog/` (or spec in
|
|
226
|
+
// `specs/proposed/`) surfaces its questions BEFORE promotion. BUILD/claim
|
|
225
227
|
// stays pool-only either way.
|
|
226
228
|
surfaceStaging: config.surfaceStaging,
|
|
227
229
|
},
|
|
@@ -249,11 +251,11 @@ function buildRegistrySetAdvanceTick(options) {
|
|
|
249
251
|
identity: config.identity,
|
|
250
252
|
autoTask: config.autoTask,
|
|
251
253
|
integration: config.integration,
|
|
252
|
-
// The per-TRANSITION TASKING override: the `do
|
|
254
|
+
// The per-TRANSITION TASKING override: the `do spec:` tasking path threads
|
|
253
255
|
// `taskingIntegration ?? integration`; the build path stays on `integration`.
|
|
254
256
|
taskingIntegration: config.taskingIntegration,
|
|
255
|
-
// The TASK-PLACEMENT configured default (`do
|
|
256
|
-
// `
|
|
257
|
+
// The TASK-PLACEMENT configured default (`do spec:` tasking output:
|
|
258
|
+
// `backlog` staged vs `ready` pool). No operator flag on this
|
|
257
259
|
// registry-driven advance context, so only the configured default rung is
|
|
258
260
|
// threaded (the resolver still layers untrusted-origin force + built-in floor).
|
|
259
261
|
tasksLandIn: config.tasksLandIn,
|
|
@@ -261,7 +263,7 @@ function buildRegistrySetAdvanceTick(options) {
|
|
|
261
263
|
verify: config.verify,
|
|
262
264
|
// Single-job build path: gate the REBASED tip (the default) unconditionally.
|
|
263
265
|
freshWorktreeGate: config.freshWorktreeGate,
|
|
264
|
-
// Cross-job merge-serialiser CAS-retry cap (
|
|
266
|
+
// Cross-job merge-serialiser CAS-retry cap (spec `land-time-reverify-and-
|
|
265
267
|
// parallel-merge-ceiling` Story 5 / Applied Answer q1 (a)) — resolved per-repo
|
|
266
268
|
// and threaded so the registry-driven advance path's `do` inherits it.
|
|
267
269
|
mergeRetries: config.mergeRetries,
|
|
@@ -300,7 +302,7 @@ function buildRegistrySetAdvanceTick(options) {
|
|
|
300
302
|
triageGate: harnessTriageGate({ harness, agentCmd: config.agentCmd }),
|
|
301
303
|
triageModel: config.model,
|
|
302
304
|
// The ANSWERED-MERGE LAND DISPATCH context (task
|
|
303
|
-
// `apply-rung-merge-disposition`,
|
|
305
|
+
// `apply-rung-merge-disposition`, spec `land-time-reverify-and-parallel-
|
|
304
306
|
// merge-ceiling`): the dispatcher cuts a per-job worktree via
|
|
305
307
|
// `workspace.ts` `createJob` off the hub mirror (so we thread the resolved
|
|
306
308
|
// `workspacesDir` + the real arbiter URL — the per-mirror tree-less
|
|
@@ -396,7 +398,7 @@ function runFlagOverrides(flags, command) {
|
|
|
396
398
|
// fleet conditional lives in `runOnce`, not in this flag mapping).
|
|
397
399
|
Object.assign(overrides, freshWorktreeGateFlagOverrides(flags));
|
|
398
400
|
// `--merge-retries <n>` rides the SAME chain: the cross-job merge-serialiser
|
|
399
|
-
// CAS-retry cap (
|
|
401
|
+
// CAS-retry cap (spec `land-time-reverify-and-parallel-merge-ceiling` Story 5 /
|
|
400
402
|
// Applied Answer q1 (a)). The `run` fleet inherits the resolved cap through
|
|
401
403
|
// the converged `performIntegration` core (config.mergeRetries threads into
|
|
402
404
|
// the merge loop, replacing the bare `DEFAULT_MERGE_RETRIES` fallback).
|
|
@@ -409,7 +411,7 @@ function formatItemLine(item) {
|
|
|
409
411
|
}
|
|
410
412
|
/**
|
|
411
413
|
* Resolve the EXPLICIT operator placement override from `--tasks-land-in <where>`
|
|
412
|
-
* (the top of the `do
|
|
414
|
+
* (the top of the `do spec:` tasking-placement precedence — task
|
|
413
415
|
* `runner-deterministic-slice-placement-policy-and-precedence`). Mirrors the
|
|
414
416
|
* `flagMode === 'merge'` ⇒ `explicitMerge: true` shape: it contributes
|
|
415
417
|
* `explicitTasksLandIn` ONLY when the operator actually typed the flag, so an
|
|
@@ -422,8 +424,8 @@ function explicitTasksLandInFromFlag(raw) {
|
|
|
422
424
|
if (raw === undefined) {
|
|
423
425
|
return undefined;
|
|
424
426
|
}
|
|
425
|
-
if (raw !== '
|
|
426
|
-
throw new Error(`--tasks-land-in must be '
|
|
427
|
+
if (raw !== 'backlog' && raw !== 'ready') {
|
|
428
|
+
throw new Error(`--tasks-land-in must be 'backlog' or 'ready' (got '${raw}').`);
|
|
427
429
|
}
|
|
428
430
|
return raw;
|
|
429
431
|
}
|
|
@@ -498,7 +500,7 @@ function printPrdToSpecReport(result) {
|
|
|
498
500
|
return;
|
|
499
501
|
}
|
|
500
502
|
if (result.leaks.length === 0) {
|
|
501
|
-
console.log('Leak scan: GREEN (no surviving
|
|
503
|
+
console.log('Leak scan: GREEN (no surviving spec data ref).');
|
|
502
504
|
}
|
|
503
505
|
else {
|
|
504
506
|
console.error(`Leak scan: FAILED (${result.leaks.length} leak(s)):`);
|
|
@@ -513,11 +515,12 @@ function printLeak(leak) {
|
|
|
513
515
|
/**
|
|
514
516
|
* Resolve a task-only command's slug argument through the §3a namespace guard
|
|
515
517
|
* (`resolveTaskOnlyArg`): accept bare (= task) + `task:` (explicit alias),
|
|
516
|
-
* REJECT `spec:` with a clear "operates on tasks, not specs" error
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
*
|
|
518
|
+
* REJECT `spec:` with a clear "operates on tasks, not specs" error. On rejection
|
|
519
|
+
* it prints the error to stderr and exits 1 (the task-only commands never act on
|
|
520
|
+
* a spec). An OMITTED slug (`start`/`complete`/`prompt` infer it from the branch)
|
|
521
|
+
* passes through untouched. (The legacy ''prd:'' prefix is a DEAD namespace token
|
|
522
|
+
* after the hard cutover — it is neither special-cased nor rejected here; a
|
|
523
|
+
* ''prd:<slug>'' arg resolves as a bare literal task slug.)
|
|
521
524
|
*
|
|
522
525
|
* `do` is the ONE command that spans both namespaces; it consumes the full
|
|
523
526
|
* `resolveSlug` (with the cross-namespace collision check) in the `do-in-place`
|
|
@@ -595,10 +598,10 @@ function buildInteractiveLauncher(flags, configPath, repoPath) {
|
|
|
595
598
|
* switching to its `work/<slug>` branch WITHOUT claiming). The runtime
|
|
596
599
|
* difference is exactly the `resume` flag — `resume` forces it on (its only mode
|
|
597
600
|
* is to re-engage), while `start` honours the (now hidden) `--resume` alias.
|
|
598
|
-
* Both are task-only (§3a: accept bare + `task:`, reject `
|
|
601
|
+
* Both are task-only (§3a: accept bare + `task:`, reject `spec:`).
|
|
599
602
|
*/
|
|
600
603
|
async function runStartAction(rawSlug, flags, resume) {
|
|
601
|
-
// Task-only command (§3a): accept bare + `task:`, reject `
|
|
604
|
+
// Task-only command (§3a): accept bare + `task:`, reject `spec:`.
|
|
602
605
|
const slug = resolveTaskOnlySlug(rawSlug);
|
|
603
606
|
const cwd = process.cwd();
|
|
604
607
|
// `resume --isolated <slug>`: re-engage the slug's RETAINED job worktree (the
|
|
@@ -675,6 +678,34 @@ export function buildProgram() {
|
|
|
675
678
|
program
|
|
676
679
|
.name(brand.bin)
|
|
677
680
|
.description('Autonomous parallel agents over file-based work/ queues.');
|
|
681
|
+
// `dorfl config --json` (spec `graceful-pre-timeout-wip-checkpoint` — step 5):
|
|
682
|
+
// print the RESOLVED per-repo config as JSON, so BOTH the advance-lifecycle
|
|
683
|
+
// workflow's `enumerate` job (which reads it to emit the dynamic
|
|
684
|
+
// `githubTimeout` output) AND a human debugging a config-precedence question
|
|
685
|
+
// share ONE honest primitive. A focused honest primitive — NOT overloaded
|
|
686
|
+
// onto `scan`. Runs in the cwd (in-place), so the resolution chain reads THIS
|
|
687
|
+
// repo's committed `dorfl.json` exactly like `scan --here`.
|
|
688
|
+
program
|
|
689
|
+
.command('config')
|
|
690
|
+
.helpGroup(HEADLINE_GROUP)
|
|
691
|
+
.description("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.")
|
|
692
|
+
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
693
|
+
.option('--arbiter <remote>', 'name of the arbiter git remote (default: per-repo/global defaultArbiter)')
|
|
694
|
+
.option('--json', 'output the resolved config as JSON')
|
|
695
|
+
.action(async (flags) => {
|
|
696
|
+
if (flags.json !== true) {
|
|
697
|
+
console.error('error: `dorfl config` currently supports only --json output. Pass --json.');
|
|
698
|
+
process.exit(1);
|
|
699
|
+
}
|
|
700
|
+
const cwd = process.cwd();
|
|
701
|
+
const { global, override } = loadGlobalAndOverride(flags.config);
|
|
702
|
+
const resolved = resolveRepoConfig({
|
|
703
|
+
repoPath: cwd,
|
|
704
|
+
global,
|
|
705
|
+
override,
|
|
706
|
+
}).config;
|
|
707
|
+
console.log(JSON.stringify(resolved, null, 2));
|
|
708
|
+
});
|
|
678
709
|
program
|
|
679
710
|
.command('scan')
|
|
680
711
|
.helpGroup(HEADLINE_GROUP)
|
|
@@ -907,14 +938,14 @@ export function buildProgram() {
|
|
|
907
938
|
program
|
|
908
939
|
.command('claim')
|
|
909
940
|
.helpGroup(ADVANCED_GROUP)
|
|
910
|
-
.description(
|
|
941
|
+
.description(`Atomically claim a ${workFolderPrefix('tasks-ready')}<slug>.md item via a compare-and-swap push to the arbiter (in-process; mirrors scripts/claim.sh).`)
|
|
911
942
|
.argument('<slug>', 'the slug of the backlog item to claim')
|
|
912
943
|
.option('--arbiter <remote>', 'name of the arbiter git remote (default: origin)', 'origin')
|
|
913
944
|
.option('--retries <n>', 'cap on push retries when main advances', '3')
|
|
914
945
|
.option('--dry-run', 'show the intended push without mutating the arbiter')
|
|
915
946
|
.option('--ignore-not-ready', 'override the readiness guard: claim despite an unmet blockedBy, and silence the needsAnswers warning (loud, never default)')
|
|
916
947
|
.action(async (rawSlug, flags) => {
|
|
917
|
-
// Task-only command (§3a): accept bare + `task:`, reject `
|
|
948
|
+
// Task-only command (§3a): accept bare + `task:`, reject `spec:`.
|
|
918
949
|
const slug = resolveTaskOnlySlug(rawSlug);
|
|
919
950
|
// Wrap ONLY this CLI surface's `performClaim` call with the spinner
|
|
920
951
|
// helper (task `claim-cas-spinner`): the push can take seconds, so the
|
|
@@ -1022,7 +1053,7 @@ export function buildProgram() {
|
|
|
1022
1053
|
const remote = flags.remote !== undefined && flags.remote.trim() !== ''
|
|
1023
1054
|
? flags.remote
|
|
1024
1055
|
: undefined;
|
|
1025
|
-
// Task-only command (§3a): accept bare + `task:`, reject `
|
|
1056
|
+
// Task-only command (§3a): accept bare + `task:`, reject `spec:`.
|
|
1026
1057
|
const theSlug = resolveTaskOnlySlug(rawSlug);
|
|
1027
1058
|
const configPath = flags.config ?? defaultConfigPath();
|
|
1028
1059
|
const { dir: configuredRoot, config } = loadHumanWorktreesDir(configPath);
|
|
@@ -1066,10 +1097,10 @@ export function buildProgram() {
|
|
|
1066
1097
|
program
|
|
1067
1098
|
.command('prompt')
|
|
1068
1099
|
.helpGroup(ADVANCED_GROUP)
|
|
1069
|
-
.description(
|
|
1100
|
+
.description(`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.`)
|
|
1070
1101
|
.argument('[slug]', 'the slug to render (inferred from a work/<slug> branch if omitted)')
|
|
1071
1102
|
.action((rawSlug) => {
|
|
1072
|
-
// Task-only command (§3a): accept bare + `task:`, reject `
|
|
1103
|
+
// Task-only command (§3a): accept bare + `task:`, reject `spec:`.
|
|
1073
1104
|
const slug = resolveTaskOnlySlug(rawSlug);
|
|
1074
1105
|
// Resolve the `promptGuidance` NUDGE namespace through the SAME chain the
|
|
1075
1106
|
// gate family uses (env > per-repo > global > default), so e.g. a
|
|
@@ -1112,7 +1143,7 @@ export function buildProgram() {
|
|
|
1112
1143
|
.option('--no-fresh-worktree-gate', 'run the acceptance gate in the current checkout (the pre-rebase tree) — the opt-out for when the per-gate install cost is too high')
|
|
1113
1144
|
.option('--merge-retries <n>', 'cross-job merge-serialiser CAS-retry cap (see `run --help`); resolved flag > env > per-repo > global > default 1000.')
|
|
1114
1145
|
.action(async (rawSlug, flags) => {
|
|
1115
|
-
// Task-only command (§3a): accept bare + `task:`, reject `
|
|
1146
|
+
// Task-only command (§3a): accept bare + `task:`, reject `spec:`.
|
|
1116
1147
|
const slug = resolveTaskOnlySlug(rawSlug);
|
|
1117
1148
|
const cwd = process.cwd();
|
|
1118
1149
|
const { global, override } = loadGlobalAndOverride(flags.config);
|
|
@@ -1133,6 +1164,11 @@ export function buildProgram() {
|
|
|
1133
1164
|
override,
|
|
1134
1165
|
flags: {
|
|
1135
1166
|
...(flagMode ? { integration: flagMode } : {}),
|
|
1167
|
+
// `--merge-retries <n>` rides the SAME gate-family precedence chain on
|
|
1168
|
+
// the isolated-recovery path so a wide-matrix CI's raised cap actually
|
|
1169
|
+
// reaches the land tail here too (task
|
|
1170
|
+
// `thread-merge-retries-cross-task-and-ratify-default`).
|
|
1171
|
+
...mergeRetriesFlagOverrides(flags),
|
|
1136
1172
|
...noPRFlagOverrides(flags),
|
|
1137
1173
|
},
|
|
1138
1174
|
});
|
|
@@ -1147,6 +1183,9 @@ export function buildProgram() {
|
|
|
1147
1183
|
workspacesDir: flags.workspace ?? isoConfig.workspacesDir,
|
|
1148
1184
|
integration: isoConfig.integration,
|
|
1149
1185
|
noPR: isoConfig.noPR,
|
|
1186
|
+
// The resolved cross-job CAS-retry cap threaded through so the isolated-
|
|
1187
|
+
// recovery land tail respects the per-repo cap (same chain as `complete`).
|
|
1188
|
+
mergeRetries: isoConfig.mergeRetries,
|
|
1150
1189
|
note: (message) => console.error(`>> ${message}`),
|
|
1151
1190
|
env: process.env,
|
|
1152
1191
|
});
|
|
@@ -1172,7 +1211,7 @@ export function buildProgram() {
|
|
|
1172
1211
|
// `--fresh-worktree-gate`/`--no-fresh-worktree-gate` rides the SAME chain.
|
|
1173
1212
|
...freshWorktreeGateFlagOverrides(flags),
|
|
1174
1213
|
// `--merge-retries <n>` rides the SAME chain: the cross-job merge-serialiser
|
|
1175
|
-
// CAS-retry cap (
|
|
1214
|
+
// CAS-retry cap (spec `land-time-reverify-and-parallel-merge-ceiling` Story 5
|
|
1176
1215
|
// / Applied Answer q1 (a)).
|
|
1177
1216
|
...mergeRetriesFlagOverrides(flags),
|
|
1178
1217
|
// `--no-pr` (the PR-INTENT axis) rides the SAME chain.
|
|
@@ -1212,7 +1251,7 @@ export function buildProgram() {
|
|
|
1212
1251
|
// a single-job path, so the resolved flag is passed UNCONDITIONALLY (no
|
|
1213
1252
|
// fleet downgrade).
|
|
1214
1253
|
freshWorktreeGate: config.freshWorktreeGate,
|
|
1215
|
-
// Cross-job merge-serialiser CAS-retry cap (
|
|
1254
|
+
// Cross-job merge-serialiser CAS-retry cap (spec `land-time-reverify-and-
|
|
1216
1255
|
// parallel-merge-ceiling` Story 5 / Applied Answer q1 (a)) — the resolved
|
|
1217
1256
|
// per-repo value reaches the merge loop via `performComplete`→
|
|
1218
1257
|
// `performIntegration`.
|
|
@@ -1243,22 +1282,23 @@ export function buildProgram() {
|
|
|
1243
1282
|
program
|
|
1244
1283
|
.command('do')
|
|
1245
1284
|
.helpGroup(HEADLINE_GROUP)
|
|
1246
|
-
.description(
|
|
1285
|
+
.description("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.")
|
|
1247
1286
|
// EXTENSIBLE argument grammar (the three do-* tasks grow this one block):
|
|
1248
1287
|
// `do-autopick` widens the single optional positional into a VARIADIC one so
|
|
1249
1288
|
// `do` (zero args = auto-pick), `do <a> <b> …` (named, in sequence), and
|
|
1250
1289
|
// `do <slug>` (exactly one) all share the one command. `-n <x>` is the count
|
|
1251
1290
|
// for the auto-pick form. `do` stays SEQUENTIAL (parallelism is `run`).
|
|
1252
|
-
.argument('[slugs...]', 'the item(s) to do: bare (= the task), task:<slug>, or spec:<slug> (task the spec
|
|
1291
|
+
.argument('[slugs...]', '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.')
|
|
1253
1292
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
1254
1293
|
.option('--arbiter <remote>', 'name of the arbiter git remote (default: per-repo/global defaultArbiter)')
|
|
1255
1294
|
.option('-n, --number <x>', 'AUTO-PICK x eligible items and do them IN SEQUENCE (ordered by selectionOrder, default drain = tasks-first then prds-to-task). Sequential — never a parallelism knob (that is `run`). Mutually exclusive with naming items.')
|
|
1256
1295
|
.option('--selection-order <order>', 'order the auto-pick pools (build/task/surface/triage; apply is always first): a preset keyword (drain (default) | groom) or an explicit comma-separated pool list (e.g. build,task,surface,triage). Resolved flag > env > per-repo > global > default.')
|
|
1257
1296
|
.option('--remote <r>', 'run against a REGISTERED repo with NO checkout: materialise a hub mirror + job worktree in the agents\u2019 area (auto-registers an unknown remote), run the pipeline there, then reap (never touches the human area)')
|
|
1258
|
-
.option('--isolated', "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
|
|
1297
|
+
.option('--isolated', "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).")
|
|
1298
|
+
.option('--in-place', "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.")
|
|
1259
1299
|
.option('--merge', 'integrate in merge mode this invocation (mutually exclusive with --propose; overrides config)')
|
|
1260
1300
|
.option('--propose', 'integrate in propose mode this invocation (default; mutually exclusive with --merge; overrides config)')
|
|
1261
|
-
.option('--tasks-land-in <where>', 'where `do
|
|
1301
|
+
.option('--tasks-land-in <where>', '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.')
|
|
1262
1302
|
.option('--no-pr', 'propose without opening a PR: push the branch but deliberately skip the review request, even on an authed GitHub arbiter (the explicit suppress-PR intent). Resolved flag > env > per-repo > global > default off.')
|
|
1263
1303
|
.option('--ignore-diverged-main', 'override the in-place divergence guard: run even when local main is ahead of <arbiter>/main (unpushed). The work still lands on the arbiter; local main is left for you to `git rebase`. In-place only; loud, never default.')
|
|
1264
1304
|
.option('--allow-backlog', 'do task:<slug> ONLY: also FIND, CLAIM, and COMPLETE a task that lives in tasks/backlog/ (staging), driving it in place WITHOUT promoting it to the pool (so no advance leg / run daemon can claim it out from under you). The done-move goes tasks/backlog/ -> tasks/done/ directly (your explicit drive IS the promotion). EXPLICIT-INVOCATION-ONLY: default off, never set by run/auto-pick/advance or config/env.')
|
|
@@ -1272,9 +1312,9 @@ export function buildProgram() {
|
|
|
1272
1312
|
.option('--no-review', 'do NOT run Gate 2 this invocation (overrides config)')
|
|
1273
1313
|
.option('--review-model <id>', 'model the Gate-2 review agent runs on (de-correlated from the builder; routing intent). Resolved flag > env > per-repo > global > default.')
|
|
1274
1314
|
.option('--review-max-rounds <n>', 'bound the revise/review loop; on exhaustion force needs-attention (default 2)')
|
|
1275
|
-
.option('--tasker-loop', 'run the tasker IMPROVER loop on `do
|
|
1276
|
-
.option('--no-tasker-loop', 'skip the tasker improver loop on `do
|
|
1277
|
-
.option('--tasker-loop-max <n>', 'cap the tasker improver loop on `do
|
|
1315
|
+
.option('--tasker-loop', '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).')
|
|
1316
|
+
.option('--no-tasker-loop', 'skip the tasker improver loop on `do spec:<slug>`')
|
|
1317
|
+
.option('--tasker-loop-max <n>', '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)')
|
|
1278
1318
|
.option('--tasker-loop-model <id>', 'model the tasker improver loop review agent runs on (de-correlated from the tasker; routing intent). Resolved flag > env > per-repo > global > default. DISTINCT from --review-model.')
|
|
1279
1319
|
.option('--fresh-worktree-gate', 'run the acceptance gate (prepare then verify) against the REBASED tip in a CLEAN throwaway worktree (the tree that actually integrates), so a green gate provably describes the merged artifact. ON by default. Resolved flag > env > per-repo > global > default on.')
|
|
1280
1320
|
.option('--no-fresh-worktree-gate', "run the acceptance gate in the agent's build worktree (the pre-rebase tree) as before — the opt-out for when the per-gate install cost is too high")
|
|
@@ -1331,13 +1371,47 @@ export function buildProgram() {
|
|
|
1331
1371
|
// `remote-do-reads-per-repo-config-from-arbiter-main`). Only the whitelisted
|
|
1332
1372
|
// `REPO_ALLOWED_KEYS` are layered (host-only keys stay global/flag/env-only,
|
|
1333
1373
|
// rejected by the SAME `repo-config.ts` split).
|
|
1334
|
-
|
|
1374
|
+
// Mutually-exclusive intent guards for the three FORM flags (task
|
|
1375
|
+
// `make-isolated-default-build-mode`, D3): `--in-place` opts OUT of the new
|
|
1376
|
+
// isolated default, `--isolated` is the redundant explicit opt-IN alias,
|
|
1377
|
+
// `--remote <r>` targets a foreign repo. `--in-place` + `--isolated` is
|
|
1378
|
+
// contradictory (build here vs build off the arbiter); `--in-place` + `--remote`
|
|
1379
|
+
// is nonsensical (there is no local checkout to take over). Reject loudly
|
|
1380
|
+
// rather than silently prefer one.
|
|
1381
|
+
if (flags.inPlace === true && flags.isolated === true) {
|
|
1382
|
+
console.error('error: --in-place and --isolated are contradictory (build in the current ' +
|
|
1383
|
+
"checkout vs build in a worktree off this repo's arbiter). Pick one.");
|
|
1384
|
+
process.exit(1);
|
|
1385
|
+
}
|
|
1386
|
+
if (flags.inPlace === true && flags.remote !== undefined) {
|
|
1387
|
+
console.error('error: --in-place builds in the CURRENT checkout; --remote <r> targets a ' +
|
|
1388
|
+
'REGISTERED repo with NO checkout. Pick one.');
|
|
1389
|
+
process.exit(1);
|
|
1390
|
+
}
|
|
1391
|
+
// The DEFAULT flip (task `make-isolated-default-build-mode`): with no
|
|
1392
|
+
// `--remote` and no `--in-place`, `do <slug>` builds in an ISOLATED job
|
|
1393
|
+
// worktree off THIS repo's arbiter — the SAME no-checkout path `--isolated`
|
|
1394
|
+
// already used. The cwd is the origin SOURCE only (arbiter-remote resolution
|
|
1395
|
+
// + per-repo config), never written. `--isolated` remains accepted as a
|
|
1396
|
+
// redundant explicit opt-IN alias (D3). `--in-place` opts out to today's
|
|
1397
|
+
// in-checkout path (the fall-through below). See ADR §3.
|
|
1398
|
+
const isolatedNoRemote = flags.remote === undefined && flags.inPlace !== true;
|
|
1399
|
+
// Whether the user DEFAULTED into isolated (no form flag typed) vs typed
|
|
1400
|
+
// `--isolated` explicitly. Only used to tune the no-arbiter error message so
|
|
1401
|
+
// the default path names `--in-place` as its natural escape (D2).
|
|
1402
|
+
const defaultedToIsolated = isolatedNoRemote && flags.isolated !== true;
|
|
1335
1403
|
if (flags.remote !== undefined || isolatedNoRemote) {
|
|
1336
1404
|
// The form's user-facing name + canonical usage, for the shared error
|
|
1337
|
-
// messages below (so
|
|
1338
|
-
const form = isolatedNoRemote
|
|
1405
|
+
// messages below (so isolated errors read in the form's own terms).
|
|
1406
|
+
const form = isolatedNoRemote
|
|
1407
|
+
? defaultedToIsolated
|
|
1408
|
+
? 'do'
|
|
1409
|
+
: '--isolated'
|
|
1410
|
+
: '--remote';
|
|
1339
1411
|
const usage = isolatedNoRemote
|
|
1340
|
-
?
|
|
1412
|
+
? defaultedToIsolated
|
|
1413
|
+
? '`do <slug>`'
|
|
1414
|
+
: '`do --isolated <slug>`'
|
|
1341
1415
|
: '`do --remote <r> <slug>`';
|
|
1342
1416
|
// The no-checkout forms now support the SAME variadic grammar the in-place
|
|
1343
1417
|
// form does: a single NAMED item, MULTIPLE named items (sequential), and
|
|
@@ -1359,10 +1433,30 @@ export function buildProgram() {
|
|
|
1359
1433
|
resolveDefaultArbiterForCwd(cwd, global, remoteFlags, override);
|
|
1360
1434
|
const resolvedUrl = resolveArbiterUrlFromCheckout(cwd, arbiterName, identityEnv(bootstrapIdentity, process.env));
|
|
1361
1435
|
if (resolvedUrl === undefined) {
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1436
|
+
// Isolated needs an arbiter to build off. Two message variants:
|
|
1437
|
+
// - defaulted-to-isolated (no form flag typed) leads with the DEFAULT
|
|
1438
|
+
// and names `--in-place` as the natural local escape (D2). This is
|
|
1439
|
+
// the ERROR-not-silent-degrade behaviour the flip requires: a repo
|
|
1440
|
+
// with no arbiter must NOT be quietly built in-place invisibly.
|
|
1441
|
+
// - explicit `--isolated` keeps its own terms (the user asked for
|
|
1442
|
+
// isolation deliberately) but ALSO surfaces `--in-place` alongside
|
|
1443
|
+
// `--remote <url>` as the two escapes.
|
|
1444
|
+
if (defaultedToIsolated) {
|
|
1445
|
+
console.error(`error: no arbiter is configured for this repo ` +
|
|
1446
|
+
`(no '${arbiterName}' remote), and \`do\` now defaults to ` +
|
|
1447
|
+
`building in an ISOLATED worktree off THIS repo's arbiter ` +
|
|
1448
|
+
`(task \`make-isolated-default-build-mode\`). Configure an ` +
|
|
1449
|
+
`arbiter (e.g. \`dorfl remote add <url>\`), pass --in-place ` +
|
|
1450
|
+
`to build in the current checkout, or use --remote <url> to ` +
|
|
1451
|
+
`target another repo.`);
|
|
1452
|
+
}
|
|
1453
|
+
else {
|
|
1454
|
+
console.error(`error: --isolated builds in a worktree off this repo's arbiter ` +
|
|
1455
|
+
`('${arbiterName}'), but no such arbiter remote is configured/found ` +
|
|
1456
|
+
`here. Run inside a participating repo (a clone with an arbiter ` +
|
|
1457
|
+
`remote), pass --in-place to build in the current checkout, or ` +
|
|
1458
|
+
`use --remote <url> to target another repo.`);
|
|
1459
|
+
}
|
|
1366
1460
|
process.exit(1);
|
|
1367
1461
|
}
|
|
1368
1462
|
effectiveRemote = resolvedUrl;
|
|
@@ -1411,7 +1505,7 @@ export function buildProgram() {
|
|
|
1411
1505
|
// Host-only runner IDENTITY — scopes git/provider ops only (not the
|
|
1412
1506
|
// agent launch); absent ⇒ ambient.
|
|
1413
1507
|
identity: remoteConfig.identity,
|
|
1414
|
-
// `do --remote
|
|
1508
|
+
// `do --remote spec:<slug>` tasking-gate policy (task-build path ignores it).
|
|
1415
1509
|
autoTask: remoteConfig.autoTask,
|
|
1416
1510
|
// The resolved `promptGuidance` nudge — threaded into the remote worker
|
|
1417
1511
|
// prompt (runRemotePipeline → buildAgentPrompt), mirroring in-place `do`.
|
|
@@ -1419,7 +1513,7 @@ export function buildProgram() {
|
|
|
1419
1513
|
integration: remoteConfig.integration,
|
|
1420
1514
|
// EXPLICIT `--merge` override for the untrusted-origin build-propose rule.
|
|
1421
1515
|
explicitMerge: flagMode === 'merge',
|
|
1422
|
-
// Per-TRANSITION TASKING override (the `do --remote
|
|
1516
|
+
// Per-TRANSITION TASKING override (the `do --remote spec:` tasking path).
|
|
1423
1517
|
taskingIntegration: remoteConfig.taskingIntegration,
|
|
1424
1518
|
// TASK-PLACEMENT: the configured default + the EXPLICIT operator override
|
|
1425
1519
|
// (`--tasks-land-in`), the top of the placement precedence — mirrors
|
|
@@ -1431,7 +1525,7 @@ export function buildProgram() {
|
|
|
1431
1525
|
// Single-job build path: gate the REBASED tip (the default) unconditionally.
|
|
1432
1526
|
freshWorktreeGate: remoteConfig.freshWorktreeGate,
|
|
1433
1527
|
// Cross-job merge-serialiser CAS-retry cap (resolved through the per-repo
|
|
1434
|
-
// chain on the arbiter-side `.dorfl.json` too) —
|
|
1528
|
+
// chain on the arbiter-side `.dorfl.json` too) — spec
|
|
1435
1529
|
// `land-time-reverify-and-parallel-merge-ceiling` Story 5.
|
|
1436
1530
|
mergeRetries: remoteConfig.mergeRetries,
|
|
1437
1531
|
noPR: remoteConfig.noPR,
|
|
@@ -1448,7 +1542,7 @@ export function buildProgram() {
|
|
|
1448
1542
|
agentCmd: remoteConfig.agentCmd,
|
|
1449
1543
|
})
|
|
1450
1544
|
: undefined,
|
|
1451
|
-
// The tasker IMPROVER loop on the `do --remote
|
|
1545
|
+
// The tasker IMPROVER loop on the `do --remote spec:` path is ON by default
|
|
1452
1546
|
// (auto-tasking has no `verify` floor, so the loop is the task path's
|
|
1453
1547
|
// quality engine). `--tasker-loop`/`--no-tasker-loop` gates wiring the seam;
|
|
1454
1548
|
// `taskerLoopMax`/`taskerLoopModel` resolve per-repo (flag > env > per-repo
|
|
@@ -1461,7 +1555,7 @@ export function buildProgram() {
|
|
|
1461
1555
|
: undefined,
|
|
1462
1556
|
taskerLoopMax: remoteConfig.taskerLoopMax,
|
|
1463
1557
|
taskerLoopModel: remoteConfig.taskerLoopModel,
|
|
1464
|
-
// The task-SET ACCEPTANCE GATE on the `do --remote
|
|
1558
|
+
// The task-SET ACCEPTANCE GATE on the `do --remote spec:` path too.
|
|
1465
1559
|
taskReviewGate: remoteConfig.review
|
|
1466
1560
|
? harnessTaskAcceptanceGate({
|
|
1467
1561
|
harness: remoteHarness,
|
|
@@ -1469,6 +1563,11 @@ export function buildProgram() {
|
|
|
1469
1563
|
})
|
|
1470
1564
|
: undefined,
|
|
1471
1565
|
watch: flags.watch === true,
|
|
1566
|
+
// The dorfl-internal agent deadline + anti-loop ceiling (spec
|
|
1567
|
+
// `graceful-pre-timeout-wip-checkpoint`). Threaded from the RESOLVED
|
|
1568
|
+
// remote-repo config (its committed `dorfl.json` on <arbiter>/main).
|
|
1569
|
+
agentDeadlineMinutes: remoteConfig.agentDeadlineMinutes,
|
|
1570
|
+
maxAutoCheckpoints: remoteConfig.maxAutoCheckpoints,
|
|
1472
1571
|
color: shouldUseColor(process.stdout),
|
|
1473
1572
|
note: (message) => console.error(`>> ${message}`),
|
|
1474
1573
|
noteBlock: (message) => console.error(message),
|
|
@@ -1565,7 +1664,7 @@ export function buildProgram() {
|
|
|
1565
1664
|
// ops (claim, push, integrate, `gh`) — NEVER the agent launch. Absent ⇒
|
|
1566
1665
|
// ambient (today's behaviour). Mapped Config → DoOptions like model/agentCmd.
|
|
1567
1666
|
identity: config.identity,
|
|
1568
|
-
// `do
|
|
1667
|
+
// `do spec:<slug>` tasking-gate policy (the task-build path ignores it).
|
|
1569
1668
|
autoTask: config.autoTask,
|
|
1570
1669
|
// The resolved `promptGuidance` NUDGE namespace (e.g. `testFirst`),
|
|
1571
1670
|
// threaded into the worker prompt by performDo → buildAgentPrompt so a
|
|
@@ -1578,11 +1677,11 @@ export function buildProgram() {
|
|
|
1578
1677
|
// typed `--merge` (`flagMode`), never when `merge` came from config — so an
|
|
1579
1678
|
// untrusted-origin task still forces propose under a config `merge`.
|
|
1580
1679
|
explicitMerge: flagMode === 'merge',
|
|
1581
|
-
// Per-TRANSITION TASKING override: the `do
|
|
1680
|
+
// Per-TRANSITION TASKING override: the `do spec:` tasking path threads
|
|
1582
1681
|
// `taskingIntegration ?? integration`; the task-build path stays on
|
|
1583
1682
|
// `integration`. Unset ⇒ tasking falls back to `integration` (today's behaviour).
|
|
1584
1683
|
taskingIntegration: config.taskingIntegration,
|
|
1585
|
-
// TASK-PLACEMENT (`do
|
|
1684
|
+
// TASK-PLACEMENT (`do spec:` tasking output): the configured default rung +
|
|
1586
1685
|
// the EXPLICIT operator override `--tasks-land-in` (top of the precedence).
|
|
1587
1686
|
// `explicitTasksLandIn` is set ONLY when the flag was typed (mirrors
|
|
1588
1687
|
// `explicitMerge`), so an untrusted-origin staging force still wins under a
|
|
@@ -1595,7 +1694,7 @@ export function buildProgram() {
|
|
|
1595
1694
|
verify: config.verify,
|
|
1596
1695
|
// Single-job build path: gate the REBASED tip (the default) unconditionally.
|
|
1597
1696
|
freshWorktreeGate: config.freshWorktreeGate,
|
|
1598
|
-
// Cross-job merge-serialiser CAS-retry cap (
|
|
1697
|
+
// Cross-job merge-serialiser CAS-retry cap (spec `land-time-reverify-and-
|
|
1599
1698
|
// parallel-merge-ceiling` Story 5 / Applied Answer q1 (a)) — resolved per-repo
|
|
1600
1699
|
// and threaded to `performComplete`→`performIntegration`.
|
|
1601
1700
|
mergeRetries: config.mergeRetries,
|
|
@@ -1619,7 +1718,7 @@ export function buildProgram() {
|
|
|
1619
1718
|
reviewGate: config.review
|
|
1620
1719
|
? harnessReviewGate({ harness, agentCmd: config.agentCmd })
|
|
1621
1720
|
: undefined,
|
|
1622
|
-
// The tasker IMPROVER loop on the `do
|
|
1721
|
+
// The tasker IMPROVER loop on the `do spec:` tasking path is ON by default
|
|
1623
1722
|
// (auto-tasking has no `verify` floor — the loop is the task path's quality
|
|
1624
1723
|
// engine). `--tasker-loop`/`--no-tasker-loop` gates wiring the seam;
|
|
1625
1724
|
// `taskerLoopMax`/`taskerLoopModel` resolve per-repo (flag > env > per-repo
|
|
@@ -1642,11 +1741,16 @@ export function buildProgram() {
|
|
|
1642
1741
|
// `--watch`: tail the pi session log live (pi harness only; the
|
|
1643
1742
|
// performDo guard errors clearly on any other adapter). READ-ONLY.
|
|
1644
1743
|
watch: flags.watch === true,
|
|
1744
|
+
// The dorfl-internal agent deadline + anti-loop ceiling (spec
|
|
1745
|
+
// `graceful-pre-timeout-wip-checkpoint`). Threaded from the resolved
|
|
1746
|
+
// per-repo config so an edit to dorfl.json reflects on the NEXT tick.
|
|
1747
|
+
agentDeadlineMinutes: config.agentDeadlineMinutes,
|
|
1748
|
+
maxAutoCheckpoints: config.maxAutoCheckpoints,
|
|
1645
1749
|
color: shouldUseColor(process.stdout),
|
|
1646
1750
|
note: (message) => console.error(`>> ${message}`),
|
|
1647
1751
|
noteBlock: (message) => console.error(message),
|
|
1648
1752
|
};
|
|
1649
|
-
// `--allow-backlog` is EXPLICIT-SINGLE-TASK-ONLY (
|
|
1753
|
+
// `--allow-backlog` is EXPLICIT-SINGLE-TASK-ONLY (spec
|
|
1650
1754
|
// `do-allow-backlog-drive-staged-tasks-without-promotion`, decision 4): it
|
|
1651
1755
|
// drives ONE named staged task in place. It must NOT combine with the
|
|
1652
1756
|
// AUTO-PICK (zero-args / -n) or MULTI-ITEM forms — those select FROM the
|
|
@@ -1700,33 +1804,33 @@ export function buildProgram() {
|
|
|
1700
1804
|
process.exit(result.exitCode);
|
|
1701
1805
|
});
|
|
1702
1806
|
// `advance` — the SIBLING top-level verb (NOT a `do` subcommand; `do`
|
|
1703
|
-
// subcommands + a standalone `task` verb are REJECTED in
|
|
1807
|
+
// subcommands + a standalone `task` verb are REJECTED in spec `advance-loop`).
|
|
1704
1808
|
// It reuses the SAME shared `prefix:arg` resolver `do` uses, EXTENDED with the
|
|
1705
1809
|
// `obs:` namespace, and wires the classify → lock → execute SKELETON: classify
|
|
1706
1810
|
// the rung (read-only, no model, no lock), take the `advancing` CAS borrow, then
|
|
1707
|
-
// dispatch winner-only — build/task rungs ORCHESTRATE `do`/`do
|
|
1811
|
+
// dispatch winner-only — build/task rungs ORCHESTRATE `do`/`do spec:` (never a
|
|
1708
1812
|
// duplicate), surface/apply/triage dispatch to a named executor seam later
|
|
1709
1813
|
// tasks fill. The DRIVERS (one-shot/loop) + `-n` + per-action gates and the
|
|
1710
1814
|
// rung BODIES are LATER tasks; the bare eligible-SET form errors clearly here.
|
|
1711
1815
|
program
|
|
1712
1816
|
.command('advance')
|
|
1713
1817
|
.helpGroup(HEADLINE_GROUP)
|
|
1714
|
-
.description('Advance work/ item(s) one lifecycle rung toward ready/built (
|
|
1715
|
-
.argument('[slugs...]', 'the item(s) to advance: bare (= the task), task:<slug>, spec:<slug
|
|
1818
|
+
.description('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).')
|
|
1819
|
+
.argument('[slugs...]', '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.')
|
|
1716
1820
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
1717
1821
|
.option('--arbiter <remote>', 'name of the arbiter git remote (default: per-repo/global defaultArbiter)')
|
|
1718
1822
|
.option('-n, --number <x>', 'AUTO-PICK x eligible items and advance them IN SEQUENCE (ordered by selectionOrder, default drain = tasks-first then prds-to-task). Sequential — never a parallelism knob (that is `run` / the CI matrix). Mutually exclusive with naming items.')
|
|
1719
1823
|
.option('--isolated', "advance in an ISOLATED worktree off THIS repo's arbiter (inferred from cwd) instead of taking over the current checkout, then integrate + reap — the in-place-but-isolated form. Shares the same grammar: 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). Lets you advance from a busy/dirty checkout or anywhere with a participating arbiter.")
|
|
1720
1824
|
.option('--selection-order <order>', 'order the auto-pick pools (build/task/surface/triage; apply is always first): a preset keyword (drain (default) | groom) or an explicit comma-separated pool list. Resolved flag > env > per-repo > global > default.')
|
|
1721
1825
|
.option('--observation-triage <mode>', 'the observation-inbox gate (off|ask|auto): off (default) leaves observations untouched (the triage pool is dropped from auto-pick); ask surfaces a promote/keep/delete question for each untriaged observation; auto auto-disposes the no-question cases (duplicate/map) and asks about the rest. Resolved flag > env > per-repo > global > default. An explicit `advance obs:<slug>` bypasses the selection gate and runs in ask-mode (auto-disposes only under `auto`).')
|
|
1722
|
-
.option('--surface-blockers', 'the declared-blocked-work gate (the orthogonal peer of --observation-triage): render a task/
|
|
1723
|
-
.option('--no-surface-blockers', 'leave a needsAnswers:true task/
|
|
1724
|
-
.option('--strict-merge-approval', 'opt in to the host-agnostic "dismiss stale approvals on base change" discipline (
|
|
1826
|
+
.option('--surface-blockers', '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).')
|
|
1827
|
+
.option('--no-surface-blockers', 'leave a needsAnswers:true task/spec silently blocked (default; the blocked pool is dropped from auto-pick)')
|
|
1828
|
+
.option('--strict-merge-approval', '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.')
|
|
1725
1829
|
.option('--no-strict-merge-approval', 'honour the prior merge-answer and land when the rebased tip re-verifies GREEN even if the merge-base changed (default; the cheap green-re-verify-is-enough path)')
|
|
1726
1830
|
.option('--merge-questions <mode>', 'the merge-question SURFACER gate (off|ask|auto): off drops the surfacer (only for a repo that lands by some other means); ask (default) enumerates unmerged `work/*` branches and surfaces a merge-question sidecar a human answers; auto self-supplies the `merge` answer and lands via the SAME deterministic apply-time re-verify (the merge-mode-like fast path). SEPARATE axis from --observation-triage with a HIGHER default (a dropped merge-question means pushed work never lands). Resolved flag > env > per-repo > global > default ask.')
|
|
1727
1831
|
.option('--merge', 'integrate the advanced item(s) in merge mode this invocation (mutually exclusive with --propose; overrides config). The CI merge shape is a SINGLE SEQUENTIAL job, so this rides the `-n`/named-sequence path, never the matrix.')
|
|
1728
1832
|
.option('--propose', 'integrate the advanced item(s) in propose mode this invocation (default; mutually exclusive with --merge; overrides config). The CI propose shape is the parallel matrix (one PR per item).')
|
|
1729
|
-
.option('--tasks-land-in <where>', 'where `advance
|
|
1833
|
+
.option('--tasks-land-in <where>', '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.')
|
|
1730
1834
|
.option('--watch', "stream the build agent's high-signal events live by tailing the pi session log (requires harness: pi; READ-ONLY observer — does not change outcome/gate/git). The same view `do --watch` gives, threaded through the build rung; CI uses it so the job log shows the agent working instead of freezing.")
|
|
1731
1835
|
.action(async (rawSlugs, flags) => {
|
|
1732
1836
|
// Variadic grammar (mirrors `do`): zero args = AUTO-PICK; one = the single
|
|
@@ -1842,7 +1946,7 @@ export function buildProgram() {
|
|
|
1842
1946
|
integration: remoteConfig.integration,
|
|
1843
1947
|
// EXPLICIT `--merge` override for the untrusted-origin build-propose rule.
|
|
1844
1948
|
explicitMerge: flagMode === 'merge',
|
|
1845
|
-
// Per-TRANSITION TASKING override (the isolated `do --remote
|
|
1949
|
+
// Per-TRANSITION TASKING override (the isolated `do --remote spec:` path).
|
|
1846
1950
|
taskingIntegration: remoteConfig.taskingIntegration,
|
|
1847
1951
|
// TASK-PLACEMENT: configured default + EXPLICIT `--tasks-land-in` override
|
|
1848
1952
|
// (set only when typed, mirroring `explicitMerge`).
|
|
@@ -1852,7 +1956,7 @@ export function buildProgram() {
|
|
|
1852
1956
|
verify: remoteConfig.verify,
|
|
1853
1957
|
// Single-job build path: gate the REBASED tip (the default) unconditionally.
|
|
1854
1958
|
freshWorktreeGate: remoteConfig.freshWorktreeGate,
|
|
1855
|
-
// Cross-job merge-serialiser CAS-retry cap —
|
|
1959
|
+
// Cross-job merge-serialiser CAS-retry cap — spec
|
|
1856
1960
|
// `land-time-reverify-and-parallel-merge-ceiling` Story 5.
|
|
1857
1961
|
mergeRetries: remoteConfig.mergeRetries,
|
|
1858
1962
|
noPR: remoteConfig.noPR,
|
|
@@ -1980,7 +2084,7 @@ export function buildProgram() {
|
|
|
1980
2084
|
// EXPLICIT `--merge` override for the untrusted-origin build-propose rule (a
|
|
1981
2085
|
// bare `advance` auto-pick passes no flag ⇒ unset ⇒ untrusted forces propose).
|
|
1982
2086
|
explicitMerge: flagMode === 'merge',
|
|
1983
|
-
// Per-TRANSITION TASKING override (the `do
|
|
2087
|
+
// Per-TRANSITION TASKING override (the `do spec:` tasking path threads
|
|
1984
2088
|
// `taskingIntegration ?? integration`; the build path stays on `integration`).
|
|
1985
2089
|
taskingIntegration: config.taskingIntegration,
|
|
1986
2090
|
// TASK-PLACEMENT: configured default + EXPLICIT `--tasks-land-in` override
|
|
@@ -1991,7 +2095,7 @@ export function buildProgram() {
|
|
|
1991
2095
|
verify: config.verify,
|
|
1992
2096
|
// Single-job build path: gate the REBASED tip (the default) unconditionally.
|
|
1993
2097
|
freshWorktreeGate: config.freshWorktreeGate,
|
|
1994
|
-
// Cross-job merge-serialiser CAS-retry cap (
|
|
2098
|
+
// Cross-job merge-serialiser CAS-retry cap (spec `land-time-reverify-and-
|
|
1995
2099
|
// parallel-merge-ceiling` Story 5 / Applied Answer q1 (a)).
|
|
1996
2100
|
mergeRetries: config.mergeRetries,
|
|
1997
2101
|
noPR: config.noPR,
|
|
@@ -2013,6 +2117,10 @@ export function buildProgram() {
|
|
|
2013
2117
|
taskReviewGate: config.review
|
|
2014
2118
|
? harnessTaskAcceptanceGate({ harness, agentCmd: config.agentCmd })
|
|
2015
2119
|
: undefined,
|
|
2120
|
+
// The dorfl-internal agent deadline + anti-loop ceiling (spec
|
|
2121
|
+
// `graceful-pre-timeout-wip-checkpoint`). Threaded from resolved config.
|
|
2122
|
+
agentDeadlineMinutes: config.agentDeadlineMinutes,
|
|
2123
|
+
maxAutoCheckpoints: config.maxAutoCheckpoints,
|
|
2016
2124
|
color: shouldUseColor(process.stdout),
|
|
2017
2125
|
note: (message) => console.error(`>> ${message}`),
|
|
2018
2126
|
noteBlock: (message) => console.error(message),
|
|
@@ -2109,7 +2217,7 @@ export function buildProgram() {
|
|
|
2109
2217
|
.option('--force', 'OVERRIDE the predicate: remove worktrees even with un-saved work (requires --yes; never the default)')
|
|
2110
2218
|
.option('--yes', 'confirm a destructive --force sweep non-interactively')
|
|
2111
2219
|
.option('--ledger [repoPath]', 'SWEEP the work/ lifecycle LEDGER instead of job worktrees: REPORT (never delete) every slug present in more than one work/ status folder (the one-slug-one-folder belt-and-suspenders), with its folders + candidate canonical folder, for a HUMAN to resolve. Defaults to the cwd repo.')
|
|
2112
|
-
.option('--reap-stale-locks', '(with --ledger) OPT-IN: also CLEAR every
|
|
2220
|
+
.option('--reap-stale-locks', '(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).')
|
|
2113
2221
|
.option('--remote-branches', 'SWEEP the arbiter’s remote work/* BRANCHES instead of job worktrees: delete (via git push --delete, NEVER --force) exactly those PROVABLY MERGED into <arbiter>/main (git merge-base --is-ancestor, the SAME predicate the worktree reaper uses), and RETAIN the rest with a reason. An in-flight/un-merged branch (the recovery point) is NEVER touched. Provider-agnostic plain git — works on a --bare arbiter. The merged-only complement of `requeue --reset`.')
|
|
2114
2222
|
.option('--arbiter <remote>', '(with --remote-branches) the arbiter git remote whose work/* branches to sweep (default: origin); resolved from --cwd')
|
|
2115
2223
|
.option('--cwd <dir>', '(with --remote-branches) the local repo/clone whose --arbiter remote points at the arbiter to sweep (default: cwd); only remote refs are read + deleted, never the working tree')
|
|
@@ -2118,7 +2226,7 @@ export function buildProgram() {
|
|
|
2118
2226
|
.action(async (flags) => {
|
|
2119
2227
|
const config = resolveGlobalConfig(loadConfig(flags.config), {});
|
|
2120
2228
|
const workspacesDir = flags.workspace ?? config.workspacesDir;
|
|
2121
|
-
// The `gc`-STYLE ledger SWEEP (
|
|
2229
|
+
// The `gc`-STYLE ledger SWEEP (spec `ledger-integrity` story 3): a SEPARATE
|
|
2122
2230
|
// surface from the worktree reaper below — it REPORTS one-slug-one-folder
|
|
2123
2231
|
// violations in a repo's `work/` lifecycle ledger and NEVER deletes (a human
|
|
2124
2232
|
// resolves each). Distinct `work/`: the ledger, not the execution substrate.
|
|
@@ -2126,7 +2234,7 @@ export function buildProgram() {
|
|
|
2126
2234
|
const repoPath = typeof flags.ledger === 'string' ? flags.ledger : process.cwd();
|
|
2127
2235
|
const result = sweepLedgerDuplicates(repoPath);
|
|
2128
2236
|
// The UNIFIED-LOCK stuck/orphaned-lock REPORT (task
|
|
2129
|
-
// `release-lock-verb-and-gc-stuck-report`,
|
|
2237
|
+
// `release-lock-verb-and-gc-stuck-report`, spec
|
|
2130
2238
|
// `ledger-status-per-item-lock-refs` US #12/#13/#14): generalises the
|
|
2131
2239
|
// advancing-marker report from advancing-only to the unified per-item
|
|
2132
2240
|
// lock. The locks live on the ARBITER ref (`refs/dorfl/lock/*`),
|
|
@@ -2139,13 +2247,16 @@ export function buildProgram() {
|
|
|
2139
2247
|
// clearing (no auto-sweep; a human asserts a lock is dead via
|
|
2140
2248
|
// `release-lock`).
|
|
2141
2249
|
// OPT-IN SWEEP (`--reap-stale-locks`): the WRITE twin of the report. A
|
|
2142
|
-
// human asserting "clear the dead TERMINAL locks now": for
|
|
2143
|
-
//
|
|
2144
|
-
//
|
|
2145
|
-
//
|
|
2146
|
-
//
|
|
2147
|
-
//
|
|
2148
|
-
//
|
|
2250
|
+
// human asserting "clear the dead TERMINAL locks now": for BOTH
|
|
2251
|
+
// terminal-on-main orphan classes (task `reaper-reap-terminal-stuck-lock-orphans`;
|
|
2252
|
+
// ADR `ledger-status-on-per-item-lock-refs` § Addendum 2026-07-10) — the
|
|
2253
|
+
// stranded `cleared-stale` AND the stuck-terminal `cleared-stuck-terminal`
|
|
2254
|
+
// crash-orphan — perform the SAME leased delete `release-lock` / the
|
|
2255
|
+
// recovery use, so one command sweeps every orphaned terminal lock. A
|
|
2256
|
+
// `kept-stuck` (STUCK + NON-terminal) / `kept-in-flight` lock is NEVER
|
|
2257
|
+
// reaped (scope fence); a concurrent change makes a clear REJECT (reported
|
|
2258
|
+
// `lost`), never --force. WITHOUT the flag the surface below stays
|
|
2259
|
+
// report-only (fail-loud, deletes nothing).
|
|
2149
2260
|
if (flags.reapStaleLocks) {
|
|
2150
2261
|
const reap = await reapStaleItemLocks(flags.cwd ?? repoPath, flags.arbiter ?? 'origin', process.env);
|
|
2151
2262
|
if (flags.json) {
|
|
@@ -2164,10 +2275,11 @@ export function buildProgram() {
|
|
|
2164
2275
|
? blocks.join('\n\n')
|
|
2165
2276
|
: formatLedgerSweep(result));
|
|
2166
2277
|
}
|
|
2167
|
-
// Fail-loud AFTER the sweep: a `kept-stuck` (
|
|
2168
|
-
// a `lost`/`error` (
|
|
2169
|
-
// needs attention; a clean sweep
|
|
2170
|
-
//
|
|
2278
|
+
// Fail-loud AFTER the sweep: a `kept-stuck` (STUCK + non-terminal —
|
|
2279
|
+
// rightly left for a human) or a `lost`/`error` (an orphan lock whose
|
|
2280
|
+
// leased delete lost the race) still needs attention; a clean sweep
|
|
2281
|
+
// that reaped every orphan lock (stale OR stuck-terminal) and left only
|
|
2282
|
+
// healthy in-flight holds exits 0.
|
|
2171
2283
|
process.exit(result.duplicates.length > 0 || reapReportNeedsAttention(reap)
|
|
2172
2284
|
? 1
|
|
2173
2285
|
: 0);
|
|
@@ -2202,14 +2314,16 @@ export function buildProgram() {
|
|
|
2202
2314
|
// ALL are REPORTED here (never auto-deleted — no automatic sweep exists; a
|
|
2203
2315
|
// human clears a NAMED unified lock via `release-lock`).
|
|
2204
2316
|
//
|
|
2205
|
-
// SCOPED to the ATTENTION verdicts only (
|
|
2206
|
-
// `ledger-status-on-per-item-lock-refs
|
|
2207
|
-
// crash-orphaned lock, NOT every held one): a `kept-stuck`
|
|
2208
|
-
//
|
|
2209
|
-
//
|
|
2210
|
-
//
|
|
2211
|
-
//
|
|
2212
|
-
//
|
|
2317
|
+
// SCOPED to the ATTENTION verdicts only (spec US#14/#21, ADR
|
|
2318
|
+
// `ledger-status-on-per-item-lock-refs` § Addendum 2026-07-10: this surface
|
|
2319
|
+
// is the STUCK / crash-orphaned lock, NOT every held one): a `kept-stuck`
|
|
2320
|
+
// (STUCK + NON-terminal — the genuine human-attention case), a
|
|
2321
|
+
// `cleared-stale`-eligible (terminal + stale active = stranded orphan), or
|
|
2322
|
+
// a `cleared-stuck-terminal`-eligible (terminal + stuck = crash-orphan the
|
|
2323
|
+
// reaper can now auto-clear) fails loud, but a `kept-in-flight` (active,
|
|
2324
|
+
// non-terminal) lock is the NORMAL in-flight state of a healthy concurrent
|
|
2325
|
+
// build (read by `status` as healthy) — it is reported informationally
|
|
2326
|
+
// and does NOT make a routine `gc --ledger` health check exit non-zero.
|
|
2213
2327
|
process.exit(result.duplicates.length > 0 ||
|
|
2214
2328
|
itemLockReportNeedsAttention(lockReport)
|
|
2215
2329
|
? 1
|
|
@@ -2229,7 +2343,7 @@ export function buildProgram() {
|
|
|
2229
2343
|
dryRun: flags.dryRun === true,
|
|
2230
2344
|
note: (message) => console.error(`>> ${message}`),
|
|
2231
2345
|
});
|
|
2232
|
-
// The ORPHAN-SIDECAR sweep (
|
|
2346
|
+
// The ORPHAN-SIDECAR sweep (spec
|
|
2233
2347
|
// `agentic-question-resolution-retire-disposition-vocabulary`, US #10) rides
|
|
2234
2348
|
// the SAME `--remote-branches` invocation the SCHEDULED CI lifecycle workflow
|
|
2235
2349
|
// runs (`dorfl gc --remote-branches --arbiter origin`) — so the reap of a
|
|
@@ -2328,7 +2442,7 @@ export function buildProgram() {
|
|
|
2328
2442
|
printPrdToSpecReport(result);
|
|
2329
2443
|
}
|
|
2330
2444
|
// Exit non-zero on a REFUSAL (quiescence gate) or a non-green leak scan
|
|
2331
|
-
// (a converted tree that still carries a dangling
|
|
2445
|
+
// (a converted tree that still carries a dangling ''prd'' ref). A clean
|
|
2332
2446
|
// dry-run or a green migration exits 0.
|
|
2333
2447
|
if (result.refused || result.leaks.length > 0) {
|
|
2334
2448
|
process.exit(1);
|
|
@@ -2420,14 +2534,16 @@ export function buildProgram() {
|
|
|
2420
2534
|
program
|
|
2421
2535
|
.command('requeue <slug>')
|
|
2422
2536
|
.helpGroup(HEADLINE_GROUP)
|
|
2423
|
-
.description('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 =
|
|
2537
|
+
.description('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).')
|
|
2424
2538
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
2425
2539
|
.option('--cwd <dir>', 'the repo/working clone whose work/ tree the arbiter remote is resolved FROM (default: cwd) — an ORIGIN SOURCE only; the move is published to the arbiter, never to this tree')
|
|
2426
2540
|
.option('--arbiter <remote>', '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.')
|
|
2427
|
-
.option('--
|
|
2541
|
+
.option('--reconcile', '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.')
|
|
2542
|
+
.option('--rebase', 'alias of --reconcile (canonical name)')
|
|
2543
|
+
.option('--reset', '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.')
|
|
2428
2544
|
.option('-m, --message <note>', 'append a dated handoff note to the item body for the next agent (append-only; applies to both default and --reset)')
|
|
2429
2545
|
.action(async (rawSlug, flags) => {
|
|
2430
|
-
// Task-only command (§3a): accept bare + `task:`, reject `
|
|
2546
|
+
// Task-only command (§3a): accept bare + `task:`, reject `spec:`.
|
|
2431
2547
|
const slug = resolveTaskOnlySlug(rawSlug);
|
|
2432
2548
|
const cwd = flags.cwd ?? process.cwd();
|
|
2433
2549
|
// Route the requeue (default keep+continue / --reset discard / -m handoff)
|
|
@@ -2442,6 +2558,12 @@ export function buildProgram() {
|
|
|
2442
2558
|
// call site, rather than relying on the seam's silent `?? process.env`
|
|
2443
2559
|
// default by omission (the implicit fallback that made `requeue`'s human
|
|
2444
2560
|
// attribution accidental rather than declared).
|
|
2561
|
+
const reconcile = flags.reconcile === true || flags.rebase === true;
|
|
2562
|
+
if (reconcile && flags.reset) {
|
|
2563
|
+
console.error('error: --reconcile and --reset are mutually exclusive ' +
|
|
2564
|
+
'(non-destructive recovery vs destructive last resort). Pick one.');
|
|
2565
|
+
process.exit(1);
|
|
2566
|
+
}
|
|
2445
2567
|
const result = await ledgerWrite.applyReturnToBacklogTransition({
|
|
2446
2568
|
cwd,
|
|
2447
2569
|
slug,
|
|
@@ -2450,6 +2572,7 @@ export function buildProgram() {
|
|
|
2450
2572
|
// `--cwd` is purely the ORIGIN SOURCE the remote is resolved from.
|
|
2451
2573
|
arbiter: flags.arbiter ?? 'origin',
|
|
2452
2574
|
reset: flags.reset,
|
|
2575
|
+
reconcile,
|
|
2453
2576
|
message: flags.message,
|
|
2454
2577
|
env: process.env,
|
|
2455
2578
|
note: (message) => console.error(`>> ${message}`),
|
|
@@ -2460,15 +2583,17 @@ export function buildProgram() {
|
|
|
2460
2583
|
}
|
|
2461
2584
|
const how = result.deletedRemoteBranch
|
|
2462
2585
|
? ` (--reset: deleted the remote ${workBranchRef('task', slug)} branch; next claim starts fresh)`
|
|
2463
|
-
:
|
|
2586
|
+
: result.reconciled
|
|
2587
|
+
? ` (--reconcile: re-synced mirror + rebased ${workBranchRef('task', slug)} onto latest main and pushed the reconciled tip back; next claim continues from it)`
|
|
2588
|
+
: ' (kept the work branch; next claim continues from its tip)';
|
|
2464
2589
|
console.log(`Requeued '${slug}' to backlog for re-claiming.${how}`);
|
|
2465
2590
|
});
|
|
2466
|
-
// `promote [item]` (
|
|
2591
|
+
// `promote [item]` (spec `staging-pool-position-gate-and-trust-model`, tasks
|
|
2467
2592
|
// `pre-backlog-staging-folder-and-promote-step-a` /
|
|
2468
2593
|
// `pre-prd-staging-pool-split-and-untrusted-prd-placement`): the HUMAN/runner-
|
|
2469
2594
|
// owned verb that moves a STAGED item into its agent-eligible POOL — a task
|
|
2470
|
-
// `work/
|
|
2471
|
-
// `work/
|
|
2595
|
+
// `work/tasks/backlog/<slug>.md → work/tasks/ready/<slug>.md`, a spec
|
|
2596
|
+
// `work/specs/proposed/<slug>.md → work/specs/ready/<slug>.md` — as a tree-less CAS on the
|
|
2472
2597
|
// arbiter, the SAME trust model + mechanism as `requeue`. The agent emits STAGED;
|
|
2473
2598
|
// only this verb (a human, or the runner) admits it to the pool. With NO argument
|
|
2474
2599
|
// it LISTS what is promotable (the "what is staged waiting for me?" discovery), so
|
|
@@ -2476,7 +2601,7 @@ export function buildProgram() {
|
|
|
2476
2601
|
program
|
|
2477
2602
|
.command('promote [item]')
|
|
2478
2603
|
.helpGroup(HEADLINE_GROUP)
|
|
2479
|
-
.description(
|
|
2604
|
+
.description(`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.`)
|
|
2480
2605
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
2481
2606
|
.option('--cwd <dir>', 'the repo/working clone whose arbiter remote the tree-less move is resolved FROM (default: cwd) — an ORIGIN SOURCE only; the move is published to the arbiter, never to this tree')
|
|
2482
2607
|
.option('--arbiter <remote>', 'the arbiter git remote the promotion is CAS-published to / the staging folders are listed from (default: origin)')
|
|
@@ -2495,7 +2620,7 @@ export function buildProgram() {
|
|
|
2495
2620
|
process.exit(1);
|
|
2496
2621
|
}
|
|
2497
2622
|
if (listed.items.length === 0) {
|
|
2498
|
-
console.log(`Nothing staged to promote on ${arbiter}/main (
|
|
2623
|
+
console.log(`Nothing staged to promote on ${arbiter}/main (${workFolderPrefix('tasks-backlog')} and ${workFolderPrefix('specs-proposed')} are empty).`);
|
|
2499
2624
|
return;
|
|
2500
2625
|
}
|
|
2501
2626
|
console.log('Staged, awaiting promotion (run `promote <item>`):');
|
|
@@ -2504,11 +2629,10 @@ export function buildProgram() {
|
|
|
2504
2629
|
}
|
|
2505
2630
|
return;
|
|
2506
2631
|
}
|
|
2507
|
-
// AN ITEM → promote it. `task:`/`spec:` are explicit
|
|
2508
|
-
//
|
|
2509
|
-
//
|
|
2510
|
-
//
|
|
2511
|
-
// no pool).
|
|
2632
|
+
// AN ITEM → promote it. `task:`/`spec:` are explicit; a bare slug defaults to
|
|
2633
|
+
// a task (mirrors `requeue`). An `obs:`/`observation:` prefix is rejected
|
|
2634
|
+
// (observations have no pool). A legacy ''prd:'' prefix is a DEAD namespace token
|
|
2635
|
+
// after the hard cutover — it resolves as a bare literal task slug, not a spec.
|
|
2512
2636
|
const parsed = parseSlugArg(rawItem);
|
|
2513
2637
|
if (parsed.explicit === 'observation') {
|
|
2514
2638
|
console.error(`error: promote takes a task or spec, not an observation ('${rawItem}'). Observations have no agent pool.`);
|
|
@@ -2535,7 +2659,7 @@ export function buildProgram() {
|
|
|
2535
2659
|
// the `work/advancing/<entry>.md` marker is gone and an advance hold is now just
|
|
2536
2660
|
// `action: advance` on the UNIFIED per-item lock, so `release-lock <item>` (below)
|
|
2537
2661
|
// is the SOLE named human release for ALL holds (implement/task/advance).
|
|
2538
|
-
// `release-lock <item>` (task `release-lock-verb-and-gc-stuck-report`,
|
|
2662
|
+
// `release-lock <item>` (task `release-lock-verb-and-gc-stuck-report`, spec
|
|
2539
2663
|
// `ledger-status-per-item-lock-refs` US #14): the HUMAN-invoked named release of
|
|
2540
2664
|
// a stuck/orphaned UNIFIED per-item lock (`refs/dorfl/lock/<entry>`) —
|
|
2541
2665
|
// the GENERALISATION of `release-advancing` from the advancing-only marker to
|
|
@@ -2548,31 +2672,67 @@ export function buildProgram() {
|
|
|
2548
2672
|
// Idempotent: deleting an absent ref is a clean exit-0 "nothing to clear"
|
|
2549
2673
|
// (`not-held`), NOT a failure — deleting the lock ref(s) is "all locks released"
|
|
2550
2674
|
// and recoverable (the work is safe on the `work/<slug>` branches + `main`).
|
|
2675
|
+
//
|
|
2676
|
+
// ESCAPE HATCH (`--entry <literal>`, task
|
|
2677
|
+
// `release-lock-entry-escape-hatch-and-literal-entry-reporting`): the item
|
|
2678
|
+
// positional is OPTIONAL because a lock whose entry name is NOT derivable from
|
|
2679
|
+
// any current item-form (a PRE-CUTOVER `slice-<slug>` / `prd-<slug>` entry minted
|
|
2680
|
+
// before the slice→task / `prd-to-spec` vocabulary cutover) is UN-NAMEABLE through
|
|
2681
|
+
// the item-form path. `--entry` takes the literal `<entry>` VERBATIM (bypassing
|
|
2682
|
+
// the namespace mapping) so a human can still NAME + clear such an orphan without
|
|
2683
|
+
// the raw `git push origin --delete refs/dorfl/lock/…` plumbing. It reuses the
|
|
2684
|
+
// SAME leased-delete path (`releaseLiteralLockEntry` → the shared entry-keyed
|
|
2685
|
+
// core), so the trust model is unchanged: the human still asserts liveness.
|
|
2686
|
+
// EXACTLY ONE of {item positional, --entry} must be given.
|
|
2551
2687
|
program
|
|
2552
|
-
.command('release-lock
|
|
2688
|
+
.command('release-lock [item]')
|
|
2553
2689
|
.helpGroup(HEADLINE_GROUP)
|
|
2554
|
-
.description('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>` / `
|
|
2690
|
+
.description('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).')
|
|
2555
2691
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
2692
|
+
.option('--entry <literal>', '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.')
|
|
2556
2693
|
.option('--cwd <dir>', 'the repo/working clone whose arbiter remote the lock ref is DELETED on (default: cwd)')
|
|
2557
2694
|
.option('--arbiter <remote>', 'the arbiter git remote the lock ref is deleted on (default: origin)')
|
|
2558
2695
|
.action(async (item, flags) => {
|
|
2559
2696
|
const cwd = flags.cwd ?? process.cwd();
|
|
2560
2697
|
const arbiter = flags.arbiter ?? 'origin';
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2698
|
+
// MUTUAL EXCLUSION: exactly one of {item positional, --entry}. BOTH or
|
|
2699
|
+
// NEITHER is a usage error (non-zero exit, actionable message) — the
|
|
2700
|
+
// item-form and the literal escape hatch are two ways to name ONE lock, not
|
|
2701
|
+
// combinable.
|
|
2702
|
+
if (item !== undefined && flags.entry !== undefined) {
|
|
2703
|
+
console.error('error: give EITHER an item (task:<slug> / spec:<slug> / obs:<slug> / <slug>) OR --entry <literal>, not both.');
|
|
2704
|
+
process.exit(1);
|
|
2705
|
+
}
|
|
2706
|
+
if (item === undefined && flags.entry === undefined) {
|
|
2707
|
+
console.error('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.');
|
|
2708
|
+
process.exit(1);
|
|
2709
|
+
}
|
|
2710
|
+
// The LITERAL escape-hatch path (`--entry`): take the entry name verbatim,
|
|
2711
|
+
// bypassing the namespace mapping, and reuse the SAME leased-delete core.
|
|
2712
|
+
const result = flags.entry !== undefined
|
|
2713
|
+
? await releaseLiteralLockEntry({
|
|
2714
|
+
entry: flags.entry,
|
|
2715
|
+
cwd,
|
|
2716
|
+
arbiter,
|
|
2717
|
+
env: process.env,
|
|
2718
|
+
})
|
|
2719
|
+
: await releaseItemLock({
|
|
2720
|
+
item: item,
|
|
2721
|
+
cwd,
|
|
2722
|
+
arbiter,
|
|
2723
|
+
env: process.env,
|
|
2724
|
+
});
|
|
2567
2725
|
if (result.outcome === 'released') {
|
|
2568
2726
|
console.log(`Released lock '${result.entry}' (${result.ref} deleted on ${arbiter}; the item itself was untouched — it rests on main / its work/<slug> branch).`);
|
|
2569
2727
|
return;
|
|
2570
2728
|
}
|
|
2571
|
-
// IDEMPOTENT exit semantics:
|
|
2729
|
+
// IDEMPOTENT exit semantics: the release returns `not-held` when the
|
|
2572
2730
|
// ref is ALREADY absent. For a HUMAN re-running the verb on an
|
|
2573
2731
|
// already-cleared lock that is the CORRECT "nothing to clear" outcome —
|
|
2574
2732
|
// deleting the lock ref(s) is "all locks released" and recoverable — so map
|
|
2575
|
-
// it to a clean exit-0 with an honest message (NOT a failure).
|
|
2733
|
+
// it to a clean exit-0 with an honest message (NOT a failure). The message
|
|
2734
|
+
// names the LITERAL entry (`refs/dorfl/lock/<entry>`) so an operator can
|
|
2735
|
+
// copy-paste it straight back into a `--entry` invocation.
|
|
2576
2736
|
if (result.outcome === 'not-held') {
|
|
2577
2737
|
console.log(`No lock to release for '${result.entry}' (${result.ref} is already absent on ${arbiter} — “all locks released”, recoverable).`);
|
|
2578
2738
|
return;
|
|
@@ -2580,12 +2740,47 @@ export function buildProgram() {
|
|
|
2580
2740
|
console.error(`error: ${result.message}`);
|
|
2581
2741
|
process.exit(1);
|
|
2582
2742
|
});
|
|
2583
|
-
// `
|
|
2743
|
+
// `migrate-stuck-locks` (spec `surface-stuck-as-questions-and-retire-stuck-lock-state`,
|
|
2744
|
+
// resolved decision #3, user story 5; task `migrate-existing-stuck-locks-one-shot`):
|
|
2745
|
+
// the ONE-SHOT ROLLOUT migration that converts every pre-existing `stuck` lock
|
|
2746
|
+
// ref (an item bounced under the OLD model, before `retire-stuck-lock-state`)
|
|
2747
|
+
// into the new resting shape (`needsAnswers:true` + `stuck`-kind sidecar on
|
|
2748
|
+
// `<arbiter>/main` + lock released) via the SAME surface-first-release-second
|
|
2749
|
+
// transition the new bounce uses. Idempotent (a re-run finds no legacy stuck
|
|
2750
|
+
// ref → clean no-op) and bare-arbiter-safe (a ref is a ref). Its own verb (not
|
|
2751
|
+
// folded into `gc --ledger`) because it is a one-shot rollout event with WRITE
|
|
2752
|
+
// semantics on `main`, distinct from `gc`'s report-oriented + terminal-orphan
|
|
2753
|
+
// reap surface — the two are different natures and their exit-code contracts
|
|
2754
|
+
// stay separate.
|
|
2755
|
+
program
|
|
2756
|
+
.command('migrate-stuck-locks')
|
|
2757
|
+
.helpGroup(ADVANCED_GROUP)
|
|
2758
|
+
.description('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.')
|
|
2759
|
+
.option('--cwd <dir>', '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)')
|
|
2760
|
+
.option('--arbiter <remote>', 'the arbiter git remote the migration reads + mutates (default: origin)')
|
|
2761
|
+
.action(async (flags) => {
|
|
2762
|
+
const cwd = flags.cwd ?? process.cwd();
|
|
2763
|
+
const arbiter = flags.arbiter ?? 'origin';
|
|
2764
|
+
const note = (message) => console.error(`>> ${message}`);
|
|
2765
|
+
const report = await migrateStuckLocks({
|
|
2766
|
+
cwd,
|
|
2767
|
+
arbiter,
|
|
2768
|
+
env: process.env,
|
|
2769
|
+
note,
|
|
2770
|
+
});
|
|
2771
|
+
for (const line of formatMigrateStuckLocksReport(report)) {
|
|
2772
|
+
console.log(line);
|
|
2773
|
+
}
|
|
2774
|
+
if (migrateStuckLocksNeedsAttention(report)) {
|
|
2775
|
+
process.exit(1);
|
|
2776
|
+
}
|
|
2777
|
+
});
|
|
2778
|
+
// `drop <slug>` (spec `agentic-question-resolution-retire-disposition-vocabulary`,
|
|
2584
2779
|
// US #5/#11; task `direct-delete-question-cli-helper`): the DIRECT "throw it
|
|
2585
2780
|
// away" verb — `git rm` a source item AND its question sidecar (when present) in
|
|
2586
2781
|
// ONE revertible commit, the reason in the commit MESSAGE (git history is the
|
|
2587
2782
|
// archive). It does NOT round-trip through the decision engine or spawn an agent
|
|
2588
|
-
// (that is the SEPARATE agentic `
|
|
2783
|
+
// (that is the SEPARATE agentic `dispose` verdict in apply-persist.ts);
|
|
2589
2784
|
// this is the human/skill/CLI no-ceremony delete of decision 7. DISTINCT from
|
|
2590
2785
|
// the existing `remote rm` (the hub-MIRROR deleter) — different concern, no
|
|
2591
2786
|
// collision. A LOCAL one-commit primitive over the working tree (like apply): it
|
|
@@ -2594,8 +2789,7 @@ export function buildProgram() {
|
|
|
2594
2789
|
program
|
|
2595
2790
|
.command('drop <slug>')
|
|
2596
2791
|
.helpGroup(HEADLINE_GROUP)
|
|
2597
|
-
.description('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>` / `
|
|
2598
|
-
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
2792
|
+
.description('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).')
|
|
2599
2793
|
.option('--cwd <dir>', 'the working clone the revertible delete commit is made in (default: cwd)')
|
|
2600
2794
|
.option('--reason <text>', 'why you are throwing this away — recorded in the commit MESSAGE (git history is the archive). Optional; recorded as "(no reason given)" when omitted.')
|
|
2601
2795
|
.action((slug, flags) => {
|
|
@@ -2622,7 +2816,7 @@ export function buildProgram() {
|
|
|
2622
2816
|
program
|
|
2623
2817
|
.command('intake')
|
|
2624
2818
|
.helpGroup(HEADLINE_GROUP)
|
|
2625
|
-
.description(
|
|
2819
|
+
.description(`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.`)
|
|
2626
2820
|
.argument('<number>', 'the GitHub issue number to intake (e.g. `intake 42`)')
|
|
2627
2821
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
2628
2822
|
.option('--arbiter <remote>', 'name of the arbiter git remote (default: per-repo/global defaultArbiter)')
|
|
@@ -2633,7 +2827,7 @@ export function buildProgram() {
|
|
|
2633
2827
|
.option('--propose-spec', 'integrate a spec outcome in propose mode (granular; overrides --merge/--propose for a spec; mutually exclusive with --merge-spec)')
|
|
2634
2828
|
.option('--merge-task', 'integrate a task outcome in merge mode (granular; overrides --merge/--propose for a task; mutually exclusive with --propose-task)')
|
|
2635
2829
|
.option('--propose-task', 'integrate a task outcome in propose mode (granular; overrides --merge/--propose for a task; mutually exclusive with --merge-task)')
|
|
2636
|
-
.option('--origin-trust <trusted|untrusted>', "the author-trust verdict to STAMP onto the emitted
|
|
2830
|
+
.option('--origin-trust <trusted|untrusted>', "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.")
|
|
2637
2831
|
.option('--specs-land-in <where>', 'where an intake-authored spec lands: `pre-proposed` (staged, not auto-taskable) or `ready` (the auto-tasking pool). The EXPLICIT operator override at the top of the placement precedence (explicit flag > untrusted-origin forces staging > specsLandIn default > built-in). Resolved flag > env (DORFL_SPECS_LAND_IN) > per-repo > global > built-in.')
|
|
2638
2832
|
.option('--agent-cmd <cmd>', 'command to run the decision agent')
|
|
2639
2833
|
.option('--model <id>', 'model the decision agent runs on (routing intent; resolved flag > env > per-repo > global > default)')
|
|
@@ -2664,7 +2858,7 @@ export function buildProgram() {
|
|
|
2664
2858
|
console.error(`>> ${resolved.message}`);
|
|
2665
2859
|
}
|
|
2666
2860
|
const config = resolved.config;
|
|
2667
|
-
// Resolve the PER-OUTCOME integration modes (
|
|
2861
|
+
// Resolve the PER-OUTCOME integration modes (spec US #9): `intake` decides
|
|
2668
2862
|
// the artifact TYPE at runtime, so a single --merge/--propose can't express
|
|
2669
2863
|
// a type-conditional policy. The granular flags override the aggregate; an
|
|
2670
2864
|
// UNSET type falls back to the per-repo/global `integration` (the SAME chain
|
|
@@ -2715,6 +2909,13 @@ export function buildProgram() {
|
|
|
2715
2909
|
cwd,
|
|
2716
2910
|
arbiter: flags.arbiter ?? config.defaultArbiter,
|
|
2717
2911
|
integration: modes,
|
|
2912
|
+
// The resolved cross-job CAS-retry cap (config `mergeRetries`). `intake`
|
|
2913
|
+
// has no `--merge-retries` flag today (task
|
|
2914
|
+
// `thread-merge-retries-cross-task-and-ratify-default` deliberately does
|
|
2915
|
+
// NOT add one — intake is unattended-CI-first, env + per-repo cover the
|
|
2916
|
+
// need); env + per-repo + global fold into `config.mergeRetries` via the
|
|
2917
|
+
// SAME chain, and unset falls through to the engine default.
|
|
2918
|
+
mergeRetries: config.mergeRetries,
|
|
2718
2919
|
// The origin-trust stamp the CI shell passes IN (unset ⇒ unstamped).
|
|
2719
2920
|
originTrust,
|
|
2720
2921
|
noPR: config.noPR,
|
|
@@ -2740,17 +2941,17 @@ export function buildProgram() {
|
|
|
2740
2941
|
}
|
|
2741
2942
|
process.exit(result.exitCode);
|
|
2742
2943
|
});
|
|
2743
|
-
// The CI CLOSE-JOB driver (
|
|
2944
|
+
// The CI CLOSE-JOB driver (spec `runner-in-ci`, capability E; task
|
|
2744
2945
|
// `install-ci-close-job-workflow`). The thin JOB the emitted close-job workflow
|
|
2745
2946
|
// invokes on a merge to main: resolve which source issue(s) the landed work
|
|
2746
|
-
// closes (resolveClosingIssue), run the "
|
|
2947
|
+
// closes (resolveClosingIssue), run the "spec complete?" query for the spec case
|
|
2747
2948
|
// (prd-complete-query, done), and close via the IssueProvider seam — all
|
|
2748
2949
|
// UNCHANGED engine pieces, CONSUMED not re-built (the Out-of-Scope fence). CI
|
|
2749
2950
|
// owns ONLY the job + trigger. Local-runnable too (a manual catch-up close).
|
|
2750
2951
|
program
|
|
2751
2952
|
.command('close-merged-issues')
|
|
2752
2953
|
.helpGroup(ADVANCED_GROUP)
|
|
2753
|
-
.description('Close source issues whose work has landed on main (CI capability E,
|
|
2954
|
+
.description('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.')
|
|
2754
2955
|
.option('--cwd <dir>', 'the repo working dir whose work/ tree to scan (default: cwd)')
|
|
2755
2956
|
.option('--gh-bin <bin>', 'the gh CLI binary (default: gh on PATH)')
|
|
2756
2957
|
.option('--json', 'output the raw result as JSON')
|
|
@@ -2867,7 +3068,7 @@ export function buildProgram() {
|
|
|
2867
3068
|
remote
|
|
2868
3069
|
.command('find <folder>')
|
|
2869
3070
|
.helpGroup(HEADLINE_GROUP)
|
|
2870
|
-
.description(
|
|
3071
|
+
.description(`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.`)
|
|
2871
3072
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
2872
3073
|
.option('--yes', 'add all discovered participating repos (no prompt)')
|
|
2873
3074
|
.action(async (folder, flags) => {
|
|
@@ -2918,7 +3119,7 @@ export function buildProgram() {
|
|
|
2918
3119
|
.option('--gh-bin <bin>', 'the gh CLI binary (default: gh on PATH)')
|
|
2919
3120
|
.option('--cwd <dir>', 'the target repo working dir (default: cwd)')
|
|
2920
3121
|
.option('--install-source <registry|workspace>', 'where the CI installs the CLI from: `registry` (npm install -g, the default) or `workspace` (build from the checked-out source + link onto PATH, for the self-hosting monorepo). Overrides auto-detection in both directions.')
|
|
2921
|
-
.option('--max-parallel <n>', '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
|
|
3122
|
+
.option('--max-parallel <n>', '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.')
|
|
2922
3123
|
.action(async (flags) => {
|
|
2923
3124
|
const workDir = flags.cwd ?? process.cwd();
|
|
2924
3125
|
if (flags.installSource !== undefined &&
|
|
@@ -2964,8 +3165,64 @@ export function buildProgram() {
|
|
|
2964
3165
|
log: (line) => console.error(line),
|
|
2965
3166
|
});
|
|
2966
3167
|
});
|
|
3168
|
+
// The `skills` group (ADR `skill-install-vendors-incur-agents-map`): install the
|
|
3169
|
+
// packaged dorfl skills (`from-idea`, `setup`, ...) into the OPERATOR's OWN
|
|
3170
|
+
// harness dirs. Distinct from any target-repo propagation (ADR
|
|
3171
|
+
// `methodology-and-skills` §6 — skills don't travel into target repos; only
|
|
3172
|
+
// `work/protocol/` does). Only `add` is exposed in this pass — `list`/`remove`
|
|
3173
|
+
// are deferred until we have a real use case (the vendored `install()` is
|
|
3174
|
+
// idempotent, so `add` alone covers upgrade + drift-repair; a future `remove`
|
|
3175
|
+
// would map onto the vendored `remove()` and is intentionally out of scope
|
|
3176
|
+
// here to keep the CLI surface minimal).
|
|
3177
|
+
const skills = program
|
|
3178
|
+
.command('skills')
|
|
3179
|
+
.helpGroup(HEADLINE_GROUP)
|
|
3180
|
+
.description("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).");
|
|
3181
|
+
skills
|
|
3182
|
+
.command('add')
|
|
3183
|
+
.helpGroup(HEADLINE_GROUP)
|
|
3184
|
+
.description('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.')
|
|
3185
|
+
.option('--local', 'install project-locally into <cwd>/.agents/skills/ (and each detected harness\u2019s PROJECT dir) instead of globally into ~/.agents/skills/. Default: global.')
|
|
3186
|
+
.action((flags) => {
|
|
3187
|
+
const result = installSkills({ global: flags.local !== true });
|
|
3188
|
+
console.log(formatSkillsAddReport(result, flags.local === true));
|
|
3189
|
+
});
|
|
2967
3190
|
return program;
|
|
2968
3191
|
}
|
|
3192
|
+
/**
|
|
3193
|
+
* Human-readable report for `dorfl skills add`. Prints the SOURCE the skills
|
|
3194
|
+
* were read from, each canonical install path (one per skill), and each
|
|
3195
|
+
* non-universal harness placement (symlink | copy) with its absolute path.
|
|
3196
|
+
* Universal harnesses read from the canonical dir directly and need no line.
|
|
3197
|
+
* Output goes to stdout; the shape is stable enough for a human to audit "what
|
|
3198
|
+
* landed where" but is NOT a machine contract (no JSON mode in this pass).
|
|
3199
|
+
*/
|
|
3200
|
+
export function formatSkillsAddReport(result, local) {
|
|
3201
|
+
const lines = [];
|
|
3202
|
+
const scope = local ? 'project-local' : 'global';
|
|
3203
|
+
lines.push(`Installed dorfl skills (${scope}) from ${result.sourceDir}:`);
|
|
3204
|
+
if (result.paths.length === 0) {
|
|
3205
|
+
lines.push(' (no skills found in source)');
|
|
3206
|
+
}
|
|
3207
|
+
else {
|
|
3208
|
+
for (const p of result.paths.slice().sort()) {
|
|
3209
|
+
lines.push(` ${p}`);
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
if (result.agents.length === 0) {
|
|
3213
|
+
lines.push('Harnesses: no non-universal harness detected (universal harnesses read from the canonical dir directly).');
|
|
3214
|
+
}
|
|
3215
|
+
else {
|
|
3216
|
+
lines.push('Harnesses:');
|
|
3217
|
+
for (const a of result.agents.slice().sort((x, y) => {
|
|
3218
|
+
const byAgent = x.agent.localeCompare(y.agent);
|
|
3219
|
+
return byAgent !== 0 ? byAgent : x.path.localeCompare(y.path);
|
|
3220
|
+
})) {
|
|
3221
|
+
lines.push(` ${a.agent}: ${a.mode} -> ${a.path}`);
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
return lines.join('\n');
|
|
3225
|
+
}
|
|
2969
3226
|
/**
|
|
2970
3227
|
* A readline-backed {@link WizardPrompts} for the interactive `install-ci`
|
|
2971
3228
|
* wizard. Prompts go to stderr (stdout is reserved for any machine output); a
|