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
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: drive-tasks
|
|
3
|
+
disable-model-invocation: true
|
|
4
|
+
description: 'The supervised conductor: build every ready work/ task in a loop via dorfl, reviewing each diff and merging, until none can advance. Requires the dorfl CLI.'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# drive-tasks
|
|
8
|
+
|
|
9
|
+
**Be the conductor, not the player.** `dorfl do task:<slug> --isolated` already builds ONE task autonomously (claim → build agent → acceptance gate → optional Gate-2 review → PR) in a worktree off the arbiter. `drive-tasks` is the layer ABOVE: it looks at the _whole_ board, checks each ready task is still _fresh_, decides _what_ to build and in _what order_, drives `do` per task, then acts as a **third reviewer** (Gate-3, the conductor's own diff-vs-criteria pass — see step 4b) over each PR before merging it.
|
|
10
|
+
|
|
11
|
+
It is a **methodology skill** (prose you follow), like `to-task` / `review` — NOT a runner command. **Precondition:** it drives the **`dorfl` CLI** over a repo using the **`work/` contract** — if neither is present, this skill does not apply. (It is the ONE skill that leans on the runner CLI directly; that is its job. The other skills stay protocol-native.) It composes:
|
|
12
|
+
|
|
13
|
+
- **`dorfl do task:<slug> --isolated`** — the per-task worker (build + acceptance gate + optional PR/code-review gate), run `--isolated` ALWAYS (a job worktree off the arbiter, never the human checkout). The harness, model, acceptance gate, review mode, and integration mode all come from dorfl CONFIG (per-repo / global) — do NOT hardcode them; `--isolated` is the one flag this skill pins, and any other flag (`--review`/`--merge`/…) is a user-confirmed per-run override only.
|
|
14
|
+
- **`review`** (`skills/review/`) — the discipline for your own diff-vs-criteria pass over each opened PR.
|
|
15
|
+
- **`to-task`** (`skills/to-task/`) — for the forward-note step and any re-tasking the human asks for.
|
|
16
|
+
|
|
17
|
+
It is **scoped to building ready TASKS.** The broader job — survey _everything_ (observations, ideas, specs, tasks), figure out what can advance, fill judgement gaps conversationally until new tasks are READY, then build them — is `orchestrate`, which delegates the BUILDING back to this skill. Keep `drive-tasks` focused on building ready tasks; hand the deep survey up to `orchestrate`.
|
|
18
|
+
|
|
19
|
+
## How it stalls (the stuck-set)
|
|
20
|
+
|
|
21
|
+
There is ONE loop. The skill **advances every task it can** and, whenever it hits a wall on a particular task, it does NOT halt — it records that task + its specific question in a **stuck-set** and moves to the next independent ready task (the [accumulate-don't-block rule](#the-accumulate-dont-block-rule)). Only when nothing more can advance does it deal with the stuck-set, and HERE is the only behavioural fork — it depends purely on whether a human is reachable in this session:
|
|
22
|
+
|
|
23
|
+
- **A human is present** (the normal case — you are running in their session): present the accumulated stuck-set as one [batched set of questions](#batching-the-questions), take the answers, and **continue the loop** (a task whose question is resolved becomes buildable again; one the human defers stays parked).
|
|
24
|
+
- **No human is reachable** (you were invoked to run unattended): do not block waiting — finish everything that can advance, then **stop and report the stuck-set** (plus the built/merged/needs-attention summary) as your result.
|
|
25
|
+
|
|
26
|
+
That is the whole difference; the loop, the selection, and the stuck-set are identical either way.
|
|
27
|
+
|
|
28
|
+
### Selection + isolation
|
|
29
|
+
|
|
30
|
+
This skill does its OWN intelligent per-task selection (graph order + freshness + diff review) and dispatches `do` **per chosen slug** — it never uses `do`'s auto-pick (that is `run`'s daemon mechanism, not a conductor's). It builds **one task at a time, end-to-end**, and it builds **`--isolated`, ALWAYS** — never in-place.
|
|
31
|
+
|
|
32
|
+
`do task:<slug> --isolated` builds in a per-job worktree off THIS repo's arbiter (the SAME isolation `run` uses), inferring the arbiter from cwd. The human checkout is **never touched**: no dirty-tree refusal, no claim/done-move churn in your tree, no entanglement with the human's (or your own) uncommitted work, no rebuild-the-dist-mid-drive dance. The conductor is a pure observer of the arbiter. (`do --remote <url>` is the same isolation against a FOREIGN repo with no checkout; `--isolated` is its same-repo sibling.)
|
|
33
|
+
|
|
34
|
+
The ONE consequence to respect: an isolated build reads the task + its `blockedBy` deps from the **arbiter's `main`**, so a **local-only / un-pushed task is INVISIBLE** to it. Do NOT fall back to in-place for such a task — **push it (and its deps) to the arbiter first** (the arbiter is the source of truth; a task that isn't on it isn't ready to drive). There is no in-place mode in this skill.
|
|
35
|
+
|
|
36
|
+
The loop, selection, freshness check, Gate-3 review, and stuck-set are exactly as described below; `--isolated` is simply the standing build mode.
|
|
37
|
+
|
|
38
|
+
## When to use vs. not
|
|
39
|
+
|
|
40
|
+
- **Use** to take a `work/tasks/ready/` from "N ready tasks" to "no ready task can advance", building + reviewing + merging each, in dependency-and-practical order; to conduct the dorfl worker through a phase of task-building; as the building engine `orchestrate` delegates to.
|
|
41
|
+
- **Don't** use it as the unattended daemon — that's `run` (genuine parallelism, no human). `drive-tasks` is **one task at a time, end-to-end**. Don't use it to _author_ tasks from scratch (that's `to-task`), or to _task specs / triage observations / fill judgement gaps / answer scattered open questions across the whole tree_ (that's `orchestrate`). Don't use it to FORCE a blocked task — it respects the gate.
|
|
42
|
+
|
|
43
|
+
## The golden rules (do not violate)
|
|
44
|
+
|
|
45
|
+
1. **One task at a time, end-to-end** (build → Gate-3 review → merge) so each merge unlocks the next cleanly and rebases stay trivial. Parallelism is `run`'s job.
|
|
46
|
+
2. **Never force a failed task.** A red gate / Gate-2 block / rebase conflict routes the item to needs-attention (its per-item lock is marked `state: stuck`) — leave it there, branch preserved, skip its dependents, continue with independent ready tasks, report it at the end.
|
|
47
|
+
3. **Capture, don't fix-in-place, off-path findings.** Spot drift outside a task's scope → write a `work/notes/observations/` note (and COMMIT + PUSH it when a later build depends on it — see rule 5), don't expand the task.
|
|
48
|
+
4. **You merge the approval.** GitHub refuses `gh pr review --approve` on a PR whose commits are under your own identity — so post the verdict as a PR **comment** (`gh pr comment <n> --body-file …`, lead with `APPROVE ✅` / `BLOCK`), then `gh pr merge <n> --squash --delete-branch`. The comment + merge IS the approval.
|
|
49
|
+
5. **Commit + PUSH your own `work/notes/observations/` notes before they need to matter.** Because builds are `--isolated` (off the arbiter), a dirty local tree does NOT block dispatch — but an un-pushed note or task is INVISIBLE to an isolated build until it lands on the arbiter. So commit your contract-native notes (append-only, low-risk) AND push them when a soon-to-build task depends on them being on `main` (e.g. a forward-note planted in a task body — step 2). Report what you committed/pushed in the summary. **What a conductor commits:** its own `work/notes/observations/` notes; a load-bearing **forward-note it plants in a task body** (step 2 — it MUST be committed to take effect before that task's `do`, and it is a small protocol-mechanical edit, not authored content); and the protocol's own moves the runner/`do`/`complete` make (claim reverts, done-moves, PR merges). It does NOT hand-author-and-commit a full spec or a fresh task SET — producing those is `to-spec`/`to-task`' job and they are left for human review. Report every commit in the summary.
|
|
50
|
+
6. **Accumulate, don't stall.** When ONE task is stuck or needs a judgement call, write it into the stuck-set and move to the next INDEPENDENT ready task — never block the whole loop on one item. When nothing more can advance, surface the stuck-set: ask the human if one is present, else report it. See [the rule](#the-accumulate-dont-block-rule).
|
|
51
|
+
7. **Never touch the target checkout; be checkout-agnostic.** Driving is a side-effect-free observation of the arbiter, so as a side-effect of driving you perform NO git mutation in the target repo's working checkout — no `git switch`/`checkout -B`/`branch -D`/`reset`/`rebase`/commit in the human's working tree. If you need a working tree for a cheap verification (e.g. re-run a task's own tests off its pushed branch), use a THROWAWAY clone / the job worktree / a temp dir — NEVER the human checkout. The human's uncommitted work and current branch are sacrosanct; leave the tree exactly as you found it. And because you need only the arbiter (a URL/remote) + `gh` + a scratch area, you can RUN FROM ANYWHERE: resolve the arbiter EXPLICITLY rather than assuming `cwd` is the repo, and if `cwd` happens to BE the human's checkout, treat it as READ-ONLY. Your own `work/notes/observations/` note commits (rule 5) go to the arbiter without dirtying the working tree. (UNLESS the human explicitly asks to drive in-place in a specific checkout.) This is the same arbiter-is-truth posture `do --remote`/`run` already use; the repeated `git rebase origin/main` reconciliations a checkout-bound drive needs are the symptom this rule removes.
|
|
52
|
+
|
|
53
|
+
## The accumulate-don't-block rule
|
|
54
|
+
|
|
55
|
+
The loop's job is to **advance as much as possible**, not to halt at the first judgement call. Whenever you hit a **wall** on a task —
|
|
56
|
+
|
|
57
|
+
- it looks **stale/drifted** (the freshness check in step 1 fires),
|
|
58
|
+
- a **forward-note** seems needed but you're unsure it's wanted,
|
|
59
|
+
- a **Gate-3 review** surfaces a genuine judgement call (a maybe-blocking nit, an ambiguously-met criterion), or
|
|
60
|
+
- the task is otherwise ambiguous / rests on an unresolved decision
|
|
61
|
+
|
|
62
|
+
— do NOT stop the loop. **Record the item + the specific question in a STUCK-SET** (a running list you keep for the session), SKIP that task (and its dependents), and **continue with the next independent ready task.** Only when nothing more can advance do you deal with the stuck-set:
|
|
63
|
+
|
|
64
|
+
- **A human is present** → present the stuck-set as a [batched set of questions](#batching-the-questions), take answers, and resume the loop (a task whose question is resolved becomes buildable again; one the human defers stays parked).
|
|
65
|
+
- **No human reachable** → finish all you can, then stop and report the stuck-set (+ the built/merged/needs-attention summary) as your result; do not block waiting.
|
|
66
|
+
|
|
67
|
+
Either way the discipline is the same: do as much as can be done, then surface the residue in ONE batch — never dribble out one question at a time, never stall the whole loop on a single item.
|
|
68
|
+
|
|
69
|
+
## Recovering a needs-attention item (requeue)
|
|
70
|
+
|
|
71
|
+
When a task has routed to needs-attention (a red gate / Gate-2 block / rebase conflict / a build-time STOP marks its per-item lock `state: stuck`), its body carries the reason and its `work/<type>-<slug>` branch is **preserved on the arbiter** — nothing is lost. Whether you can re-drive it depends on the reason:
|
|
72
|
+
|
|
73
|
+
- **A fixable problem the agent can resolve on a retry** — a real bug the gate caught, a scoping miss, a flaky-test red — is a CONDUCTOR move, not a human question. Recover it with **`dorfl requeue <slug> --arbiter origin`** (DEFAULT = keep + continue: releases the stuck lock; the body is already resting in the pool `tasks/ready/`, and the branch is left UNTOUCHED so the next claim CONTINUES from its tip). Optionally add a precise handoff with **`-m "<what to fix>"`** (appended to the body). Then `do task:<slug>` again: the re-claim CONTINUES from the kept branch, and the merged `agent-prompt-continue-context` puts the prior work + the needs-attention reason
|
|
74
|
+
- your `-m` note into the agent's prompt — so it BUILDS ON the good code and fixes the gap rather than restarting.
|
|
75
|
+
- **A genuine human-decision block** — the task is ambiguous / drifted / rests on an unresolved fork — is NOT something a retry fixes. Leave it parked; it is a stuck-set question (ask it if a human is present, else report it). Do NOT requeue a task whose premise is wrong — re-scope it first (that is `orchestrate`/human work).
|
|
76
|
+
- **`requeue --reset`** (DISCARD + fresh: deletes the remote branch first, then releases the lock so the next claim starts CLEAN) is for when the kept work is worthless. It is guarded and NEVER the default — only on an explicit human call; the conductor's default recovery is keep+continue.
|
|
77
|
+
|
|
78
|
+
AFTER any requeue, re-sync (`git fetch && pull --rebase`) so the re-`do` claims off the latest main. A requeued-and-rebuilt task then flows through the normal step-4 BUILD → REVIEW → MERGE.
|
|
79
|
+
|
|
80
|
+
> **Let `do` re-drive a recovered branch — do NOT hand-roll a parallel `pr/<slug>` branch.** When a flake-recovered task's `work/<type>-<slug>` branch already holds green work but needs only a small lifecycle fixup before its PR (release the stuck lock, strip the runner's "aborted/needs-attention" commit subjects), the right move is `requeue` (keep+continue) + re-`do`: the re-claim continues from the kept branch tip and the runner ITSELF opens the PR — no manual PR at all. If you genuinely must fix up by hand, commit the fixup **ON the existing `work/<type>-<slug>` branch** and PR that branch (`gh pr create --head work/<type>-<slug>`). NEVER spin a separate `pr/<slug>` branch off `main` and re-apply the tree: it **orphans** the canonical `work/<type>-<slug>` branch on the remote (it then has to be remembered and hand-deleted — easy to miss) and **discards** the branch's real history for mere cosmetic single-commit tidiness. One branch, its own history, no orphan.
|
|
81
|
+
|
|
82
|
+
## The loop
|
|
83
|
+
|
|
84
|
+
### 0. ANALYSE the task set + dependency graph
|
|
85
|
+
|
|
86
|
+
Read every `work/tasks/ready/*.md` frontmatter (`slug`, `blockedBy`/`deps`, `needsAnswers`, `humanOnly`, `spec`, `covers`) and the `work/tasks/done/` set. Build the graph:
|
|
87
|
+
|
|
88
|
+
- **READY** = every `blockedBy` is in `work/tasks/done/` AND `needsAnswers !== true` AND `humanOnly !== true`. (BOTH gate fields exclude a task from READY: `needsAnswers` means open questions a human must answer first; `humanOnly` means a human must DRIVE it. Check BOTH in the frontmatter scan, not just `needsAnswers`. A `humanOnly` task dispatched to `do` will rightly STOP at build time, wasting a claim/surface cycle, so catch it UP FRONT.)
|
|
89
|
+
- **BLOCKED** = a `blockedBy` is still in the pool `tasks/ready/`, held (in-progress on its lock), or stuck (needs-attention on its lock).
|
|
90
|
+
- **GATED** = `needsAnswers: true` OR `humanOnly: true` (needs a human first, by question or by drive-ownership; not part of the AUTONOMOUS READY set; list it but do not AUTO-build it, even once its deps land). The agent-buildable READY set is the tasks that are neither blocked nor gated by EITHER field.
|
|
91
|
+
- **"GATED" gates AUTONOMOUS SELECTION, not explicit human dispatch.** `humanOnly`/`needsAnswers` keep a task out of the set THIS LOOP (and `run`/`advance`/auto-pick) picks ON ITS OWN; they do NOT make the task unbuildable. An EXPLICIT `dorfl do task:<slug>` typed by a human (or by THIS conductor on a human's explicit instruction to build that named slug) STILL builds it — the readiness guard does not consult `humanOnly` on the human path ("a human is never bound by `humanOnly`"), and explicit dispatch drops the policy term ("the pool gates the policy, not the explicit claim"). So a `humanOnly` task is the human's to drive by NAME, never the conductor's to AUTO-select. (This is also why `humanOnly` can serve, off-label, as a "keep CI/`run` off while I drive this by hand" latch — but prefer POSITION/staging for that; see WORK-CONTRACT.md "Task `humanOnly` is NARROW".)
|
|
92
|
+
|
|
93
|
+
(A task in the STAGING slot `tasks/backlog/` is review-first, awaiting a human's promotion into the pool `tasks/ready/`; it is NOT in the READY set — surface it, don't build it.) Note which tasks **unlock the most downstream work** when they land — those go first.
|
|
94
|
+
|
|
95
|
+
> **This is the DEFAULT and it does NOT change:** the READY set is computed from the agent POOL `work/tasks/ready/`, and `work/tasks/backlog/` is review-first STAGING the conductor does NOT build (it surfaces those, it never dispatches `do` against them). Unless the caller explicitly opts into the drive-from-backlog mode below, behave exactly as documented above.
|
|
96
|
+
|
|
97
|
+
#### Opt-in: drive tasks from `tasks/backlog/` (the staging folder)
|
|
98
|
+
|
|
99
|
+
**OPT-IN ONLY — never the default.** When, and ONLY when, the **caller EXPLICITLY instructs** this skill to drive tasks from the staging folder `work/tasks/backlog/` (e.g. "drive the tasks in backlog/", "build the backlog tasks <slugs>", an explicit drive-from-backlog mode), the conductor builds those staged tasks too, using the **identical** loop, selection, freshness check, Gate-3 review, requeue recovery, and stuck-set discipline described in this whole skill — the ONLY change is WHERE the READY set is read from. Absent that explicit instruction, `tasks/backlog/` stays review-first staging you surface but do NOT build (the default above). If you are unsure whether the caller meant this, do NOT assume it — treat the staging folder as review-first and ask.
|
|
100
|
+
|
|
101
|
+
In this mode the READY computation reads **`work/tasks/backlog/`** instead of `work/tasks/ready/` (or, if the caller explicitly says to drive BOTH, the UNION of `tasks/backlog/` + `tasks/ready/`). Everything else is unchanged:
|
|
102
|
+
|
|
103
|
+
- **READY** = every `blockedBy` is in `work/tasks/done/` AND `needsAnswers !== true` AND `humanOnly !== true` — the SAME gating, just over the staging set (and, when the caller scopes the run to specific slugs, restricted to those). `blockedBy` still resolves against `work/tasks/done/` exactly as in the default; deps held in `tasks/backlog/` (or `tasks/ready/`) that are not yet `done/` are BLOCKED, so honour the same dependency ordering.
|
|
104
|
+
- **GATED** (`needsAnswers`/`humanOnly`) and **BLOCKED** mean exactly what they mean above; a `humanOnly`/`needsAnswers` staged task is NEVER agent-buildable here either.
|
|
105
|
+
- The build is the SAME `dorfl do task:<slug> --isolated --allow-backlog`, the SAME Gate-3 diff-vs-criteria review, the SAME merge-via-PR-comment, and the SAME accumulate-don't-block stuck-set. The one added flag is **`--allow-backlog`**: it widens `do`'s task resolution to also search `tasks/backlog/`, so the isolated build can resolve a staged slug. WITHOUT it, `do` searches only `tasks/ready/` + in-progress and would throw `no task '<slug>' found in work/in-progress/, work/tasks/ready/` — i.e. this opt-in mode is a spec without a mechanism unless you pass the flag. Everything else about the build (claim, lock, gate, Gate-2) is identical to driving a ready task; the staged task done-moves `tasks/backlog/ → tasks/done/` directly, because your explicit drive IS the promotion. (The staged task must be on the arbiter's `main` for the isolated build to see it — per [Selection + isolation](#selection--isolation), push it and its deps first if they are local-only.)
|
|
106
|
+
|
|
107
|
+
This mode exists so a caller who has already decided a set of staged tasks is good can have the conductor build them straight from `tasks/backlog/` without first promoting them into `tasks/ready/`. It does NOT relax the review-first nature of staging for any OTHER caller or run; it is a per-invocation, explicitly-requested override of the read location only.
|
|
108
|
+
|
|
109
|
+
**Why drive in place beats promote-then-drive (the competition window).** The obvious-but-wrong way to build a staged task is to first promote it `tasks/backlog/ → tasks/ready/`, then drive it. But `tasks/ready/` is the AGENT POOL: the instant a task lands there on the arbiter, a CI `advance` leg OR a machine-local `run` daemon can CLAIM it (both pull from `tasks-ready`). So promote-then-drive opens a **competition window** — the autonomous claimer races the human who wanted to drive the work themselves. Driving in place with `--allow-backlog` keeps the body resting in `tasks/backlog/` (never the pool), so no CI leg or `run` daemon can claim it: the human keeps sole control of the set they are driving until they decide (if ever) to promote. Staging is not only review-first admission; it is the human-control position — promotion to the pool is EXACTLY what makes an item claimable-by-anyone. So drive in place; never promote-then-drive.
|
|
110
|
+
|
|
111
|
+
### 1. CHECK freshness / up-to-dateness of each ready task
|
|
112
|
+
|
|
113
|
+
**A ready task is not necessarily a CORRECT task.** Tasks are authored ahead of time; by the time one is ready its load-bearing premises may have **drifted** — something it says is "unconsumed / not yet built / still TODO" may already have landed in `work/tasks/done/` + the code. Building a drifted task wastes a full `do` run (the build agent will rightly STOP, or worse, churn working code) — and the conductor, which sees the WHOLE graph, can catch it cheaply UP FRONT.
|
|
114
|
+
|
|
115
|
+
For each ready task, before dispatching `do`, sanity-check its premises against current reality:
|
|
116
|
+
|
|
117
|
+
- Read the task's "What to build" + any **drift-check / READ-FIRST** block (tasks often name the exact files + the premise to confirm).
|
|
118
|
+
- Spot-check the load-bearing claims: if it says "X has zero consumers", "Y still uses the old path", "the seam is unwired" — grep `work/tasks/done/` + `src/` to confirm that is STILL true. Recently-merged tasks are the usual culprit (a convergence that already happened, a verb already renamed, a primitive already adopted).
|
|
119
|
+
- Glance at `work/notes/observations/` for a `*-premise-drifted` / drift note naming this task.
|
|
120
|
+
|
|
121
|
+
If the task still holds → proceed. **If it smells stale → it's a WALL**: record it in the stuck-set with the specific premise that no longer holds + a suggested re-scope, SKIP it (per the accumulate-don't-block rule), and move on. (This catches drift cheaply up front. The build-time backstop also exists: a task that IS drifted and slips past this check makes the build agent raise a STOP — the runner routes it to needs-attention with the agent's reason, skipping the wasted gate — but catching it here saves the whole `do` run.)
|
|
122
|
+
|
|
123
|
+
This is also the natural place for a **light look-ahead**: skim `work/specs/ready/` (and, if cheap, `work/notes/observations/` + `work/notes/ideas/`) for what's coming — it informs the forward-notes in step 2. The DEEP survey-everything pass is `orchestrate`'s job, not this skill's; keep this shallow.
|
|
124
|
+
|
|
125
|
+
### 2. CHECK for forward-looking notes a soon-to-be-tasked spec will need
|
|
126
|
+
|
|
127
|
+
Before building, scan `work/specs/ready/` for a spec that will be tasked soon and whose design **depends on the shape** of tasks you're about to land (a `taskedAfter:` / "builds on the X convergence" relationship). If a ready task should carry a `> FORWARD-POINTER` note so that spec can be tasked later WITHOUT amending the spec (e.g. "keep this loop/tick separable", "keep `-n` sequential", "don't rename X — the advance migration owns it", "shape this as a named callable unit"), **add the note to the task body now** (compose `to-task`' forward-note discipline). These notes are load-bearing: they prevent the downstream spec from needing changes. A note you're CONFIDENT about: plant it and COMMIT it (it must land before that task's `do` to take effect; per rule 5 this small protocol edit is committed, unlike authored artifacts). If a note is non-trivial or you're unsure it's wanted, that is a WALL → record it in the stuck-set (surface it with the batch when the loop stalls) rather than planting a guessed note.
|
|
128
|
+
|
|
129
|
+
> This is the step that earns the conductor its keep — a per-task `do` agent only sees its own task; only the conductor sees the whole graph + the pending specs and can plant the cross-task notes.
|
|
130
|
+
|
|
131
|
+
### 3. SELECT the tasks + a practical order
|
|
132
|
+
|
|
133
|
+
From the READY set, order by: (a) **dependency** (a task that unlocks others first), then (b) **practical** concerns — serialise tasks that edit the SAME hot file (e.g. one big `cli.ts`) so rebases stay trivial; prefer the order that keeps each subsequent claim rebasing cleanly off fresh `main`. State the planned order (and why) before you start.
|
|
134
|
+
|
|
135
|
+
### 4. For EACH fresh, ready task, in order — BUILD → REVIEW → MERGE
|
|
136
|
+
|
|
137
|
+
**4a. Build it** — ALWAYS `--isolated`:
|
|
138
|
+
|
|
139
|
+
```sh
|
|
140
|
+
dorfl do task:<slug> --isolated
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**`--isolated` is the one flag this skill mandates. Everything else — review mode, integration mode, harness, model — is LET TO CONFIG** (resolved `flag > env > per-repo > global > default`), so do NOT hardcode `--review`/`--propose`/`--merge` here: the repo's `.dorfl.json` decides, and `do --propose` is already the default. (`--isolated` reads the target repo's committed `.dorfl.json` from the arbiter's main, so per-repo `harness`/`verify`/`noPR`/`review` apply automatically — no `--harness`/env workaround.) **At the START of a drive, CONFIRM the run mode with the user** — do they want Gate 2 (`--review`, off by default — the human-first family default), and propose vs merge integration? — and add `--review` / `--merge` / etc. as explicit per-run OVERRIDES only when the user asks; otherwise let config drive every build of the drive.
|
|
144
|
+
|
|
145
|
+
After a failed/aborted isolated run, a stale job worktree can linger and block the next build's mirror fetch — run `dorfl gc` (or `gc --force --yes` once you've confirmed the work is safe on the arbiter) to reap it before continuing. Use a **generous timeout** — `do` runs a build agent + the full gate + (if enabled) the Gate-2 review and can take well over an hour for a big task. If you interrupt it, KILL the spawned `do`/agent process tree explicitly (an abort of your wrapper does NOT stop the child); the isolated worktree is then reaped/recovered via `gc` + the kept arbiter branch (your checkout is untouched).
|
|
146
|
+
|
|
147
|
+
- **Non-zero exit** (red gate / Gate-2 block / rebase conflict) → the item is now in needs-attention (its lock is `state: stuck`) with its reason in the body and its branch preserved on the arbiter. STOP that task (golden rule 2), skip its dependents, move to the next INDEPENDENT ready task. If the reason is a FIXABLE problem (not a human-decision block), it is recoverable IN-LOOP via `requeue` + re-`do` (continues from the kept branch) — see [Recovering a needs-attention item](#recovering-a-needs-attention-item-requeue); otherwise it becomes a stuck-set question.
|
|
148
|
+
|
|
149
|
+
**4b. Gate-3 — review the opened PR yourself** (the conductor's own review, the third review layer after Gate-1 = `do`'s acceptance gate and Gate-2 = `do`'s PR/code-review gate — the discipline that makes you a real reviewer of the result, not a rubber stamp). `do` already ran Gate-1 AND Gate-2 before opening the PR, so **trust that green** — do NOT re-run the (potentially slow) acceptance gate here. Your job is the JUDGEMENT the gates can't fully do: does the diff actually deliver THIS task?
|
|
150
|
+
|
|
151
|
+
- Read the **diff against the task's acceptance criteria** — tick each criterion (apply the `review` skill's lenses + destination check).
|
|
152
|
+
- **Verify every drift note / forward-pointer / must-fix-before-consume** the task carried was actually honoured (these are exactly where a `do` agent silently drifts — e.g. "don't rename X", "keep it sequential", "make the omitted path REFUSE"). Grep the branch to confirm.
|
|
153
|
+
- Read the gate-generated `work/notes/observations/review-nits-<slug>-*.md` and triage each nit (blocking? benign? a real off-path finding worth its own observation?).
|
|
154
|
+
- **Verdict:** if a drift note was violated or an acceptance criterion is unmet → **BLOCK** (comment the blocking findings; do NOT merge). If it is a clear BLOCK or clear APPROVE, act on it. If it is a genuine **judgement call** (a maybe-blocking nit, an ambiguously-met criterion), that is a WALL → record it in the stuck-set and skip (do not merge on a coin-flip), per the [accumulate-don't-block rule](#the-accumulate-dont-block-rule). Otherwise **APPROVE**.
|
|
155
|
+
|
|
156
|
+
> Dropping the Gate-3 re-verify is SOUND by default: `do`'s OWN acceptance gate runs against the merged artifact, because the `freshWorktreeGate` config (ON by default) runs `prepare`+`verify` in a CLEAN throwaway worktree cut from the work branch REBASED onto `<arbiter>/main` (the exact tree that integrates) — closing the checkout-vs-pushed-tree divergence at the root, so there is no separate per-task re-verify to re-introduce. (The opt-out `--no-fresh-worktree-gate` reverts to the old in-build-worktree pre-rebase gate, where that rare divergence is consciously accepted.)
|
|
157
|
+
|
|
158
|
+
**4c. Merge** (golden rule 4):
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
gh pr comment <n> --body-file /tmp/approve-<n>.md # leads with APPROVE ✅ + per-criterion reasoning
|
|
162
|
+
gh pr merge <n> --squash --delete-branch
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Use `--body-file` (PR bodies are backtick-heavy and break inline `--body` shell quoting).
|
|
166
|
+
|
|
167
|
+
> PROVIDER ASSUMPTION: the verdict-as-PR-comment + `gh pr merge` flow above assumes **`--propose` mode + a GitHub arbiter** (the only review-surface this skill knows today). In `--merge` mode `do` integrates directly with no PR — then your Gate-3 diff review still applies, but you record the verdict in the task/observation, not a PR comment, and there is nothing to `gh pr merge`. A non-GitHub arbiter has no `gh` at all. Making the approval/merge surface provider-agnostic (a likely future `dorfl` command, e.g. an `approve`/`land` verb) is NOT built yet; until then this step is GitHub-propose-specific — adapt the merge mechanics to the repo's actual integration mode.
|
|
168
|
+
|
|
169
|
+
**4d. Re-sync + re-evaluate:** recompute the READY set from the ARBITER (read-only) — `git fetch origin` and read the refreshed `origin/main` `work/` state (or use the mirror-side scan). You do NOT need to mutate a working checkout to do this:
|
|
170
|
+
|
|
171
|
+
```sh
|
|
172
|
+
git fetch origin # then read origin/main's work/ state to recompute the ready set
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Builds run `--isolated` on the arbiter, NOT your checkout, so a `git fetch` against the arbiter is all you need to RECOMPUTE the READY set — there is no local rebase dance, no branch switch, and no clean-tree precondition for the next dispatch (per golden rule 7, do not `git checkout`/`pull` INTO the human's working tree as a driving side-effect; if you are driving from a scratch clone you own, fast-forwarding IT is fine). If the next `do` runs from a BUILT copy of dorfl (e.g. a local checkout of this very repo), rebuild it so the merge you just made is in the binary the next `do` invokes. The merge landed `work/tasks/done/<slug>.md` on `main`; any task blocked only by it is now unlocked. Recompute the READY set (steps 0–1, including a fresh freshness check on newly-unlocked tasks) and continue.
|
|
176
|
+
|
|
177
|
+
### 5. CONTINUE until nothing can advance
|
|
178
|
+
|
|
179
|
+
Repeat step 4 until no ready task can advance (the READY set is empty OR every remaining ready task is parked in the stuck-set). THEN deal with the stuck-set ([the rule](#the-accumulate-dont-block-rule)): if a human is present, ask the batched questions and resume the loop from the answers; if not, report the stuck-set and stop.
|
|
180
|
+
|
|
181
|
+
### 6. SUMMARISE — the conductor's report
|
|
182
|
+
|
|
183
|
+
End with a structured rundown (this is a first-class deliverable, not an afterthought):
|
|
184
|
+
|
|
185
|
+
- **Built + merged** — each task with its PR number + a one-line note on any drift/forward-pointer/must-fix it honoured (`do` ran the acceptance + review gates; you reviewed the diff).
|
|
186
|
+
- **Routed to needs-attention** — each, with the EXACT blocking reason, whether the agent produced no code vs. a real bug the gate caught, and that the branch is preserved on the arbiter (recoverable via `requeue` + re-claim, or `work-on`).
|
|
187
|
+
- **Still blocked / gated** — what remains and on what (a needs-attention item? a `needsAnswers` human gate?).
|
|
188
|
+
- **What's now UNLOCKED in the project** — new commands, new behaviours/capabilities, retired verbs, and crucially **which specs are now taskable / unblocked** by what landed (the cross-cutting view only the conductor has).
|
|
189
|
+
- **Observations filed** (committed as you go, per rule 5) — list them so the human can find them in `git log`.
|
|
190
|
+
- **Housekeeping** — any direct-to-`main` chore commits you made (claim reverts, forward-notes), so the human can see them in `git log`.
|
|
191
|
+
|
|
192
|
+
When you run unattended (no human reachable), this report (plus the stuck-set) is your RESULT to whoever invoked you, not a message to a human.
|
|
193
|
+
|
|
194
|
+
## Batching the questions
|
|
195
|
+
|
|
196
|
+
When the loop stalls with a non-empty stuck-set, do NOT ask one question at a time. **Regroup the stuck-set into a single, well-organised batch** (the way a good conductor surfaces everything at once for one efficient answering pass):
|
|
197
|
+
|
|
198
|
+
- Group by item, each with: the task, the SPECIFIC question, why it's stuck (stale premise / uncertain forward-note / review judgement call), enough inline context to answer WITHOUT opening the file, and a **suggested default** where you have one.
|
|
199
|
+
- Order by leverage (a question whose answer unblocks the most downstream work first).
|
|
200
|
+
- If a human is present: present the batch, take answers, resume the loop (resolved → buildable again; deferred → stays parked). If not: this batch is the residue you report and stop on.
|
|
201
|
+
|
|
202
|
+
The batch is conversational (asked) or reported (unattended), not a written file.
|
|
203
|
+
|
|
204
|
+
## Beyond tasks
|
|
205
|
+
|
|
206
|
+
This skill builds READY TASKS. Two things sit ABOVE it, sharing its loop shape:
|
|
207
|
+
|
|
208
|
+
- **`orchestrate`** — the human-in-the-loop META conductor: surveys _everything_ (observations / ideas / specs / tasks), advances what it can (tasking specs, triaging), fills judgement gaps with the human conversationally until new tasks are READY, then **delegates the building to THIS skill** and surfaces the stuck-set to the human.
|
|
209
|
+
- **`advance`** — the AUTONOMOUS, file-mediated version of the same idea, driven by `run`/CI with a `work/questions/` sidecar. `drive-tasks` + `orchestrate` are the human-agency, synchronous siblings of `advance`; they share the same tick contract.
|
|
210
|
+
|
|
211
|
+
The conductor is **tick-agnostic**: today the per-item action is `dorfl do task:<slug>` (build a task); as `advance`-class ticks land (task / triage / surface / apply), the SAME loop applies — only the per-item command in step 4a changes. (Mirrors the loop/tick split in `run`: the conductor is a _loop_; the per-item command is the _tick_.)
|
|
212
|
+
|
|
213
|
+
## Pitfalls
|
|
214
|
+
|
|
215
|
+
- **The interrupt footgun.** Aborting your `do` wrapper does NOT kill the spawned agent — it keeps editing files in the background. After any interrupt, `ps`-find + kill the `do`/agent/`tsc` tree, discard its partial edits, and release the claim (the runner reverts the lock; the body is already resting in the pool `tasks/ready/`) before redoing.
|
|
216
|
+
- **Flaky tests red a good gate.** If a task's gate fails ONLY on a known-flaky test with everything else green, re-run before treating it as a real block (and the flake itself is a `notes/observations/` note, not a task fix).
|
|
217
|
+
- **A `do` that delivers no code is NOT a success** (nothing changed → the gate passes vacuously). The runner catches this two ways — an agent STOP (drift) routes to needs-attention before the gate, and Gate-2 (plus your Gate-3) check the _diff against the criteria_, not just the gate. Trust the block; never merge an empty-or-criteria-unmet diff.
|
|
218
|
+
- **Don't sum two freshness models.** When reporting cross-repo + local state, keep them distinct.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: from-idea
|
|
3
|
+
disable-model-invocation: true
|
|
4
|
+
description: 'The from-scratch ON-RAMP: take a raw project idea and end with a scaffolded work/-contract repo where that idea is captured as a spec in work/specs/ready/, ready to task. The front door that owns the idea-interview and sequences setup (scaffold) then to-spec (synthesize). NOT a spec-producer itself — to-spec is the synthesis primitive it calls; NOT adversarial spec-grilling (that is the separate grilling skill).'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# from-idea
|
|
8
|
+
|
|
9
|
+
**The from-scratch entrance to the main flow.** You have a raw idea ("I want to build X") and want ONE move that ends with a contract-ready repo holding that idea as a spec in `work/specs/ready/`, ready to task. This is **rung A of the spec lifecycle's front door** — the on-ramp that wraps the first two steps of the main flow (`setup` then `to-spec`) and adds the one thing neither does: a thin **interview that turns a raw idea into something spec-worthy**.
|
|
10
|
+
|
|
11
|
+
It is an **on-ramp** (per `work/SKILL.md`'s taxonomy — a starting situation that generates work, then merges onto the main flow), not a survey-loop. It is a **thin orchestrator**: its entire net-new surface is the idea interview plus two skill invocations in order plus the plumbing between them. Everything else is borrowed.
|
|
12
|
+
|
|
13
|
+
## What it is NOT (the boundaries that keep it thin)
|
|
14
|
+
|
|
15
|
+
- **NOT a spec-producer of its own.** `to-spec` writes the spec (it owns the spec shape, the `work/protocol/spec-template.md`, the launch-snapshot banner, the `specs/ready/` target, the two autonomy axes). from-idea is the FRONT DOOR that calls it. Read the two names as a pair: `from-idea` owns the on-ramp (setup + the interview); `to-spec` is the synthesis primitive it hands the conversation to. Do not reimplement any of to-spec here.
|
|
16
|
+
- **NOT a scaffolder of its own.** `setup` owns the `work/` skeleton, the `work/protocol/` docs, `CONTEXT.md`, the `.dorfl.json` `verify`/`prepare` gate, and the empty-vs-populated detection. from-idea CALLS setup; it never hand-rolls a "is this a contract repo?" check or writes `CONTEXT.md` itself (that forks setup's detection and drifts). The one-way direction is fixed: **from-idea calls setup; setup never calls from-idea** (setup stays a focused adoption primitive). setup's empty-repo branch MAY _mention_ from-idea as the next step — a discoverability pointer, never an invocation.
|
|
17
|
+
- **NOT adversarial grilling.** Stress-testing a plan/design before building is the personal `grilling` skill's job. from-idea clarifies only enough to be spec-worthy and DEFERS the rest (see the interview floor below). If the user wants the idea grilled, that is a separate move after the spec lands.
|
|
18
|
+
|
|
19
|
+
## The sequence (honor BOTH human checkpoints; never auto-commit)
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
clarify the idea → setup (PLAN → HARD STOP → scaffold) → to-spec (write specs/ready/<slug>.md, unstaged)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`setup` MUST run first: `to-spec` writes to `work/specs/ready/`, which requires `work/` to exist. The flow has **two natural human checkpoints**, and from-idea honors both:
|
|
26
|
+
|
|
27
|
+
1. **setup's plan-confirm HARD STOP** — setup presents the proposed description + detected `verify`/`prepare` gate (+ any Phase-B mapping) and STOPS for the user to ratify before writing the judgement-heavy parts. Do NOT bulldoze this; it is a real stop. Let setup own its arc.
|
|
28
|
+
2. **The spec landing unstaged in `specs/ready/`** — `to-spec` leaves the file in the working tree for the human to review; it does not stage/commit. from-idea inherits that etiquette: **never stage/commit/push** at any step.
|
|
29
|
+
|
|
30
|
+
## Step 1 — Clarify the idea (the ONE net-new piece): just enough to be spec-worthy
|
|
31
|
+
|
|
32
|
+
This is the only doing from-idea adds. Run a SHORT interview to lift the raw idea to the floor a spec needs — no further.
|
|
33
|
+
|
|
34
|
+
**The interview floor (the stop condition).** Clarify enough that `to-spec` can write a coherent launch snapshot. Borrow to-spec's own spine — a spec-worthy idea has:
|
|
35
|
+
|
|
36
|
+
- **(a) the problem / intent** — what is this for, what pain or opportunity does it address;
|
|
37
|
+
- **(b) the rough shape of success** — what does "it works" look like, who/what uses it, what it integrates with;
|
|
38
|
+
- **(c) the obvious seams / constraints** — the highest seams the feature would be tested at, and any hard constraints (stack, platform, external systems) the user already knows.
|
|
39
|
+
|
|
40
|
+
That is the FLOOR, not "everything resolved." Ask a small number of focused questions (think 2–5, batched), then stop.
|
|
41
|
+
|
|
42
|
+
**The no-grill / defer rule (do not become a second grilling skill).** Genuine design forks, unknowns, and "we'll decide later" calls are NOT interview rounds — they are recorded by `to-spec` as `needsAnswers: true` with the open questions in the spec body, and DEFERRED. The auto-tasker then refuses to task until a human resolves them. Be honest: a spec flagged `needsAnswers` is the correct output of a real but unresolved idea, far better than over-interviewing to force a false resolution. When in doubt, defer rather than grill. (If the idea is so thin it is barely a wish, say so and offer to capture it as a `notes/ideas/` note instead of pushing it through to a spec.)
|
|
43
|
+
|
|
44
|
+
**One interview, two consumers.** The answers you gather here feed BOTH downstream skills — do not let them re-ask:
|
|
45
|
+
|
|
46
|
+
- the one-to-two-sentence **project description** (problem + intent) is what setup's A2 step needs for `CONTEXT.md`;
|
|
47
|
+
- the fuller **problem + shape + seams + open questions** is what to-spec synthesizes into the spec.
|
|
48
|
+
|
|
49
|
+
So you interview ONCE. When setup's A2 asks "what is this repo about?", you already have the description — supply it, do not re-interview (that same-session double-ask is exactly the drift the on-ramp pattern forbids). setup (not from-idea) writes `CONTEXT.md` from that description.
|
|
50
|
+
|
|
51
|
+
## Step 2 — Run setup (always; let its idempotency decide depth)
|
|
52
|
+
|
|
53
|
+
**Always invoke `setup`** — do NOT hand-roll a `test -d work/` to decide whether to. setup's A1 already detects empty-vs-populated and does the right thing in each case, and re-running it on an existing contract repo **re-syncs the `work/protocol/` docs** (the one legitimate clobber) so the repo picks up protocol updates. Skipping setup would skip that refresh and fork its detection logic.
|
|
54
|
+
|
|
55
|
+
- **Empty / new repo (the common from-idea case):** setup does Phase A — scaffolds `work/`, copies `work/protocol/`, writes `CONTEXT.md` (from the description you gathered in step 1) and `.dorfl.json` (the `verify`/`prepare` gate it detects, presented for confirmation at its HARD STOP). Phase B is empty.
|
|
56
|
+
- **Existing contract repo (adding a new idea to a repo already set up):** setup's Phase A is a near-no-op + protocol re-sync; the scaffold already exists. You then go straight to step 3. **Flag the Phase-B-hijack seam:** if the repo is populated with convertible material, be explicit that the intent is "scaffold/refresh and accept a NEW idea", not "migrate everything" — do not let setup's Phase-B conversion hijack the from-idea session. (A new idea is not migration of existing material.)
|
|
57
|
+
|
|
58
|
+
Honor setup's HARD STOP: present the plan, wait for confirmation, then it scaffolds. Feed it the description from step 1 so its A2 does not re-ask.
|
|
59
|
+
|
|
60
|
+
## Step 3 — Hand the conversation to to-spec
|
|
61
|
+
|
|
62
|
+
Once `work/` exists (setup's scaffold confirmed and written), invoke `to-spec`. It synthesizes the SAME conversation you have been having — the idea, the clarifications from step 1, the codebase understanding setup just established — into `work/specs/ready/<slug>.md`:
|
|
63
|
+
|
|
64
|
+
- to-spec targets **`specs/ready/`** (the auto-task pool) — that is its written target and the goal of this on-ramp. from-idea does NOT route the spec into `specs/proposed/` (staging): there is no grilling/promotion gate here (grilling is scoped out), so the review gate is simply the **unstaged file** a human reviews before tasking. (If a session later wants the idea grilled before it is trusted, that is a separate move — and `specs/proposed/` is where a review-first spec would live — but from-idea's deliberate target is `ready/`.)
|
|
65
|
+
- to-spec sets the two autonomy axes from what the interview resolved: `humanOnly` if a human must drive the tasking, and `needsAnswers: true` (with the questions in the body) for everything step 1 deliberately deferred.
|
|
66
|
+
- to-spec writes the file UNSTAGED and reports the path. from-idea does not commit it.
|
|
67
|
+
|
|
68
|
+
## Report + hand off
|
|
69
|
+
|
|
70
|
+
Tell the user, concisely:
|
|
71
|
+
|
|
72
|
+
- the repo is now contract-ready (what setup scaffolded / re-synced, the `verify` gate configured);
|
|
73
|
+
- the spec written, by path — `work/specs/ready/<slug>.md` — left UNSTAGED for review, plus any `needsAnswers` questions it carries that a human must resolve before tasking;
|
|
74
|
+
- **what's next on the main flow:** review the spec, then task it with `to-task` (or `dorfl do spec:<slug>` once the runner is installed and the spec is agent-safe). If the idea has real design forks worth pressure-testing first, point at the `grilling` skill — explicitly NOT part of this on-ramp.
|
|
75
|
+
|
|
76
|
+
**Git etiquette:** never stage, commit, or push — leave both setup's scaffold and to-spec's spec in the working tree for the user to inspect and commit (the producer-skill convention setup and to-spec both follow).
|
|
77
|
+
|
|
78
|
+
## Boundary (what from-idea does NOT do)
|
|
79
|
+
|
|
80
|
+
- It does NOT reimplement detection (setup A1), the gate (setup A3/A3b), `CONTEXT.md` (setup A2), the spec shape / banner / target (to-spec + `spec-template.md`). Its only net-new surface is the idea interview + ordering the two calls + feeding the description to setup.
|
|
81
|
+
- It does NOT grill the idea adversarially (the `grilling` skill), force-resolve genuine unknowns (they become `needsAnswers`), or push a barely-a-wish idea onto the spec board (offer `notes/ideas/` instead).
|
|
82
|
+
- It is NOT called BY setup (one-way: from-idea → setup). setup may _mention_ it; it never invokes it.
|
|
83
|
+
- It NEVER auto-commits, and it never bulldozes setup's plan-confirm HARD STOP.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: merge-prs
|
|
3
|
+
disable-model-invocation: true
|
|
4
|
+
description: "Review the open work/ PRs and land them EFFICIENTLY: partition into conflict-free clusters, gate each cluster's combined tip ONCE, then merge the cluster; PRs that don't cleanly combine fall out to their own gate. The batch-landing sibling of drive-tasks (which builds+merges one task at a time). Requires gh + a GitHub arbiter in propose mode."
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# merge-prs
|
|
8
|
+
|
|
9
|
+
**Land the already-built, not build the not-yet-built.** `drive-tasks` is the conductor that _builds_ ready tasks one at a time and merges each PR as it opens. `merge-prs` starts one step LATER: a set of work PRs is already OPEN (they came from `dorfl do --propose`, `run`, CI intake, or a `drive-tasks` pass you didn't merge), and the job is to review them and get them ONTO `main` with the fewest possible gate runs.
|
|
10
|
+
|
|
11
|
+
The trick is **batching by conflict-free clustering**. Instead of one review then gate then merge per PR (N gate runs), partition the approved PRs into clusters that touch disjoint files, combine each cluster onto a scratch branch, run the repo's `verify` gate ONCE against that combined tip, and merge the whole cluster. N PRs that don't overlap become 1 gate run, not N.
|
|
12
|
+
|
|
13
|
+
It is a **methodology skill** (prose you follow), like `review` and `drive-tasks`, NOT a runner command. **Precondition:** it operates over **open GitHub PRs** on a repo using the **`work/` contract**, in **`propose` integration mode**, with **`gh`** available. If the arbiter is not GitHub, or integration is `merge` (no PRs), this skill does not apply as written (see [Provider assumption](#provider-assumption)). It composes:
|
|
14
|
+
|
|
15
|
+
- **`review`** (`skills/review/`): the diff-vs-criteria discipline, applied PER PR before it is eligible to join a cluster. A PR that fails review never enters the batch.
|
|
16
|
+
- **`drive-tasks`** (`skills/drive-tasks/`): the sibling conductor. Its [accumulate-don't-block rule](../drive-tasks/SKILL.md) and its **never-touch-the-target-checkout** rule (golden rule 7) apply here UNCHANGED. A PR you can't cleanly land goes in the stuck-set and you move on; all combining/gating happens in a scratch worktree/clone you own, never the human checkout.
|
|
17
|
+
|
|
18
|
+
## When to use vs. not
|
|
19
|
+
|
|
20
|
+
- **Use** when several work PRs are already open and you want to land the sound ones in as few gate runs as possible; after a `run`/CI burst left a pile of green-gated PRs awaiting a human merge; when you'd rather review-and-land a backlog of PRs than build new tasks.
|
|
21
|
+
- **Do NOT use** to BUILD tasks; that is `drive-tasks` (or `dorfl do`). If there are zero open PRs, there is nothing to do here. If integration mode is `merge` (direct-to-main, no PR), there is no PR surface to batch, because the landing already happened at build time.
|
|
22
|
+
|
|
23
|
+
## The one correctness rule (why clustering, not just "gate the combine")
|
|
24
|
+
|
|
25
|
+
The whole value is "gate once for N PRs", but a combined gate is only HONEST if **what you gated is what actually lands**. Two ways to break that, and the rule that avoids both:
|
|
26
|
+
|
|
27
|
+
- Under `gh pr merge`, each PR squash-merges onto the CURRENT `main`, so after the first merge the tree GitHub produces for the second PR is NOT byte-identical to the combined tip you gated. If two PRs touch the **same file**, a green combined gate can still yield a broken/conflicted `main`.
|
|
28
|
+
- So a combined gate over OVERLAPPING PRs is necessary-but-not-sufficient. It can lie.
|
|
29
|
+
|
|
30
|
+
**The rule: only ever batch PRs whose file-sets are DISJOINT.** Within a conflict-free cluster there is no overlap for a later merge to invalidate, so the combined green gate genuinely predicts each per-PR merge. Concretely: partition by "do these PRs' changed-file sets intersect?" A PR that overlaps any other lands in its OWN singleton cluster (gate it alone, merge it alone). You lose the batching win for that one, but never the honesty. This is the "combine only the PRs that make sense" instinct, made a hard invariant.
|
|
31
|
+
|
|
32
|
+
> Overlap is judged on **changed files** (`gh pr diff <n> --name-only`), which is a conservative proxy: two PRs can touch the same file on non-adjacent lines and still merge cleanly, so file-level disjointness may over-split (extra singletons) but never under-splits into a dishonest batch. That trade is deliberate: prefer an extra gate run over a lying one. (If you want tighter packing later, upgrade the test to an actual trial `git merge`/`rebase` in the scratch tree and treat a clean 3-way merge as non-conflicting; until then, file-set disjointness is the safe default.)
|
|
33
|
+
|
|
34
|
+
## The loop
|
|
35
|
+
|
|
36
|
+
Run from ANYWHERE: you need only the arbiter (a URL/remote) plus `gh` plus a scratch area. Resolve the arbiter EXPLICITLY; if `cwd` is the human's checkout, treat it as READ-ONLY (drive-tasks golden rule 7).
|
|
37
|
+
|
|
38
|
+
**0. Enumerate the open work PRs.** `gh pr list --state open --json number,title,headRefName,url`. Keep only the ones that are work-branch PRs (dorfl pushes `work/<slug>` head branches, so filter on `headRefName` prefix / your repo's convention). Map each PR back to its task slug and the `work/tasks/**/<slug>.md` it done-moves, so review has the acceptance criteria to check against.
|
|
39
|
+
|
|
40
|
+
**1. Per-PR review (the eligibility gate).** For EACH PR, apply the `review` discipline (`skills/review/`) to its diff vs. its task's criteria. This is the same Gate-3 diff-vs-criteria pass `drive-tasks` step 4 does, just done up front for all PRs:
|
|
41
|
+
|
|
42
|
+
- clear **APPROVE** → the PR is eligible to be batched.
|
|
43
|
+
- clear **BLOCK** (a drift note violated, an acceptance criterion unmet, an empty/vacuous diff) → drop it from the batch, record the blocking finding (post it as a PR comment leading with `BLOCK`, per drive-tasks step 4), and do NOT land it.
|
|
44
|
+
- genuine **judgement call** (maybe-blocking nit, ambiguously-met criterion) → that is a WALL, so stuck-set it and skip, per the [accumulate-don't-block rule](../drive-tasks/SKILL.md). Never merge on a coin-flip.
|
|
45
|
+
|
|
46
|
+
**2. Cluster the approved PRs by conflict-freedom.** For each approved PR collect its changed-file set (`gh pr diff <n> --name-only`). Group PRs so that within a group every pair has DISJOINT file-sets (the [one correctness rule](#the-one-correctness-rule-why-clustering-not-just-gate-the-combine)). Any PR overlapping another becomes its own singleton cluster. Note that dorfl PRs each done-move their OWN `work/tasks/…/<slug>.md` (distinct paths) and edit distinct code, so in practice most PRs land in one big disjoint cluster and overlaps are the exception.
|
|
47
|
+
|
|
48
|
+
**3. Per cluster: combine → gate ONCE → merge the cluster.** In a scratch worktree/clone you own (NEVER the human checkout):
|
|
49
|
+
|
|
50
|
+
- fetch the arbiter, create a scratch integration branch off the current `main`, and merge each PR's head branch into it (`git merge --no-ff <headRef>` or cherry-pick the PR range). A merge that CONFLICTS despite the file-set proxy (rare, but possible) → pull that PR OUT of the cluster into a singleton and re-form the cluster; do not force it.
|
|
51
|
+
- run the repo's **`verify` gate** ONCE against the combined tip. In this repo that is `pnpm -r build && pnpm -r test && pnpm format:check` (the `dorfl verify` equivalent; see AGENTS.md. Do NOT invent a gate, read the repo's `dorfl.json` `verify`).
|
|
52
|
+
- **green** → merge every PR in the cluster: `gh pr comment <n> --body "APPROVE ✅ (batch-gated with #a #b #c)"` then `gh pr merge <n> --squash --delete-branch` for each. Because the file-sets are disjoint, the sequential squash-merges reproduce the tip you gated.
|
|
53
|
+
- **red** → the cluster is NOT landable as a batch. Do NOT merge any of it on a batch coin-flip. Bisect by falling back to gating the cluster's PRs INDIVIDUALLY (singleton gate each), land the green ones, and stuck-set the red one with the failing gate output. A batch red almost always means one bad PR poisoning the combine, and the singleton pass isolates it.
|
|
54
|
+
|
|
55
|
+
**4. Recompute and continue.** Each cluster merge lands `work/tasks/done/<slug>.md` for its PRs on `main`. Fetch, re-enumerate open PRs (new ones may have appeared; a PR blocked-by a now-merged one may have become reviewable), and repeat from step 0 until no open work PR can advance.
|
|
56
|
+
|
|
57
|
+
**5. Surface the residue in ONE batch.** When nothing more can land, deal with the stuck-set exactly as drive-tasks does: **human present** → present the blocked/judgement-call PRs as one batched set of questions, take answers, resume; **no human reachable** → stop and report the stuck-set plus the landed/blocked summary. Never dribble questions one at a time.
|
|
58
|
+
|
|
59
|
+
## Confirm the run mode first
|
|
60
|
+
|
|
61
|
+
At the START, CONFIRM with the user (mirrors drive-tasks): squash vs. merge-commit for the `gh pr merge` (default `--squash --delete-branch`), and whether they want you to actually merge or only review-and-report the clusters (a dry-run: emit "these N PRs cluster into these groups, each would gate as one" without merging). Batch-MERGING is mutating and irreversible-ish (branches deleted), so a dry-run first pass is often the right default; offer it.
|
|
62
|
+
|
|
63
|
+
## Provider assumption
|
|
64
|
+
|
|
65
|
+
The `gh pr comment` + `gh pr merge` flow assumes **`propose` mode + a GitHub arbiter**, the only review-and-land surface dorfl exposes today. There is **no `dorfl` merge/land/approve verb yet** (checked against the CLI: `scan/run/do/advance/complete/promote/requeue/drop/intake/...`, none land a PR), so this skill is GitHub-propose-specific by necessity, same as `drive-tasks` step 5. In `merge` mode there are no PRs to batch. A future provider-agnostic land verb would let this skill drop the `gh` specifics; until then, adapt the merge mechanics to the repo's actual arbiter.
|
|
66
|
+
|
|
67
|
+
## Relationship to the other conductors
|
|
68
|
+
|
|
69
|
+
- **`drive-tasks`** BUILDS ready tasks (one at a time, build then review then merge). `merge-prs` starts where a build left an OPEN PR and batches the LANDING. Use `drive-tasks` to turn tasks into PRs; use `merge-prs` to turn a pile of PRs into merges cheaply.
|
|
70
|
+
- **`orchestrate`** is the meta-conductor over the whole tree. If it (or `run`) produced a burst of PRs, `merge-prs` is the natural closer to land them in a batch rather than one-by-one.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: orchestrate
|
|
3
|
+
disable-model-invocation: true
|
|
4
|
+
description: 'The human-in-the-loop meta-conductor: survey the whole work/ tree, advance every autonomous rung, batch the judgement residue to the human, then build the ready tasks via drive-tasks.'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# orchestrate
|
|
8
|
+
|
|
9
|
+
**The conductor of conductors.** `drive-tasks` builds the _ready tasks_. `orchestrate` is the layer above THAT: it looks at the **entire** `work/` tree — observations, ideas, specs, tasks, needs-attention — works out **what can advance and what is stuck on a human**, does every autonomous rung it can, and turns the human into nothing but an **answerer of well-batched questions**, looping until the backlog of ready tasks is drained.
|
|
10
|
+
|
|
11
|
+
It is a **methodology skill** (prose you follow), like `to-task` / `review` — NOT a runner command. It is **protocol-native**: it works the `work/` tree by reading the contract files directly (frontmatter + bodies), not by leaning on runner commands to tell it the state. (The one place runner commands ARE used is building — which it hands to `drive-tasks`, the skill whose job is to drive the `dorfl` CLI.) It composes:
|
|
12
|
+
|
|
13
|
+
- **`drive-tasks`** (`skills/drive-tasks/`) — to BUILD the ready tasks (you load and FOLLOW it for the build loop; it owns the runner-CLI mechanics).
|
|
14
|
+
- **`review`** (`skills/review/`) — to judge any artifact (task / spec / observation / code).
|
|
15
|
+
- **`to-task`** (`skills/to-task/`) — to task a ready spec into tasks (the `tasks/backlog` staging slot).
|
|
16
|
+
- **`promote`** (`skills/promote/`) — to judge a STAGED task/spec (`tasks/backlog/` / `specs/proposed/`) against its acceptance + destination before recommending its promotion into the pool.
|
|
17
|
+
- **`answer-questions`** (`skills/answer-questions/`) — to walk the open `work/questions/` sidecars, DRAFT answers to the factual ones for the human to ratify, and DEFER the genuine-judgement ones into the step-3 batch.
|
|
18
|
+
- **`to-spec`** (`skills/to-spec/`) — when an idea/observation has matured enough to become a spec.
|
|
19
|
+
|
|
20
|
+
## When to use vs. not
|
|
21
|
+
|
|
22
|
+
- **Use** when you have a populated `work/` and want the system to do _everything it can autonomously_ and then _ask you only at the real judgement residue_ — in one interactive sitting, with full visibility and agency; to answer "what needs me?" across the whole tree; as the human-driven alternative to the autonomous `advance` loop when you want to watch and steer.
|
|
23
|
+
- **Don't** use it just to build already-ready tasks (that's `drive-tasks` directly), just to task one spec (`to-task`), or just to review one artifact (`review`). Don't use it as the unattended daemon (`run`/`advance`). It is **always main-session and conversational** — its defining job is the live Q&A.
|
|
24
|
+
|
|
25
|
+
## Relationship to the autonomous `advance` engine
|
|
26
|
+
|
|
27
|
+
`orchestrate` is the **interactive, human-in-the-loop** way to drain a `work/` tree: it asks its questions conversationally, in the session, and you answer live. Its autonomous, file-mediated counterpart is the `advance` capability, driven by `run`/CI with `work/questions/` sidecars the human answers whenever they like. Same lifecycle drain, different mode: reach for `orchestrate` when the human is present and wants visibility + agency; the autonomous engine is for unattended draining. They share the same rung contract.
|
|
28
|
+
|
|
29
|
+
## Core invariant
|
|
30
|
+
|
|
31
|
+
**Advance every rung you can; for everything else, ASK — never invent an answer.** Each `work/` item has autonomous rungs (triage, task, build, promote, surface-question, answer/apply) and a judgement residue (ambiguity, design forks, `needsAnswers`, stale premises). Do the autonomous part; surface the residue as batched questions; apply the answers; repeat. The human's throughput is the only limit; everything else is automatic.
|
|
32
|
+
|
|
33
|
+
## The loop
|
|
34
|
+
|
|
35
|
+
### 1. SURVEY the whole tree (one read pass)
|
|
36
|
+
|
|
37
|
+
Read across ALL buckets and build a single picture of state + what each item needs to advance ONE rung:
|
|
38
|
+
|
|
39
|
+
- **`work/notes/observations/`** — untriaged signals. Each wants: promote to a task/spec/ADR? keep as a note? delete? (a judgement rung — compose `review`).
|
|
40
|
+
- **`work/notes/ideas/`** — incubating. Any matured enough to become a spec (`to-spec`)? Most are left alone (no readiness to force) — note them, don't push.
|
|
41
|
+
- **`work/specs/ready/`** — for each spec: is it already tasked (does it RESIDE in `work/specs/tasked/`)? `humanOnly`/`needsAnswers`? `taskedAfter:` satisfied? → **taskable now**, **blocked on a dep**, or **blocked on a human answer**. (Tasked-ness is folder residence, not a `tasked:` marker.)
|
|
42
|
+
- **STAGING** — `work/tasks/backlog/` (review-first tasks) and `work/specs/proposed/` (review-first specs), the items awaiting human promotion into the pool. NOT built/tasked here; they are a **promotion rung** (step 2, compose `promote`). Either folder may be absent when empty per the contract — treat a missing staging folder as "nothing awaiting promotion", not an error.
|
|
43
|
+
- **`work/tasks/ready/`** — the task dependency graph (READY / BLOCKED / GATED), AND each ready task's **freshness** (drifted vs current `tasks/done/`+code — same check `drive-tasks` step 1 does).
|
|
44
|
+
- **`work/questions/`** — open question sidecars (a `<type>-<slug>.md` with an unanswered entry). Each is an item PAUSED on an answer; classify each open question factual-vs-judgement for the answer rung (step 2, compose `answer-questions`). A missing/empty `work/questions/` means "no pending questions", per the empty-folder rule — not an error.
|
|
45
|
+
- **needs-attention** — stuck items (their per-item lock is `state: stuck`) + the recorded reason; each wants a human decision (requeue-continue / requeue-reset / re-scope / drop). Read them via `dorfl status`/`scan` (which read the lock refs).
|
|
46
|
+
|
|
47
|
+
Produce a short **state map**: what's ready to build, what's taskable, what's triageable, and what's parked on a human.
|
|
48
|
+
|
|
49
|
+
### 2. ADVANCE the non-build rungs (no human needed)
|
|
50
|
+
|
|
51
|
+
Do the autonomous rungs that PREPARE work — i.e. everything EXCEPT building ready tasks (building is step 4, deliberately last, so all gap-filling happens first). In leverage order (the rung that unlocks the most downstream work first):
|
|
52
|
+
|
|
53
|
+
- **Taskable specs** → task them, NAMING the choice between the two paths that meet at the same `work/tasks/*` artifact (don't default to the conversational one by reflex):
|
|
54
|
+
- **`dorfl do spec:<slug>`** — the AUTONOMOUS, unattended path (gate-gated by `autoTask` + the spec's own gates; runner-owns-git; harness/model/gate from dorfl config, don't hardcode). PREFER this for a **ready, agent-safe spec** (`humanOnly: false`, no open `needsAnswers`, `taskedAfter:` satisfied) — and it is the path to use when the intent is to exercise the runner.
|
|
55
|
+
- **`to-task`** (the skill) — the CONVERSATIONAL, human-in-the-loop, protocol-only path (no dorfl dependency). Use it for a **`humanOnly` / unclear / not-yet-ready spec**, or whenever a conversation is wanted. (`to-task` deliberately stays runner-agnostic — it never points BACK at `do spec:`; this routing lives HERE, in the runner-aware conductor, by design.)
|
|
56
|
+
- The choice is "unattended run vs conversation", decided by the spec classification you already did in step 1 (`humanOnly`/`needsAnswers`/`taskedAfter`). A `do spec:` on a spec it cannot take (e.g. `humanOnly`) correctly REFUSES on the agent path — that refusal IS the protocol routing you to `to-task`.
|
|
57
|
+
- Then **review the produced tasks** (compose `review`; the tasker's own review→edit loop also runs on the `do spec:` path). Newly-produced tasks feed back into the survey (they may be READY, or carry their own questions).
|
|
58
|
+
- **Staged items awaiting promotion** (`tasks/backlog/`, `specs/proposed/`) → run the **promotion rung**: for each, compose `promote` (review + freshness + pool-readiness gate) and emit promote / keep-staged / drop. A clear PROMOTE is recommended to the human (you never move it yourself — the runner's `promote` verb / the human does the `git mv`); a KEEP-STAGED with a fixable gap, or a judgement-call promotion, becomes a step-3 question; a clear DROP routes to the regime terminal. Promotion is the human review-gate, so the MOVE is always the human's/runner's — you surface the verdict.
|
|
59
|
+
- **Clearly-resolvable observations** → triage them (compose `triage-observations`: leave / promote into a self-contained task/spec/ADR / direct-delete) where the right outcome is obvious; the ambiguous ones become questions (step 3).
|
|
60
|
+
- **Open question sidecars** (`work/questions/`) → run the **answer rung**: compose `answer-questions` over the pending sidecars. It DRAFTS answers to the factual ones (each cited to its evidence) for the human to RATIFY — a ratified draft is a human-authored answer you then apply — and DEFERS the genuine-judgement ones into the step-3 batch. You never invent/finalise an answer (the human is the clock); you draft for ratification and surface the rest.
|
|
61
|
+
- **Apply any human answers** you already have (ratified drafts from the answer rung, plus answers from earlier in the session) → flip the relevant `needsAnswers`, fill the task/spec gap, which may make new items advanceable (re-run step 1's classification for them).
|
|
62
|
+
|
|
63
|
+
(Ready tasks are NOT built here — they accumulate for step 4, after the residue is resolved.)
|
|
64
|
+
|
|
65
|
+
Commit policy (matches the producer skills): **commit your own `work/notes/observations/` notes and small load-bearing forward-notes you plant in a task body** (these are contract-native protocol edits), plus the runner-owned transitions that `do spec:` / `do` / `complete` make themselves (the tasking transition, done-moves, PR merges). Do NOT hand-author-and-commit a full spec or a fresh TASK SET — producing those is `to-spec`/`to-task`' job, and per their convention they are left UNSTAGED for human review (you surface them; the human commits). Never sweep in unrelated source. Report everything you committed in the final summary. (Tasks that get BUILT are committed/merged by `drive-tasks` via the normal PR flow.)
|
|
66
|
+
|
|
67
|
+
### 3. ASK the residue — batched, conversational, never invented
|
|
68
|
+
|
|
69
|
+
Everything that needs judgement becomes a **question**. Do NOT ask one at a time and do NOT guess: **regroup all open questions into one efficient batch** (the discipline this skill is named for), each with:
|
|
70
|
+
|
|
71
|
+
- the item + the SPECIFIC question, inline context to answer without opening files, the consequence of each option, and a **suggested default** where you have a view.
|
|
72
|
+
- grouped/ordered by leverage (answer-unblocks-the-most first).
|
|
73
|
+
|
|
74
|
+
Present the batch, take the human's answers, then **apply them** (back to step 2 for the now-unblocked items). Iterate steps 1–3 until the only thing left is _building ready tasks_. Questions the human defers stay parked; you proceed with the rest.
|
|
75
|
+
|
|
76
|
+
> If the human prefers to answer later / asynchronously rather than inline, offer to persist the batch as a question file under `work/questions/<date>-batch.md` (the file-mediated fallback the autonomous `advance` engine also reads). Default is conversational.
|
|
77
|
+
|
|
78
|
+
### 4. BUILD the ready tasks (follow `drive-tasks`)
|
|
79
|
+
|
|
80
|
+
Once the survey + gap-filling have produced a set of READY tasks, build them by **loading and following the `drive-tasks` skill inline** (you are already in the human's session — `drive-tasks` runs its build→review→merge loop here, asking the human when it stalls, exactly as you do). Hand it the whole ready set; it re-runs its own freshness check + dependency ordering, so you do NOT need to pre-filter or pre-order beyond what your survey established. Its build-loop mechanics (the long `do` process, the interrupt footgun, the diff-review, merge) are ITS to own — don't re-derive them here.
|
|
81
|
+
|
|
82
|
+
Any task `drive-tasks` parks in its stuck-set (a drifted task, a Gate-3 judgement call) comes back to YOUR step 3 batch — same residue, same human.
|
|
83
|
+
|
|
84
|
+
### 5. LOOP until drained, then SUMMARISE
|
|
85
|
+
|
|
86
|
+
Repeat 1→4 until no rung can advance without a human answer you don't have. Then give the meta report:
|
|
87
|
+
|
|
88
|
+
- **Advanced autonomously** — observations triaged, specs tasked, tasks built+merged (PR numbers from `drive-tasks`'s own report).
|
|
89
|
+
- **What's now unlocked** — new ready tasks, newly-taskable specs, capabilities landed (the whole-tree view only this skill has).
|
|
90
|
+
- **Parked on the human** — the questions still unanswered / deferred, and exactly what each unblocks when answered.
|
|
91
|
+
- **Still stuck** — needs-attention items + the decision each awaits.
|
|
92
|
+
- **Suggested next sitting** — the smallest set of human answers that would unblock the most work.
|
|
93
|
+
|
|
94
|
+
## Pitfalls
|
|
95
|
+
|
|
96
|
+
- **Don't invent answers.** The one unforgivable move. A confident wrong answer to a judgement question produces drifted tasks that cost far more than asking. Ask.
|
|
97
|
+
- **Don't over-ask either.** Resolve from the code/ADRs what is genuinely a small certain factual gap; only the real judgement residue becomes a question (same discipline `drive-tasks`/the build agents use for tasks).
|
|
98
|
+
- **Commit observations + forward-notes; leave authored artifacts for review.** Your `work/notes/observations/` notes and small planted forward-notes are committed as you go (contract-native) and listed in the summary; a freshly-authored spec or task SET is left UNSTAGED for the human (the producer-skill convention). Don't sweep in unrelated source changes.
|
|
99
|
+
- **Building mechanics live in `drive-tasks`.** When you build (step 4), the long-running `do` process, the interrupt footgun (an abort does NOT kill the spawned agent), generous timeouts, flaky-gate retries, and the Gate-3 diff review are all `drive-tasks`'s — follow that skill for them; don't re-derive them here.
|
|
100
|
+
- **If your OWN run is interrupted, re-orient before resuming.** This loop can run long. On resume, do a fresh step-1 survey (state lives in the `work/` files + `git`, not your memory): re-read the buckets, check what is now in-progress / needs-attention / merged, and continue from the recomputed state — never assume the pre-interrupt picture still holds.
|
|
101
|
+
- **Ideas are incubating.** Don't force `work/notes/ideas/` toward readiness; surface the ripe ones, leave the rest.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: promote
|
|
3
|
+
description: 'The pre-promotion checklist: judge ONE staged work/ item (a task in tasks/backlog/, a spec in specs/proposed/) against its acceptance + destination before a human admits it into the agent pool. Emits a promote / keep-staged / drop recommendation; the human (or the runner promote verb) does the move.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# promote
|
|
7
|
+
|
|
8
|
+
**Staging is the human review-gate.** In the `work/` contract, `tasks/backlog/` and `specs/proposed/` are STAGING (untrusted / agent-authored output lands here); the agent POOL is `tasks/ready/` and `specs/ready/`. Items enter staging by a runner-deterministic placement decision; they leave it ONLY when a human promotes them. This skill is the **discipline a human applies to ONE staged item before promoting it** — it is the checklist that makes the gate a real review, not a rubber stamp.
|
|
9
|
+
|
|
10
|
+
It is a thin **methodology skill** (prose you follow), a sibling of `review`. It does NOT move anything: the promotion `git mv` (`tasks/backlog/ → tasks/ready/`, `specs/proposed/ → specs/ready/`) is a runner-owned transition (a future `dorfl promote <item>` verb, or the human's own move). You EMIT a verdict; the caller acts on it. Per the contract, an agent never sets position.
|
|
11
|
+
|
|
12
|
+
## When to use vs. not
|
|
13
|
+
|
|
14
|
+
- **Use** before admitting a staged task/spec into the pool: when reviewing what an agent (tasking / intake) emitted into `tasks/backlog/` or `specs/proposed/`, to decide promote / keep-staged / drop for each. `orchestrate` composes this in its survey (the "awaiting promotion" set); a human also reaches for it directly to clear a staging backlog.
|
|
15
|
+
- **Don't** use it to judge an item already IN the pool (that is plain `review`), to build a ready task (`drive-tasks`), or to task a spec (`to-task`). It is the ONE gate-crossing judgement: staging → pool.
|
|
16
|
+
|
|
17
|
+
## How to use
|
|
18
|
+
|
|
19
|
+
For each staged item, run the `review` discipline FIRST (it is the body of this check), then add the promotion-specific gate:
|
|
20
|
+
|
|
21
|
+
1. **Review the artifact** — apply `review` (i.e. `work/protocol/REVIEW-PROTOCOL.md`'s lenses + the destination check) to the staged task/spec exactly as you would any `work/` artifact. This already covers: does it deliver its stated goal, is it coherent, does it match the spec/ADR it descends from.
|
|
22
|
+
2. **Freshness / drift** — staging items are often agent-authored ahead of time. Spot- check the load-bearing premises against current reality (`tasks/done/` + `src/`): does anything it says is "not yet built / still TODO / has no consumers" already hold? A drifted staged item is NOT promotable as-is — it is keep-staged with the stale premise named (the same drift check `drive-tasks`/`orchestrate` run on ready tasks, applied one step earlier).
|
|
23
|
+
3. **Pool-readiness gate** — once in the pool, can the item rest there SAFELY? Promote if yes. The bar is "safe in the pool", NOT "claimable this instant":
|
|
24
|
+
- **`blockedBy` is NOT a promotion gate — it is machine-ENFORCED at claim time, so a blocked task is safe in the pool.** Do NOT keep a task staged merely because a `blockedBy` slug has not yet reached `tasks/done/`. The claim predicate already refuses an item whose `blockedBy` is unresolved, so a blocked-but-otherwise-ready task simply WAITS in `tasks/ready/` until its blocker lands, then becomes claimable automatically — no human re-check needed. Holding it in staging for an unresolved dependency duplicates an enforced invariant and needlessly strands ready work behind a human round-trip. (The blocker slug does not even need to exist yet; `blockedBy` resolves lazily against `tasks/done/` whenever a claim is attempted.) Promote a well-formed blocked task NOW; the dependency graph sequences it for you.
|
|
25
|
+
- **Task → the real gates are the UNENFORCED, human-owned ones:** `needsAnswers` is false (no open question a human must answer first — an open question is NOT machine-enforced, so a `needsAnswers:true` task promoted into the pool would be picked up blind), and `humanOnly` is correctly set (off unless never-for-agents-by-nature). A staged task carrying open questions is keep-staged until they are answered, not promoted-then-blocked. (Contrast `blockedBy` above: that one IS enforced, so it is not a reason to keep-stage.)
|
|
26
|
+
- **Spec** → `humanOnly`/`needsAnswers` correct and it is genuinely taskable (not still a design sketch). Like `blockedBy`, `taskedAfter:` is ENFORCED against `specs/tasked/` residence by the auto-tasker, so an unsatisfied `taskedAfter:` is NOT a reason to keep a taskable spec staged — promote it and let the tasker sequence it. An unready (design-sketch / question-bearing) spec stays `specs/proposed/`.
|
|
27
|
+
4. **No collision / no duplicate** — confirm no item with the same `(umbrella, slug)` already rests in the destination pool or a terminal, and the work isn't already covered by a done item.
|
|
28
|
+
5. **Verdict** — emit ONE of:
|
|
29
|
+
- **PROMOTE** — review passed, fresh, pool-ready, no collision. State the move the caller should make (`dorfl promote <item>`, or the `git mv`).
|
|
30
|
+
- **KEEP-STAGED** — a fixable gap that is NOT machine-enforced: a drifted premise, an open question (`needsAnswers`), a wrongly-set `humanOnly`, or a spec that is still a design sketch. (An unresolved `blockedBy` / `taskedAfter:` is NOT such a gap — it is enforced, so it is a PROMOTE, not a keep-staged.) State the SPECIFIC gap so it can be resolved, then re-checked.
|
|
31
|
+
- **DROP** — superseded / out-of-scope / duplicate. Route to the regime terminal (`tasks/cancelled/` / `specs/dropped/`) with the `reason:`, per the contract.
|
|
32
|
+
|
|
33
|
+
You WRITE nothing and you MOVE nothing — you surface the verdict; the human or the runner's `promote` verb performs the transition (and a DROP is its own runner-owned move to the terminal). Batch the verdicts when judging several staged items at once, ordered by leverage (what each promotion unblocks downstream).
|
|
34
|
+
|
|
35
|
+
> Why position is human-gated and runner-moved: placement is runner-deterministic on the way IN (the `originTrust` stamp + policy decide STAGING vs POOL); promotion OUT is the human's review-gate. The agent never sets the folder. See `work/protocol/WORK-CONTRACT.md` (staging → pool).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review
|
|
3
|
+
description: 'Thoroughly and adversarially review a work/-protocol artifact against the work/ contract, ending in a destination check against the spec/ADR goal. Use before any artifact is trusted: a task before it lands, code in a work PR against its task, a spec before tasking, or a captured note. Emits a verdict; the caller routes it.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# review
|
|
7
|
+
|
|
8
|
+
**The review discipline lives in `work/protocol/REVIEW-PROTOCOL.md`** (the in-band protocol doc every set-up repo carries; the source-of-truth is `skills/setup/protocol/REVIEW-PROTOCOL.md`). This skill is the **human-facing pointer** to that standard — the operator/agent entry point a person reaches for to invoke the discipline interactively. The standard itself (the lenses, the destination check, the emitted-verdict shape) is stated ONCE in the protocol doc so the autonomous runner and the human caller cannot drift.
|
|
9
|
+
|
|
10
|
+
## How to use
|
|
11
|
+
|
|
12
|
+
1. Read `work/protocol/REVIEW-PROTOCOL.md` in the repo you are working in.
|
|
13
|
+
2. Apply its lenses IN ORDER to the artifact under review, ENDING in the destination check.
|
|
14
|
+
3. Emit the verdict it specifies (`{verdict, findings, …}`); the caller routes it (you write nothing — see "Your output" in the protocol doc).
|
|
15
|
+
|
|
16
|
+
> Why the standard lives in `work/protocol/`: a `review`-named discipline that the autonomous runner invokes BY NAME must be in-band in every set-up repo, not host-installed. Operator skills (this file) are human-facing and not copied.
|