dorfl 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advance-ci-template.d.ts +3 -3
- package/dist/advance-ci-template.js +1 -1
- package/dist/advance-ci-template.js.map +1 -1
- package/dist/advance-classify.d.ts +5 -5
- package/dist/advance-classify.d.ts.map +1 -1
- package/dist/advance-classify.js +4 -4
- package/dist/advance-drivers.d.ts +8 -8
- package/dist/advance-drivers.d.ts.map +1 -1
- package/dist/advance-drivers.js +20 -8
- package/dist/advance-drivers.js.map +1 -1
- package/dist/advance-isolated.d.ts +3 -3
- package/dist/advance-isolated.d.ts.map +1 -1
- package/dist/advance-isolated.js +1 -1
- package/dist/advance-lifecycle-template.d.ts +2 -2
- package/dist/advance-lifecycle-template.d.ts.map +1 -1
- package/dist/advance-lifecycle-template.js +85 -13
- package/dist/advance-lifecycle-template.js.map +1 -1
- package/dist/advance-loop-driver.d.ts +3 -3
- package/dist/advance-loop-driver.d.ts.map +1 -1
- package/dist/advance-loop-driver.js +1 -1
- package/dist/advance-treeless-publish.d.ts +24 -1
- package/dist/advance-treeless-publish.d.ts.map +1 -1
- package/dist/advance-treeless-publish.js +41 -0
- package/dist/advance-treeless-publish.js.map +1 -1
- package/dist/advance.d.ts +69 -17
- package/dist/advance.d.ts.map +1 -1
- package/dist/advance.js +409 -102
- package/dist/advance.js.map +1 -1
- package/dist/advancing-lock.d.ts +31 -3
- package/dist/advancing-lock.d.ts.map +1 -1
- package/dist/advancing-lock.js +55 -5
- package/dist/advancing-lock.js.map +1 -1
- package/dist/agent-launch.d.ts +12 -0
- package/dist/agent-launch.d.ts.map +1 -1
- package/dist/agent-launch.js +22 -12
- package/dist/agent-launch.js.map +1 -1
- package/dist/agent-stop.d.ts +40 -2
- package/dist/agent-stop.d.ts.map +1 -1
- package/dist/agent-stop.js +30 -2
- package/dist/agent-stop.js.map +1 -1
- package/dist/apply-decide.d.ts +19 -5
- package/dist/apply-decide.d.ts.map +1 -1
- package/dist/apply-decide.js +38 -9
- package/dist/apply-decide.js.map +1 -1
- package/dist/apply-merge-action.d.ts +20 -8
- package/dist/apply-merge-action.d.ts.map +1 -1
- package/dist/apply-merge-action.js +44 -9
- package/dist/apply-merge-action.js.map +1 -1
- package/dist/apply-persist.d.ts +62 -31
- package/dist/apply-persist.d.ts.map +1 -1
- package/dist/apply-persist.js +173 -44
- package/dist/apply-persist.js.map +1 -1
- package/dist/apply-stuck-action.d.ts +151 -0
- package/dist/apply-stuck-action.d.ts.map +1 -0
- package/dist/apply-stuck-action.js +125 -0
- package/dist/apply-stuck-action.js.map +1 -0
- package/dist/brand.d.ts +12 -1
- package/dist/brand.d.ts.map +1 -1
- package/dist/brand.js +2 -1
- package/dist/brand.js.map +1 -1
- package/dist/buildable-body.d.ts +14 -14
- package/dist/buildable-body.js +9 -9
- package/dist/claim-cas.d.ts +3 -3
- package/dist/claim-cas.js +1 -1
- package/dist/claim-cas.js.map +1 -1
- package/dist/cli-spinner.d.ts +1 -1
- package/dist/cli-spinner.js +1 -1
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +385 -128
- package/dist/cli.js.map +1 -1
- package/dist/close-job-template.d.ts +3 -3
- package/dist/close-job-template.js +9 -9
- package/dist/close-job.d.ts +8 -8
- package/dist/close-job.js +25 -25
- package/dist/close-job.js.map +1 -1
- package/dist/complete.d.ts +9 -6
- package/dist/complete.d.ts.map +1 -1
- package/dist/complete.js +99 -45
- package/dist/complete.js.map +1 -1
- package/dist/concurrency.d.ts +1 -1
- package/dist/concurrency.js +1 -1
- package/dist/config.d.ts +86 -40
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +61 -9
- package/dist/config.js.map +1 -1
- package/dist/continue-branch.d.ts +1 -1
- package/dist/continue-branch.d.ts.map +1 -1
- package/dist/continue-branch.js +23 -1
- package/dist/continue-branch.js.map +1 -1
- package/dist/cwd-section.js +1 -1
- package/dist/cwd-section.js.map +1 -1
- package/dist/decision-engine.d.ts +39 -11
- package/dist/decision-engine.d.ts.map +1 -1
- package/dist/decision-engine.js +10 -6
- package/dist/decision-engine.js.map +1 -1
- package/dist/do-autopick.d.ts +6 -6
- package/dist/do-autopick.d.ts.map +1 -1
- package/dist/do-autopick.js +16 -6
- package/dist/do-autopick.js.map +1 -1
- package/dist/do-config.d.ts +4 -4
- package/dist/do-config.js +3 -3
- package/dist/do-config.js.map +1 -1
- package/dist/do-remote-auto.d.ts +2 -2
- package/dist/do-remote-auto.js +1 -1
- package/dist/do.d.ts +74 -81
- package/dist/do.d.ts.map +1 -1
- package/dist/do.js +439 -50
- package/dist/do.js.map +1 -1
- package/dist/drop-source.d.ts +2 -2
- package/dist/env-config.d.ts.map +1 -1
- package/dist/env-config.js +19 -11
- package/dist/env-config.js.map +1 -1
- package/dist/failure-cause.d.ts +3 -2
- package/dist/failure-cause.d.ts.map +1 -1
- package/dist/failure-cause.js +28 -1
- package/dist/failure-cause.js.map +1 -1
- package/dist/format.d.ts +6 -6
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +13 -30
- package/dist/format.js.map +1 -1
- package/dist/frontmatter.d.ts +28 -15
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +38 -24
- package/dist/frontmatter.js.map +1 -1
- package/dist/gc.d.ts +65 -6
- package/dist/gc.d.ts.map +1 -1
- package/dist/gc.js +126 -9
- package/dist/gc.js.map +1 -1
- package/dist/github.d.ts +14 -0
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +73 -0
- package/dist/github.js.map +1 -1
- package/dist/harness.d.ts +25 -0
- package/dist/harness.d.ts.map +1 -1
- package/dist/harness.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/install-ci-branch-protection.d.ts +133 -39
- package/dist/install-ci-branch-protection.d.ts.map +1 -1
- package/dist/install-ci-branch-protection.js +191 -49
- package/dist/install-ci-branch-protection.js.map +1 -1
- package/dist/install-ci-capabilities/advance-lifecycle.d.ts +1 -1
- package/dist/install-ci-capabilities/advance-lifecycle.js +1 -1
- package/dist/install-ci-capabilities/close-job.d.ts +1 -1
- package/dist/install-ci-capabilities/close-job.js +1 -1
- package/dist/install-ci-capabilities/example-noop.d.ts +1 -1
- package/dist/install-ci-capabilities/example-noop.js +1 -1
- package/dist/install-ci-capabilities/intake.d.ts +2 -2
- package/dist/install-ci-capabilities/intake.js +2 -2
- package/dist/install-ci-capabilities/verify.d.ts +1 -1
- package/dist/install-ci-capabilities/verify.js +1 -1
- package/dist/install-ci-core.d.ts +24 -4
- package/dist/install-ci-core.d.ts.map +1 -1
- package/dist/install-ci-core.js +12 -5
- package/dist/install-ci-core.js.map +1 -1
- package/dist/install-ci-github.d.ts +33 -1
- package/dist/install-ci-github.d.ts.map +1 -1
- package/dist/install-ci-github.js +74 -1
- package/dist/install-ci-github.js.map +1 -1
- package/dist/install-ci.d.ts +1 -1
- package/dist/install-ci.js +4 -4
- package/dist/install-ci.js.map +1 -1
- package/dist/install-skills.d.ts +123 -0
- package/dist/install-skills.d.ts.map +1 -0
- package/dist/install-skills.js +100 -0
- package/dist/install-skills.js.map +1 -0
- package/dist/intake-event.d.ts +6 -6
- package/dist/intake-event.js +6 -6
- package/dist/intake-marker.d.ts +3 -3
- package/dist/intake-marker.d.ts.map +1 -1
- package/dist/intake-triage.d.ts +1 -1
- package/dist/intake-triage.js +2 -2
- package/dist/intake-trigger-template.d.ts +8 -8
- package/dist/intake-trigger-template.d.ts.map +1 -1
- package/dist/intake-trigger-template.js +17 -17
- package/dist/intake-trigger-template.js.map +1 -1
- package/dist/intake.d.ts +65 -52
- package/dist/intake.d.ts.map +1 -1
- package/dist/intake.js +92 -76
- package/dist/intake.js.map +1 -1
- package/dist/integration-core.d.ts +27 -24
- package/dist/integration-core.d.ts.map +1 -1
- package/dist/integration-core.js +191 -77
- package/dist/integration-core.js.map +1 -1
- package/dist/integrator.d.ts +1 -1
- package/dist/integrator.d.ts.map +1 -1
- package/dist/integrator.js +19 -3
- package/dist/integrator.js.map +1 -1
- package/dist/isolation.d.ts +4 -4
- package/dist/isolation.d.ts.map +1 -1
- package/dist/isolation.js +9 -0
- package/dist/isolation.js.map +1 -1
- package/dist/issue-provider.d.ts +4 -4
- package/dist/issue-provider.js +1 -1
- package/dist/item-lock.d.ts +216 -149
- package/dist/item-lock.d.ts.map +1 -1
- package/dist/item-lock.js +345 -270
- package/dist/item-lock.js.map +1 -1
- package/dist/item-path.d.ts +2 -2
- package/dist/item-path.js +2 -2
- package/dist/ledger-lint.d.ts +9 -9
- package/dist/ledger-lint.js +9 -9
- package/dist/ledger-read.d.ts +57 -57
- package/dist/ledger-read.d.ts.map +1 -1
- package/dist/ledger-read.js +24 -24
- package/dist/ledger-read.js.map +1 -1
- package/dist/ledger-write.d.ts +33 -28
- package/dist/ledger-write.d.ts.map +1 -1
- package/dist/ledger-write.js +100 -122
- package/dist/ledger-write.js.map +1 -1
- package/dist/lifecycle-gather.d.ts +18 -1
- package/dist/lifecycle-gather.d.ts.map +1 -1
- package/dist/lifecycle-gather.js +23 -17
- package/dist/lifecycle-gather.js.map +1 -1
- package/dist/lifecycle-pools.d.ts +44 -7
- package/dist/lifecycle-pools.d.ts.map +1 -1
- package/dist/lifecycle-pools.js +27 -7
- package/dist/lifecycle-pools.js.map +1 -1
- package/dist/merge-question-surfacer.d.ts +18 -4
- package/dist/merge-question-surfacer.d.ts.map +1 -1
- package/dist/merge-question-surfacer.js +20 -5
- package/dist/merge-question-surfacer.js.map +1 -1
- package/dist/migrate-stuck-locks.d.ts +129 -0
- package/dist/migrate-stuck-locks.d.ts.map +1 -0
- package/dist/migrate-stuck-locks.js +355 -0
- package/dist/migrate-stuck-locks.js.map +1 -0
- package/dist/mint-adr.js +4 -4
- package/dist/mirror-pool-scan.d.ts +2 -2
- package/dist/mirror-pool-scan.js +3 -3
- package/dist/mirror-pool-scan.js.map +1 -1
- package/dist/needs-attention.d.ts +304 -27
- package/dist/needs-attention.d.ts.map +1 -1
- package/dist/needs-attention.js +542 -67
- package/dist/needs-attention.js.map +1 -1
- package/dist/orphan-sidecar.d.ts +10 -6
- package/dist/orphan-sidecar.d.ts.map +1 -1
- package/dist/orphan-sidecar.js +35 -2
- package/dist/orphan-sidecar.js.map +1 -1
- package/dist/pi-harness.d.ts +16 -0
- package/dist/pi-harness.d.ts.map +1 -1
- package/dist/pi-harness.js +82 -2
- package/dist/pi-harness.js.map +1 -1
- package/dist/placement.d.ts +8 -8
- package/dist/placement.js +3 -3
- package/dist/prd-to-spec.d.ts.map +1 -1
- package/dist/prd-to-spec.js +9 -5
- package/dist/prd-to-spec.js.map +1 -1
- package/dist/prompt.d.ts +16 -19
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +17 -19
- package/dist/prompt.js.map +1 -1
- package/dist/protocol/CLAIM-PROTOCOL.md +17 -10
- package/dist/protocol/REVIEW-PROTOCOL.md +4 -1
- package/dist/protocol/SURFACE-PROTOCOL.md +16 -2
- package/dist/protocol/TASKING-PROTOCOL.md +3 -1
- package/dist/protocol/WORK-CONTRACT.md +22 -18
- package/dist/protocol/task-template.md +1 -1
- package/dist/readiness.d.ts +1 -1
- package/dist/reap-branches.d.ts +12 -9
- package/dist/reap-branches.d.ts.map +1 -1
- package/dist/reap-branches.js +25 -7
- package/dist/reap-branches.js.map +1 -1
- package/dist/recover-isolated.d.ts +12 -0
- package/dist/recover-isolated.d.ts.map +1 -1
- package/dist/recover-isolated.js +6 -1
- package/dist/recover-isolated.js.map +1 -1
- package/dist/repo-config.d.ts +23 -2
- package/dist/repo-config.d.ts.map +1 -1
- package/dist/repo-config.js +64 -18
- package/dist/repo-config.js.map +1 -1
- package/dist/repo-mirror.d.ts.map +1 -1
- package/dist/repo-mirror.js +18 -2
- package/dist/repo-mirror.js.map +1 -1
- package/dist/review-gate.d.ts +3 -3
- package/dist/review-gate.d.ts.map +1 -1
- package/dist/review-gate.js +11 -10
- package/dist/review-gate.js.map +1 -1
- package/dist/review-verdict.d.ts +2 -2
- package/dist/review-verdict.d.ts.map +1 -1
- package/dist/review-verdict.js +2 -2
- package/dist/review-verdict.js.map +1 -1
- package/dist/run.d.ts +2 -2
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +58 -23
- package/dist/run.js.map +1 -1
- package/dist/scan.d.ts +26 -17
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +29 -15
- package/dist/scan.js.map +1 -1
- package/dist/select-order.d.ts +1 -1
- package/dist/select-priority.d.ts +14 -14
- package/dist/select-priority.d.ts.map +1 -1
- package/dist/select-priority.js +5 -5
- package/dist/select-priority.js.map +1 -1
- package/dist/sidecar-apply.js +1 -1
- package/dist/sidecar-apply.js.map +1 -1
- package/dist/sidecar.d.ts +47 -12
- package/dist/sidecar.d.ts.map +1 -1
- package/dist/sidecar.js +84 -4
- package/dist/sidecar.js.map +1 -1
- package/dist/skills/answer-questions/SKILL.md +89 -0
- package/dist/skills/capture-signal/SKILL.md +52 -0
- package/dist/skills/convert-from-prd-to-spec/SKILL.md +90 -0
- package/dist/skills/drive-tasks/SKILL.md +218 -0
- package/dist/skills/from-idea/SKILL.md +83 -0
- package/dist/skills/merge-prs/SKILL.md +70 -0
- package/dist/skills/orchestrate/SKILL.md +101 -0
- package/dist/skills/promote/SKILL.md +35 -0
- package/dist/skills/review/SKILL.md +16 -0
- package/dist/skills/setup/SKILL.md +258 -0
- package/dist/skills/setup/protocol/ADR-FORMAT.md +47 -0
- package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +224 -0
- package/dist/skills/setup/protocol/REVIEW-PROTOCOL.md +122 -0
- package/dist/skills/setup/protocol/SURFACE-PROTOCOL.md +135 -0
- package/dist/skills/setup/protocol/TASKING-PROTOCOL.md +124 -0
- package/dist/skills/setup/protocol/WORK-CONTRACT.md +280 -0
- package/dist/skills/setup/protocol/spec-template.md +71 -0
- package/dist/skills/setup/protocol/task-template.md +65 -0
- package/dist/skills/surface-questions/SKILL.md +16 -0
- package/dist/skills/to-spec/SKILL.md +34 -0
- package/dist/skills/to-task/SKILL.md +19 -0
- package/dist/skills/triage-observations/SKILL.md +78 -0
- package/dist/skills/work/SKILL.md +51 -0
- package/dist/slug-namespace.d.ts +6 -6
- package/dist/slug-namespace.js +8 -8
- package/dist/slug-namespace.js.map +1 -1
- package/dist/spec-complete.d.ts.map +1 -1
- package/dist/spec-complete.js +4 -5
- package/dist/spec-complete.js.map +1 -1
- package/dist/start.d.ts +1 -1
- package/dist/start.d.ts.map +1 -1
- package/dist/start.js +54 -60
- package/dist/start.js.map +1 -1
- package/dist/status.d.ts +3 -3
- package/dist/status.js +4 -4
- package/dist/status.js.map +1 -1
- package/dist/surface-gate.d.ts +2 -2
- package/dist/surface-gate.d.ts.map +1 -1
- package/dist/surface-gate.js +10 -3
- package/dist/surface-gate.js.map +1 -1
- package/dist/surface-persist.d.ts +2 -2
- package/dist/surface-persist.d.ts.map +1 -1
- package/dist/surface-persist.js +1 -1
- package/dist/surface-persist.js.map +1 -1
- package/dist/tasker-review-loop.d.ts +11 -10
- package/dist/tasker-review-loop.d.ts.map +1 -1
- package/dist/tasker-review-loop.js +8 -8
- package/dist/tasker-review-loop.js.map +1 -1
- package/dist/tasking-eligibility.d.ts +21 -21
- package/dist/tasking-eligibility.d.ts.map +1 -1
- package/dist/tasking-eligibility.js +10 -10
- package/dist/tasking-lock.d.ts +10 -10
- package/dist/tasking-lock.d.ts.map +1 -1
- package/dist/tasking-lock.js +69 -49
- package/dist/tasking-lock.js.map +1 -1
- package/dist/tasking.d.ts +73 -39
- package/dist/tasking.d.ts.map +1 -1
- package/dist/tasking.js +309 -95
- package/dist/tasking.js.map +1 -1
- package/dist/triage-gate.d.ts +1 -1
- package/dist/triage-persist.d.ts +15 -11
- package/dist/triage-persist.d.ts.map +1 -1
- package/dist/triage-persist.js +48 -18
- package/dist/triage-persist.js.map +1 -1
- package/dist/vendor/incur/agents.d.ts +58 -0
- package/dist/vendor/incur/agents.d.ts.map +1 -0
- package/dist/vendor/incur/agents.js +343 -0
- package/dist/vendor/incur/agents.js.map +1 -0
- package/dist/verify-workflow-template.d.ts +1 -1
- package/dist/verify-workflow-template.js +3 -3
- package/dist/watch-session.d.ts +11 -3
- package/dist/watch-session.d.ts.map +1 -1
- package/dist/watch-session.js +94 -7
- package/dist/watch-session.js.map +1 -1
- package/dist/work-layout.d.ts +15 -9
- package/dist/work-layout.d.ts.map +1 -1
- package/dist/work-layout.js +14 -9
- package/dist/work-layout.js.map +1 -1
- package/dist/workspace.d.ts +2 -2
- package/package.json +2 -2
- package/src/advance-ci-template.ts +4 -4
- package/src/advance-classify.ts +5 -5
- package/src/advance-drivers.ts +26 -13
- package/src/advance-isolated.ts +3 -3
- package/src/advance-lifecycle-template.ts +98 -13
- package/src/advance-loop-driver.ts +3 -3
- package/src/advance-treeless-publish.ts +46 -1
- package/src/advance.ts +495 -115
- package/src/advancing-lock.ts +102 -7
- package/src/agent-launch.ts +37 -12
- package/src/agent-stop.ts +60 -2
- package/src/apply-decide.ts +38 -9
- package/src/apply-merge-action.ts +49 -11
- package/src/apply-persist.ts +236 -62
- package/src/apply-stuck-action.ts +260 -0
- package/src/brand.ts +14 -2
- package/src/buildable-body.ts +14 -14
- package/src/claim-cas.ts +4 -4
- package/src/cli-spinner.ts +1 -1
- package/src/cli.ts +472 -141
- package/src/close-job-template.ts +9 -9
- package/src/close-job.ts +26 -26
- package/src/complete.ts +121 -63
- package/src/concurrency.ts +1 -1
- package/src/config.ts +143 -49
- package/src/continue-branch.ts +23 -1
- package/src/cwd-section.ts +1 -1
- package/src/decision-engine.ts +57 -19
- package/src/do-autopick.ts +21 -10
- package/src/do-config.ts +7 -7
- package/src/do-remote-auto.ts +2 -2
- package/src/do.ts +558 -85
- package/src/drop-source.ts +2 -2
- package/src/env-config.ts +19 -11
- package/src/failure-cause.ts +30 -1
- package/src/format.ts +13 -33
- package/src/frontmatter.ts +55 -35
- package/src/gc.ts +172 -9
- package/src/github.ts +78 -0
- package/src/harness.ts +25 -0
- package/src/index.ts +10 -0
- package/src/install-ci-branch-protection.ts +283 -58
- package/src/install-ci-capabilities/advance-lifecycle.ts +1 -1
- package/src/install-ci-capabilities/close-job.ts +1 -1
- package/src/install-ci-capabilities/example-noop.ts +1 -1
- package/src/install-ci-capabilities/intake.ts +2 -2
- package/src/install-ci-capabilities/verify.ts +1 -1
- package/src/install-ci-core.ts +34 -7
- package/src/install-ci-github.ts +87 -1
- package/src/install-ci.ts +4 -4
- package/src/install-skills.ts +166 -0
- package/src/intake-event.ts +6 -6
- package/src/intake-marker.ts +3 -3
- package/src/intake-triage.ts +2 -2
- package/src/intake-trigger-template.ts +18 -18
- package/src/intake.ts +138 -103
- package/src/integration-core.ts +234 -100
- package/src/integrator.ts +22 -9
- package/src/isolation.ts +13 -4
- package/src/issue-provider.ts +4 -4
- package/src/item-lock.ts +505 -361
- package/src/item-path.ts +2 -2
- package/src/ledger-lint.ts +9 -9
- package/src/ledger-read.ts +81 -81
- package/src/ledger-write.ts +137 -152
- package/src/lifecycle-gather.ts +45 -22
- package/src/lifecycle-pools.ts +71 -13
- package/src/merge-question-surfacer.ts +32 -8
- package/src/migrate-stuck-locks.ts +451 -0
- package/src/mint-adr.ts +4 -4
- package/src/mirror-pool-scan.ts +5 -5
- package/src/needs-attention.ts +877 -104
- package/src/orphan-sidecar.ts +49 -8
- package/src/pi-harness.ts +82 -2
- package/src/placement.ts +8 -8
- package/src/prd-to-spec.ts +13 -5
- package/src/prompt.ts +20 -25
- package/src/readiness.ts +1 -1
- package/src/reap-branches.ts +38 -14
- package/src/recover-isolated.ts +18 -1
- package/src/repo-config.ts +66 -17
- package/src/repo-mirror.ts +23 -1
- package/src/review-gate.ts +12 -11
- package/src/review-verdict.ts +3 -3
- package/src/run.ts +69 -23
- package/src/scan.ts +36 -22
- package/src/select-order.ts +1 -1
- package/src/select-priority.ts +15 -15
- package/src/sidecar-apply.ts +1 -1
- package/src/sidecar.ts +141 -14
- package/src/slug-namespace.ts +10 -10
- package/src/spec-complete.ts +4 -5
- package/src/start.ts +57 -63
- package/src/status.ts +7 -7
- package/src/surface-gate.ts +12 -5
- package/src/surface-persist.ts +3 -3
- package/src/tasker-review-loop.ts +18 -17
- package/src/tasking-eligibility.ts +21 -21
- package/src/tasking-lock.ts +75 -60
- package/src/tasking.ts +381 -135
- package/src/triage-gate.ts +1 -1
- package/src/triage-persist.ts +71 -27
- package/src/vendor/incur/LICENSE +21 -0
- package/src/vendor/incur/README.md +19 -0
- package/src/vendor/incur/agents.ts +392 -0
- package/src/verify-workflow-template.ts +3 -3
- package/src/watch-session.ts +102 -7
- package/src/work-layout.ts +14 -9
- package/src/workspace.ts +2 -2
package/src/advance.ts
CHANGED
|
@@ -66,13 +66,19 @@ import {
|
|
|
66
66
|
type MergeActionHandler,
|
|
67
67
|
type MergeActionResult,
|
|
68
68
|
} from './apply-merge-action.js';
|
|
69
|
+
import {
|
|
70
|
+
detectAnsweredStuckAction,
|
|
71
|
+
performStuckAction,
|
|
72
|
+
type StuckActionHandler,
|
|
73
|
+
type StuckActionResult,
|
|
74
|
+
} from './apply-stuck-action.js';
|
|
69
75
|
import type {VerifyConfig} from './verify.js';
|
|
70
76
|
import type {NewQuestion} from './sidecar.js';
|
|
71
77
|
|
|
72
78
|
/**
|
|
73
|
-
* The **`advance` verb SKELETON** (
|
|
79
|
+
* The **`advance` verb SKELETON** (spec `advance-loop`, task
|
|
74
80
|
* `advance-verb-resolver`, US #1/5/6/18). `advance` is the SIBLING top-level verb
|
|
75
|
-
* (NOT a `do` subcommand — `do` subcommands are REJECTED in the
|
|
81
|
+
* (NOT a `do` subcommand — `do` subcommands are REJECTED in the spec) that drives
|
|
76
82
|
* a `work/` item ONE lifecycle rung toward "ready/built", reusing the SAME shared
|
|
77
83
|
* `prefix:arg` resolver `do` uses (extended with the `obs:` namespace, see
|
|
78
84
|
* {@link resolveAdvanceArg}).
|
|
@@ -96,7 +102,7 @@ import type {NewQuestion} from './sidecar.js';
|
|
|
96
102
|
* (`advance-rung-surface`), `apply` (`advance-rung-apply`), and
|
|
97
103
|
* `triage-observation` (`advance-rung-triage`) dispatch through the clearly-named
|
|
98
104
|
* executor SEAM ({@link RungExecutor}); the build/task rungs ORCHESTRATE
|
|
99
|
-
* `do`/`do
|
|
105
|
+
* `do`/`do spec:`. What this verb does NOT do (LATER tasks):
|
|
100
106
|
* - The two **DRIVERS** (one-shot sequential / loop) + `-n` + the gate-FAMILY
|
|
101
107
|
* WIRING that resolves `autoBuild`/`autoTask`/`observationTriage` and threads
|
|
102
108
|
* them into the build/task gate composition — task `advance-drivers-and-gates`.
|
|
@@ -131,11 +137,34 @@ export type AdvanceOutcome =
|
|
|
131
137
|
| 'advanced'
|
|
132
138
|
| 'no-op'
|
|
133
139
|
| 'vanished'
|
|
140
|
+
/**
|
|
141
|
+
* The triage/apply promote leg found the task it would mint ALREADY EXISTS on
|
|
142
|
+
* the arbiter AND was PROVABLY minted from THIS observation in a prior run (its
|
|
143
|
+
* `promotedFrom:` back-reference matches) — an idempotency fact, so the source was
|
|
144
|
+
* already triaged. `exitCode: 0` (the matrix tolerates it, so it does NOT red CI),
|
|
145
|
+
* DISTINGUISHABLE from `vanished` (item file gone) and `no-op` (calm classify) so
|
|
146
|
+
* reviewers can grep it. The LOUD `lost` (exit 2) is reserved for a genuine
|
|
147
|
+
* concurrent-create race with an UNRELATED same-path item, where a retry helps.
|
|
148
|
+
* (task `observation-triage-already-triaged-benign-skip`; sibling of the
|
|
149
|
+
* stale-snapshot / held-lock CI-noise fixes.)
|
|
150
|
+
*/
|
|
151
|
+
| 'already-triaged'
|
|
134
152
|
| 'usage-error'
|
|
135
153
|
| 'lost'
|
|
136
154
|
| 'contended'
|
|
137
155
|
| 'not-implemented'
|
|
138
|
-
| 'invariant-violation'
|
|
156
|
+
| 'invariant-violation'
|
|
157
|
+
/**
|
|
158
|
+
* The answered-merge dispatcher REFUSED the land on the rebased tip (RED
|
|
159
|
+
* re-verify, rebase conflict, or a pre-checkout failure). Distinct from
|
|
160
|
+
* `usage-error` (which is reserved for genuine caller-usage errors — e.g.
|
|
161
|
+
* the workspacesDir-unset guard below). `performIntegration` has already
|
|
162
|
+
* routed the item to needs-attention via its shared seam, so `main` never
|
|
163
|
+
* received a failing tree; the sidecar is LEFT IN PLACE so the open answer
|
|
164
|
+
* stays surfaced for a human follow-up. `exitCode: 1`.
|
|
165
|
+
* (task `merge-action-nits-followup` nit 2.)
|
|
166
|
+
*/
|
|
167
|
+
| 'merge-refused';
|
|
139
168
|
|
|
140
169
|
/** Maps onto the claim-CAS exit codes (identical semantics). */
|
|
141
170
|
export type AdvanceExitCode = 0 | 1 | 2 | 3;
|
|
@@ -144,7 +173,7 @@ export type AdvanceExitCode = 0 | 1 | 2 | 3;
|
|
|
144
173
|
* The injectable rung-executor SEAM — WHAT happens once the tick has classified a
|
|
145
174
|
* rung AND won the `advancing` lock. It is the boundary between the skeleton (this
|
|
146
175
|
* task) and the rung bodies (later tasks): the surface/apply/triage rungs are
|
|
147
|
-
* filled by their own tasks; the build/task rungs ORCHESTRATE `do`/`do
|
|
176
|
+
* filled by their own tasks; the build/task rungs ORCHESTRATE `do`/`do spec:`.
|
|
148
177
|
*
|
|
149
178
|
* Production wires {@link defaultRungExecutor}; tests inject a spy to assert the
|
|
150
179
|
* classify→lock→dispatch ORDER (and that a CAS loser never reaches the executor).
|
|
@@ -164,15 +193,15 @@ export interface RungExecutor {
|
|
|
164
193
|
|
|
165
194
|
/** What a rung executor is handed: the resolved identity + the run context. */
|
|
166
195
|
export interface RungExecInput {
|
|
167
|
-
/** The canonical namespaced identity (`task:<slug>` / `
|
|
196
|
+
/** The canonical namespaced identity (`task:<slug>` / `spec:<slug>` / `observation:<slug>`). */
|
|
168
197
|
item: string;
|
|
169
|
-
/** The resolved namespace (`task` / `
|
|
198
|
+
/** The resolved namespace (`task` / `spec` / `observation`). */
|
|
170
199
|
namespace: SlugNamespace;
|
|
171
200
|
/** The bare slug. */
|
|
172
201
|
slug: string;
|
|
173
202
|
/** The classification that selected this rung (the two signals are visible). */
|
|
174
203
|
classification: TickClassification;
|
|
175
|
-
/** The tick's run context (cwd, arbiter, …) — threaded to `do`/`do
|
|
204
|
+
/** The tick's run context (cwd, arbiter, …) — threaded to `do`/`do spec:`. */
|
|
176
205
|
context: AdvanceContext;
|
|
177
206
|
}
|
|
178
207
|
|
|
@@ -250,14 +279,14 @@ export interface AdvanceContext {
|
|
|
250
279
|
* The AGENTIC apply DECISION seam (task
|
|
251
280
|
* `agentic-apply-retire-disposition-vocabulary`): the fresh-context decision
|
|
252
281
|
* agent the apply rung runs on a fully-answered OBSERVATION to choose what to DO
|
|
253
|
-
* with the signal (`mint-task | mint-
|
|
254
|
-
* ask-follow-up`),
|
|
282
|
+
* with the signal (`mint-task | mint-spec | mint-adr | dispose-source |
|
|
283
|
+
* resolve-no-mint | ask-follow-up`),
|
|
255
284
|
* grounded in the source's full context. It is the injected
|
|
256
285
|
* {@link ApplyDecider} the shared `decide(input, allowedOutcomes)` engine runs;
|
|
257
286
|
* tests inject a CANNED verdict (no model). `undefined` ⇒ the apply rung defaults
|
|
258
287
|
* to {@link harnessApplyDecider} (a NullHarness, no real model) so the seam is
|
|
259
288
|
* never a crash — but the CLI threads the real harness-backed decider. The
|
|
260
|
-
* verdict's type SELECTION (task vs
|
|
289
|
+
* verdict's type SELECTION (task vs spec vs adr) replaces the retired `promote-*`
|
|
261
290
|
* disposition token; `adr` is now WIRED (task `agentic-apply-mint-adr-route`).
|
|
262
291
|
*/
|
|
263
292
|
applyDecide?: ApplyDecider;
|
|
@@ -322,10 +351,14 @@ export interface AdvanceContext {
|
|
|
322
351
|
* The execution working area (`workspacesDir`, default `~/.dorfl`) the
|
|
323
352
|
* answered-merge LAND uses to cut a per-job worktree from the hub mirror
|
|
324
353
|
* (via `workspace.ts` `createJob`). Unset ⇒ no `workspacesDir` is available
|
|
325
|
-
* to the dispatcher, so an answered `kind: merge` entry is REFUSED
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
354
|
+
* to the dispatcher, so an answered `kind: merge` entry is REFUSED as a
|
|
355
|
+
* genuine caller-usage error (outcome `usage-error`, `exitCode: 1`) and the
|
|
356
|
+
* answer stays surfaced for a follow-up — threading `workspacesDir` is the
|
|
357
|
+
* caller's contract, and forgetting it gets this documented clean refusal
|
|
358
|
+
* rather than a mysterious downstream failure (task
|
|
359
|
+
* `merge-action-nits-followup` nit 3). The registry-set advance driver
|
|
360
|
+
* threads the resolved `workspacesDir` here.
|
|
361
|
+
* (spec `land-time-reverify-and-parallel-merge-ceiling`, task
|
|
329
362
|
* `apply-rung-merge-disposition`)
|
|
330
363
|
*/
|
|
331
364
|
workspacesDir?: string;
|
|
@@ -353,7 +386,7 @@ export interface AdvanceContext {
|
|
|
353
386
|
/**
|
|
354
387
|
* Resolved `strictMergeApproval` boolean (sibling task
|
|
355
388
|
* `strict-merge-approval-gate`). Default OFF ⇒ honour the prior approval +
|
|
356
|
-
* land on a green re-verify (cheap, the
|
|
389
|
+
* land on a green re-verify (cheap, the SPEC-applied OQ6 default). ON ⇒
|
|
357
390
|
* re-surface the merge-question when the merge-base moved between the
|
|
358
391
|
* surfacer's question and this apply (the host-agnostic analogue of
|
|
359
392
|
* GitHub's "dismiss stale approvals when the base changes").
|
|
@@ -370,6 +403,20 @@ export interface AdvanceContext {
|
|
|
370
403
|
* WITHOUT spinning up a hub mirror or running real verify.
|
|
371
404
|
*/
|
|
372
405
|
mergeAction?: MergeActionHandler;
|
|
406
|
+
/**
|
|
407
|
+
* The stuck-action dispatch SEAM (task
|
|
408
|
+
* `apply-resolve-reset-flag-discards-work-branch`, spec
|
|
409
|
+
* `surface-stuck-as-questions-and-retire-stuck-lock-state`): the
|
|
410
|
+
* deterministic answer-driven runner-action handler the apply rung invokes
|
|
411
|
+
* BEFORE the fall-through persist when an answered `kind: 'stuck'` entry
|
|
412
|
+
* (the shape the bounce-surface path stamps) is detected. Production wires
|
|
413
|
+
* {@link performStuckAction} (drives the shared
|
|
414
|
+
* `deleteRemoteWorkBranchIfPresent` primitive on `reset`); tests inject a
|
|
415
|
+
* stub so they assert on the apply-rung's `keep | reset | refused | cancel`
|
|
416
|
+
* routing WITHOUT touching a real arbiter. The deterministic sibling of
|
|
417
|
+
* {@link mergeAction} for the `kind: 'stuck'` axis.
|
|
418
|
+
*/
|
|
419
|
+
stuckAction?: StuckActionHandler;
|
|
373
420
|
/** Sink for human-readable progress notes. */
|
|
374
421
|
note?: (message: string) => void;
|
|
375
422
|
}
|
|
@@ -377,7 +424,7 @@ export interface AdvanceContext {
|
|
|
377
424
|
/** The options one `advance` tick consumes. */
|
|
378
425
|
export interface AdvanceOptions extends AdvanceContext {
|
|
379
426
|
/**
|
|
380
|
-
* The raw CLI slug argument: bare (= task), `task:<slug>`, `
|
|
427
|
+
* The raw CLI slug argument: bare (= task), `task:<slug>`, `spec:<slug>`, or
|
|
381
428
|
* `obs:<slug>` / `observation:<slug>`. Omit/empty ⇒ the bare eligible-SET form,
|
|
382
429
|
* which needs the driver task (a clear error here — see `## Decisions`).
|
|
383
430
|
*/
|
|
@@ -420,7 +467,7 @@ export interface ItemSignals {
|
|
|
420
467
|
export interface ReadSignalsInput {
|
|
421
468
|
/** The repo working-tree root. */
|
|
422
469
|
repoPath: string;
|
|
423
|
-
/** The item type (task /
|
|
470
|
+
/** The item type (task / spec / observation). */
|
|
424
471
|
type: SidecarType;
|
|
425
472
|
/** The bare slug. */
|
|
426
473
|
slug: string;
|
|
@@ -469,14 +516,14 @@ export function readItemSignals(input: ReadSignalsInput): ItemSignals {
|
|
|
469
516
|
* The lifecycle folders each item type may rest in (frontmatter source). After
|
|
470
517
|
* the capstone cut-over (task
|
|
471
518
|
* `cutover-retire-slicing-advancing-markers-and-trim-folder-sets`) the transient
|
|
472
|
-
* `tasking/` folder is GONE — a
|
|
473
|
-
* `
|
|
474
|
-
* `
|
|
519
|
+
* `tasking/` folder is GONE — a spec rests in `specs/ready` (source) or
|
|
520
|
+
* `specs/tasked` (tasked); while it is being tasked the body STAYS in
|
|
521
|
+
* `specs/ready` (the lock no longer moves it), so `tasking/` is never a
|
|
475
522
|
* frontmatter source.
|
|
476
523
|
*
|
|
477
524
|
* STAGING IS INCLUDED (`tasks-backlog` / `prds-proposed`): with `surfaceStaging`
|
|
478
525
|
* on (the user-visible default) the lifecycle surface pool enumerates
|
|
479
|
-
* `needsAnswers` items resting in STAGING as `task:`/`
|
|
526
|
+
* `needsAnswers` items resting in STAGING as `task:`/`spec:` legs
|
|
480
527
|
* (`lifecycle-gather.ts`). The rung CLASSIFIER's signal read MUST see those
|
|
481
528
|
* staged bodies, or `needsAnswers` reads back `undefined`, the classifier
|
|
482
529
|
* mis-routes the item to the BUILD rung, and claim dies with "not found on
|
|
@@ -512,11 +559,11 @@ function readNeedsAnswers(
|
|
|
512
559
|
|
|
513
560
|
/**
|
|
514
561
|
* The PRODUCTION rung executor: build/task rungs ORCHESTRATE the existing
|
|
515
|
-
* `do`/`do
|
|
562
|
+
* `do`/`do spec:` machinery ({@link performDo}); the `surface`/`apply`/
|
|
516
563
|
* `triage-observation` rung bodies are filled by their own tasks
|
|
517
564
|
* ({@link surfaceRung} / {@link applyRung} / {@link triageRung}). It NEVER
|
|
518
565
|
* re-implements the build/task path — it hands the resolved arg to `performDo`,
|
|
519
|
-
* which spans both namespaces (the task path is the `do
|
|
566
|
+
* which spans both namespaces (the task path is the `do spec:` rung the spec's
|
|
520
567
|
* 2026-06-09 UPDATE confirms routes through `performIntegration`).
|
|
521
568
|
*/
|
|
522
569
|
export const defaultRungExecutor: RungExecutor = {
|
|
@@ -602,7 +649,10 @@ function mapDoOutcome(result: DoResult): AdvanceOutcome {
|
|
|
602
649
|
* answered sidecar back to not-all-answered (the persist owns that). An EMPTY
|
|
603
650
|
* emit (the skill's honest "no open judgement") writes nothing and reports it.
|
|
604
651
|
*/
|
|
605
|
-
async function surfaceRung(
|
|
652
|
+
async function surfaceRung(
|
|
653
|
+
input: RungExecInput,
|
|
654
|
+
surfaceOpts: {baseQuestions?: NewQuestion[]} = {},
|
|
655
|
+
): Promise<RungExecResult> {
|
|
606
656
|
const {item, context} = input;
|
|
607
657
|
const note = context.note ?? (() => {});
|
|
608
658
|
const cwd = context.cwd;
|
|
@@ -615,50 +665,84 @@ async function surfaceRung(input: RungExecInput): Promise<RungExecResult> {
|
|
|
615
665
|
return vanishedSkip({rung: 'surface', item});
|
|
616
666
|
}
|
|
617
667
|
|
|
668
|
+
// DETERMINISTIC BASE QUESTIONS (the triage rung's "always ask" contract): the
|
|
669
|
+
// caller may pass engine-built questions that MUST be surfaced regardless of the
|
|
670
|
+
// agent. They are added ONLY on the FIRST pass (no sidecar yet) — on a re-surface
|
|
671
|
+
// the sidecar already carries them, and `appendQuestions` does NOT dedup, so
|
|
672
|
+
// re-adding would duplicate. When base questions are present the agent is
|
|
673
|
+
// ADDITIVE ONLY and its flake/empty is NON-FATAL (the base question still lands).
|
|
674
|
+
const baseQuestions = surfaceOpts.baseQuestions ?? [];
|
|
675
|
+
const hasBase = baseQuestions.length > 0;
|
|
676
|
+
const sidecarExists = existsSync(join(cwd, sidecarPathFor(item)));
|
|
677
|
+
const baseToAdd = hasBase && !sidecarExists ? baseQuestions : [];
|
|
678
|
+
|
|
618
679
|
// 1. SPAWN the fresh-context `surface-questions` agent (the skill JUDGES). The
|
|
619
680
|
// expensive model work is POST-lock (the lock is held by `performAdvance`).
|
|
681
|
+
//
|
|
682
|
+
// SHORT-CIRCUIT (primary/load-bearing half of task
|
|
683
|
+
// `surface-short-circuit-already-triaged-observations-and-harden-skill-empty-emit`):
|
|
684
|
+
// an OBSERVATION with provably no open judgement (frontmatter `needsAnswers` NOT
|
|
685
|
+
// true, no non-empty `## Open questions` section, no pending sidecar) — the
|
|
686
|
+
// typical shape of a decision-record / already-triaged note — deterministically
|
|
687
|
+
// yields `{questions: []}` WITHOUT round-tripping the flaky surface-questions
|
|
688
|
+
// agent (source observation
|
|
689
|
+
// `surface-questions-agent-still-emits-no-parseable-questions-on-decision-record-obs-2026-07-10`).
|
|
690
|
+
// The loud-error contract from
|
|
691
|
+
// `advance-surface-limbo-observation-loudly-instead-of-silent-no-op` is
|
|
692
|
+
// PRESERVED for the observations that DO reach the agent — this only spares the
|
|
693
|
+
// ones that provably have nothing to ask. Conservative: fires only on
|
|
694
|
+
// observations; tasks/specs still always ask.
|
|
620
695
|
const gate = context.surfaceGate ?? harnessSurfaceGate();
|
|
621
696
|
let emit;
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
item,
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
}
|
|
697
|
+
if (isNothingToSurfaceObservation(cwd, input, itemPath, sidecarExists)) {
|
|
698
|
+
note(
|
|
699
|
+
`surface ${item}: auto-triaged (no open questions, no sidecar) — skipped agent.`,
|
|
700
|
+
);
|
|
701
|
+
emit = {questions: []};
|
|
702
|
+
} else {
|
|
703
|
+
try {
|
|
704
|
+
emit = await gate({
|
|
705
|
+
item,
|
|
706
|
+
cwd,
|
|
707
|
+
surfaceModel: context.surfaceModel,
|
|
708
|
+
});
|
|
709
|
+
} catch (err) {
|
|
710
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
711
|
+
if (!hasBase) {
|
|
712
|
+
return {
|
|
713
|
+
exitCode: 1,
|
|
714
|
+
outcome: 'usage-error',
|
|
715
|
+
message: `surface ${item}: the surface-questions agent produced no usable emit (${detail}).`,
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
// The agent flaked but we have a deterministic base question to surface: the
|
|
719
|
+
// flake is NON-FATAL. Treat the agent's extras as empty and carry on (the
|
|
720
|
+
// base triage question still lands, so the human can triage via the sidecar).
|
|
721
|
+
note(
|
|
722
|
+
`surface ${item}: the surface-questions agent produced no usable emit ` +
|
|
723
|
+
`(${detail}); surfacing the deterministic question(s) only.`,
|
|
724
|
+
);
|
|
725
|
+
emit = {questions: []};
|
|
726
|
+
}
|
|
635
727
|
}
|
|
636
728
|
|
|
637
729
|
// 2. The ENGINE persists (the skill wrote nothing): append-or-create the sidecar
|
|
638
730
|
// + set `needsAnswers:true` in ONE commit (CAS-atomic under the held lock).
|
|
731
|
+
// Base questions FIRST (q1…), then the agent's additive extras.
|
|
639
732
|
const persist = context.surfacePersist ?? persistSurfacedQuestions;
|
|
640
733
|
const result = persist({
|
|
641
734
|
cwd,
|
|
642
735
|
item,
|
|
643
736
|
itemPath,
|
|
644
|
-
questions: toNewQuestions(emit),
|
|
737
|
+
questions: [...baseToAdd, ...toNewQuestions(emit)],
|
|
645
738
|
note,
|
|
646
739
|
});
|
|
647
740
|
if (result.outcome === 'nothing') {
|
|
648
|
-
//
|
|
649
|
-
//
|
|
650
|
-
//
|
|
651
|
-
//
|
|
652
|
-
//
|
|
653
|
-
// the promote path only from an answered `disposition: promote` SIDECAR
|
|
654
|
-
// (`triage-persist.ts`). Without either, the item is untriaged (re-enumerated
|
|
655
|
-
// every tick), un-surfaceable (nothing to ask), un-promotable (no sidecar) —
|
|
656
|
-
// a silent exit-0 no-op forever. Surface it LOUDLY instead.
|
|
657
|
-
const limbo = detectObservationLimbo(input);
|
|
658
|
-
if (limbo !== undefined) {
|
|
659
|
-
note(limbo);
|
|
660
|
-
return {exitCode: 1, outcome: 'usage-error', message: limbo};
|
|
661
|
-
}
|
|
741
|
+
// The agent had nothing to ask AND there was no base question to add (a
|
|
742
|
+
// task/spec surface, or a re-surface whose base question is already present).
|
|
743
|
+
// This is the calm "no open judgement" no-op — no sidecar written. (There is
|
|
744
|
+
// no "limbo" any more: the triage rung ALWAYS passes a base question on the
|
|
745
|
+
// first pass, so an untriaged observation can never fall here empty-handed.)
|
|
662
746
|
return {
|
|
663
747
|
exitCode: 0,
|
|
664
748
|
outcome: 'no-op',
|
|
@@ -675,40 +759,113 @@ async function surfaceRung(input: RungExecInput): Promise<RungExecResult> {
|
|
|
675
759
|
}
|
|
676
760
|
|
|
677
761
|
/**
|
|
678
|
-
*
|
|
679
|
-
*
|
|
680
|
-
*
|
|
681
|
-
*
|
|
762
|
+
* Would this OBSERVATION provably surface `{questions: []}` — i.e. is there no
|
|
763
|
+
* open judgement anywhere on it? Cheap read-only predicate the surface rung uses
|
|
764
|
+
* to skip the flaky agent round-trip for already-triaged / decision-record notes
|
|
765
|
+
* (task
|
|
766
|
+
* `surface-short-circuit-already-triaged-observations-and-harden-skill-empty-emit`;
|
|
767
|
+
* source observation
|
|
768
|
+
* `surface-questions-agent-still-emits-no-parseable-questions-on-decision-record-obs-2026-07-10`).
|
|
682
769
|
*
|
|
683
|
-
*
|
|
684
|
-
*
|
|
685
|
-
*
|
|
686
|
-
*
|
|
687
|
-
*
|
|
770
|
+
* Fires ONLY when ALL of these hold (conservative — err on the side of STILL
|
|
771
|
+
* calling the agent if uncertain):
|
|
772
|
+
* - the item's namespace is `observation` (tasks/specs are out of scope here —
|
|
773
|
+
* they carry no engine-owned base question and a false-positive there would
|
|
774
|
+
* silently drop a real question the author asked for);
|
|
775
|
+
* - the item body's frontmatter does NOT set `needsAnswers: true` (an author
|
|
776
|
+
* who set the flag is explicitly asking for the agent's pass);
|
|
777
|
+
* - the body carries no non-empty `## Open questions` section (empty /
|
|
778
|
+
* whitespace-only sections don't count as judgement);
|
|
779
|
+
* - there is no pending open-question sidecar for the item (same signal the
|
|
780
|
+
* classifier's invariant-1 read uses — the sidecar path is identity-derived).
|
|
688
781
|
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
691
|
-
*
|
|
692
|
-
* (the sidecar + `triaged:` frontmatter) keeps the loop honest.
|
|
782
|
+
* The decision-record shape (a `Decision (…)` line and/or `## Alternatives
|
|
783
|
+
* considered` section) is a HINT, not required — the four conditions above are
|
|
784
|
+
* load-bearing on their own.
|
|
693
785
|
*/
|
|
694
|
-
function
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
if (
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
786
|
+
function isNothingToSurfaceObservation(
|
|
787
|
+
cwd: string,
|
|
788
|
+
input: RungExecInput,
|
|
789
|
+
itemPath: string,
|
|
790
|
+
sidecarExists: boolean,
|
|
791
|
+
): boolean {
|
|
792
|
+
if (input.namespace !== 'observation') {
|
|
793
|
+
return false;
|
|
794
|
+
}
|
|
795
|
+
if (sidecarExists) {
|
|
796
|
+
return false;
|
|
797
|
+
}
|
|
798
|
+
let content: string;
|
|
799
|
+
try {
|
|
800
|
+
content = readFileSync(join(cwd, itemPath), 'utf8');
|
|
801
|
+
} catch {
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
804
|
+
const fm = parseFrontmatter(content);
|
|
805
|
+
if (fm.needsAnswers === true) {
|
|
806
|
+
return false;
|
|
807
|
+
}
|
|
808
|
+
if (hasNonEmptyOpenQuestionsSection(content)) {
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
return true;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Does the body carry a non-empty `## Open questions` section? Tolerates the
|
|
816
|
+
* `## Open questions to NOT guess` variant the capture-signal skill writes (same
|
|
817
|
+
* pattern the triage split uses). Whitespace-only / "none" markers count as
|
|
818
|
+
* empty.
|
|
819
|
+
*/
|
|
820
|
+
function hasNonEmptyOpenQuestionsSection(content: string): boolean {
|
|
821
|
+
const lines = content.replace(/\r\n/g, '\n').split('\n');
|
|
822
|
+
const startIdx = lines.findIndex((l) => /^##\s+Open questions\b/i.test(l));
|
|
823
|
+
if (startIdx === -1) {
|
|
824
|
+
return false;
|
|
825
|
+
}
|
|
826
|
+
let endIdx = lines.length;
|
|
827
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
828
|
+
if (/^##\s+/.test(lines[i])) {
|
|
829
|
+
endIdx = i;
|
|
830
|
+
break;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
const body = lines
|
|
834
|
+
.slice(startIdx + 1, endIdx)
|
|
835
|
+
.join('\n')
|
|
836
|
+
.trim();
|
|
837
|
+
if (body === '') {
|
|
838
|
+
return false;
|
|
839
|
+
}
|
|
840
|
+
// A single "none" / "n/a" marker line — treat as empty (author signalled "no
|
|
841
|
+
// open judgement" explicitly).
|
|
842
|
+
if (/^(?:none|n\/a|-|_+)\.?$/i.test(body)) {
|
|
843
|
+
return false;
|
|
844
|
+
}
|
|
845
|
+
return true;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* The DETERMINISTIC triage question the triage rung ALWAYS surfaces for an
|
|
850
|
+
* untriaged observation (the "no limbo, ever" contract). It is engine-built (NOT
|
|
851
|
+
* LLM output), so it can never be zeroed out or flake: every untriaged observation
|
|
852
|
+
* gets exactly this question, and the human answers it via the sidecar — a record,
|
|
853
|
+
* a rationale note, and a fresh-bug signal are all treated identically (the human
|
|
854
|
+
* decides the disposition). The `surface-questions` agent runs ADDITIVELY on top,
|
|
855
|
+
* adding any extra pointed questions it extracts from the body.
|
|
856
|
+
*/
|
|
857
|
+
export function buildTriageBaseQuestion(): NewQuestion {
|
|
858
|
+
return {
|
|
859
|
+
question:
|
|
860
|
+
'What should become of this observation? Reply with a disposition and a ' +
|
|
861
|
+
'reason: resolve (settle it, keep the note on record — say why), promote ' +
|
|
862
|
+
'(mint a task / spec / adr — say which and why), delete (redundant or ' +
|
|
863
|
+
'obsolete — say why), or duplicate (maps onto an existing item — name it).',
|
|
864
|
+
context:
|
|
865
|
+
'The engine records your disposition from the answer (no token needed); an ' +
|
|
866
|
+
'answered promote mints the artifact, resolve keeps the note settled, ' +
|
|
867
|
+
'delete/duplicate discharge it.',
|
|
868
|
+
};
|
|
712
869
|
}
|
|
713
870
|
|
|
714
871
|
/**
|
|
@@ -745,6 +902,28 @@ async function triageRung(input: RungExecInput): Promise<RungExecResult> {
|
|
|
745
902
|
const note = context.note ?? (() => {});
|
|
746
903
|
const cwd = context.cwd;
|
|
747
904
|
|
|
905
|
+
// SETTLED GUARD: a `triaged:` frontmatter marker means a human already
|
|
906
|
+
// dispositioned this observation, so it DROPS OUT of the triage pool. The pool
|
|
907
|
+
// enumeration already excludes it, but an EXPLICIT `obs:<slug>` bypasses the pool
|
|
908
|
+
// gate and still classifies as `triage-observation` — so re-check the marker here
|
|
909
|
+
// and no-op rather than surfacing a FRESH triage question on an already-settled
|
|
910
|
+
// note. (Replaces the old `detectObservationLimbo` special-case: there is no
|
|
911
|
+
// limbo any more — an UNtriaged observation always surfaces the deterministic
|
|
912
|
+
// question below; a SETTLED one is a calm no-op here.)
|
|
913
|
+
{
|
|
914
|
+
const itemRel = findItemPath(cwd, input.namespace, input.slug);
|
|
915
|
+
if (itemRel !== undefined) {
|
|
916
|
+
const fm = parseFrontmatter(readFileSync(join(cwd, itemRel), 'utf8'));
|
|
917
|
+
if (fm.triaged !== undefined && fm.triaged !== '') {
|
|
918
|
+
return {
|
|
919
|
+
exitCode: 0,
|
|
920
|
+
outcome: 'no-op',
|
|
921
|
+
message: `triage ${item}: already triaged (triaged:${fm.triaged}) — nothing to do.`,
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
748
927
|
// The CONSERVATIVE auto-disposition EXCEPTION — ONLY under `observationTriage:
|
|
749
928
|
// 'auto'`. Under `'ask'`/`'off'`/unset (including `off` + an EXPLICIT obs:<slug>
|
|
750
929
|
// that bypassed the selection gate), EVERY untriaged observation surfaces the
|
|
@@ -795,7 +974,12 @@ async function triageRung(input: RungExecInput): Promise<RungExecResult> {
|
|
|
795
974
|
// skill emits the triage question — NO disposition token any more, task
|
|
796
975
|
// `agentic-apply-retire-disposition-vocabulary`); the AGENTIC apply decision
|
|
797
976
|
// reads the human's answer + source and decides what to DO when it is answered.
|
|
798
|
-
|
|
977
|
+
//
|
|
978
|
+
// ALWAYS-ASK (the "no limbo" contract): pass the DETERMINISTIC triage question as
|
|
979
|
+
// the base question, so an untriaged observation ALWAYS surfaces it (q1) even if
|
|
980
|
+
// the `surface-questions` agent emits empty or flakes. The agent is ADDITIVE:
|
|
981
|
+
// any pointed questions it extracts from the body are appended after q1.
|
|
982
|
+
return surfaceRung(input, {baseQuestions: [buildTriageBaseQuestion()]});
|
|
799
983
|
}
|
|
800
984
|
|
|
801
985
|
/**
|
|
@@ -807,8 +991,9 @@ async function triageRung(input: RungExecInput): Promise<RungExecResult> {
|
|
|
807
991
|
* apply rung is now AGENT-DRIVEN: it runs the shared `decide(input, allowedOutcomes)`
|
|
808
992
|
* engine ({@link decide}) over `(the answered question(s) + the SOURCE item + its
|
|
809
993
|
* type/context)` via the injected {@link ApplyDecider}, allowing the set
|
|
810
|
-
* `{task |
|
|
811
|
-
*
|
|
994
|
+
* `{task | spec | adr | dispose | resolve | ask}` (= `{mint-task | mint-spec |
|
|
995
|
+
* mint-adr | dispose-source | resolve-no-mint | ask-follow-up}`; `adr` is now WIRED
|
|
996
|
+
* by task
|
|
812
997
|
* `agentic-apply-mint-adr-route`, which added the {@link mintAdr} route). The
|
|
813
998
|
* verdict ROUTES:
|
|
814
999
|
* - `ask` → the EXISTING append/re-pause loop ({@link applyAnsweredQuestions}
|
|
@@ -817,15 +1002,26 @@ async function triageRung(input: RungExecInput): Promise<RungExecResult> {
|
|
|
817
1002
|
* - `adr` → {@link mintAdr} (mint a SELF-CONTAINED ADR into `docs/adr/` + `git
|
|
818
1003
|
* rm` the source + sidecar in the SAME atomic commit; the SIBLING route for the
|
|
819
1004
|
* off-board target);
|
|
820
|
-
* - `task` / `
|
|
1005
|
+
* - `task` / `spec` → {@link promoteObservation} (mint a SELF-CONTAINED artifact +
|
|
821
1006
|
* `git rm` the source + sidecar in the SAME atomic commit); the artifact type
|
|
822
1007
|
* comes from the agent's VERDICT, NOT a human `promote-*` field;
|
|
823
|
-
* - `
|
|
824
|
-
*
|
|
1008
|
+
* - `resolve` → {@link applyAnsweredQuestions} resolve-fully (harvest answers
|
|
1009
|
+
* into the body, clear `needsAnswers`, delete the sidecar; the note is
|
|
1010
|
+
* RETAINED — the sibling of `dispose` that mints nothing but keeps the note);
|
|
1011
|
+
* - `dispose` → {@link applyAnsweredQuestions} regime-polymorphic disposal
|
|
1012
|
+
* (task `apply-disposition-delete-to-dispose-regime-polymorphic`, spec
|
|
1013
|
+
* `surface-stuck-as-questions-and-retire-stuck-lock-state` decision #5): an
|
|
1014
|
+
* OBSERVATION is `git rm`-ed in one revertible commit (notes leave by
|
|
1015
|
+
* deletion; reason in the message); a TASK is `git mv`-ed to
|
|
1016
|
+
* `tasks/cancelled/` (RETAINED, `reason:` written into the moved body); a
|
|
1017
|
+
* SPEC is `git mv`-ed to `specs/dropped/` (RETAINED). Making `dispose`
|
|
1018
|
+
* polymorphic (rather than a literal `delete`) makes "a task cannot be
|
|
1019
|
+
* hard-deleted by the apply rung, only disposed to its terminal" true BY
|
|
1020
|
+
* CONSTRUCTION.
|
|
825
1021
|
*
|
|
826
|
-
* For a TASK/
|
|
1022
|
+
* For a TASK/SPEC (answering its OWN open questions) or a caller-supplied follow-up
|
|
827
1023
|
* batch, it delegates straight to {@link applyAnsweredQuestions} (resolve fully /
|
|
828
|
-
* re-pause) — the lifecycle path is untouched (a task/
|
|
1024
|
+
* re-pause) — the lifecycle path is untouched (a task/spec is dropped by its own
|
|
829
1025
|
* lifecycle, not by a question answer).
|
|
830
1026
|
*
|
|
831
1027
|
* Under the `advancing` CAS lock (held by {@link performAdvance} BEFORE this runs
|
|
@@ -844,7 +1040,7 @@ async function applyRung(input: RungExecInput): Promise<RungExecResult> {
|
|
|
844
1040
|
return vanishedSkip({rung: 'apply', item});
|
|
845
1041
|
}
|
|
846
1042
|
|
|
847
|
-
// RUNNER-ACTION KIND-CHECK (task `apply-rung-merge-disposition`,
|
|
1043
|
+
// RUNNER-ACTION KIND-CHECK (task `apply-rung-merge-disposition`, spec
|
|
848
1044
|
// `land-time-reverify-and-parallel-merge-ceiling`): an answered `kind: merge`
|
|
849
1045
|
// sidecar entry is a DETERMINISTIC land action, NOT a content decision. It
|
|
850
1046
|
// dispatches HERE (a sibling of the agentic `decide()`), keyed off the
|
|
@@ -859,10 +1055,30 @@ async function applyRung(input: RungExecInput): Promise<RungExecResult> {
|
|
|
859
1055
|
return mergeRoute;
|
|
860
1056
|
}
|
|
861
1057
|
|
|
1058
|
+
// RUNNER-ACTION KIND-CHECK for `kind: 'stuck'` (task
|
|
1059
|
+
// `apply-resolve-reset-flag-discards-work-branch`, spec
|
|
1060
|
+
// `surface-stuck-as-questions-and-retire-stuck-lock-state` decision #6): a
|
|
1061
|
+
// bounced TASK surfaces as a `kind: 'stuck'` question; the human's plain
|
|
1062
|
+
// `keep | reset | cancel` answer drives one of the three deterministic
|
|
1063
|
+
// verbs. The DIRECT SIBLING of `maybeRunMergeAction` above (there is NO
|
|
1064
|
+
// agentic decider on the TASK apply path — `runAgenticDecision` fires only
|
|
1065
|
+
// for observations — so a bounced task's answer MUST be sourced from a
|
|
1066
|
+
// deterministic parse+dispatch, not a widened decider). `keep` and `reset`
|
|
1067
|
+
// fall through to the normal fall-through persist (with the branch
|
|
1068
|
+
// pre-deleted on `reset` via the SHARED
|
|
1069
|
+
// `deleteRemoteWorkBranchIfPresent`); `cancel` dispatches through
|
|
1070
|
+
// `applyAnsweredQuestions`' `dispose` option (task →
|
|
1071
|
+
// `tasks/cancelled/`); `refused` short-circuits with the sidecar left in
|
|
1072
|
+
// place for a re-answer.
|
|
1073
|
+
const stuckRoute = await maybeRunStuckAction(input, itemPath);
|
|
1074
|
+
if (stuckRoute !== undefined) {
|
|
1075
|
+
return stuckRoute;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
862
1078
|
// AGENTIC APPLY for an answered OBSERVATION (the subsumed triage rung): run the
|
|
863
1079
|
// shared decision engine over the answer(s) + source, route the verdict. A
|
|
864
1080
|
// caller-supplied follow-up batch (`applyFollowups`) bypasses the decision and
|
|
865
|
-
// re-pauses directly (a test/driver hook). TASK/
|
|
1081
|
+
// re-pauses directly (a test/driver hook). TASK/SPEC items resolve in place
|
|
866
1082
|
// (their own lifecycle), so they skip the decision and fall through to the
|
|
867
1083
|
// persist below.
|
|
868
1084
|
const runAgenticDecision =
|
|
@@ -910,7 +1126,7 @@ async function applyRung(input: RungExecInput): Promise<RungExecResult> {
|
|
|
910
1126
|
* the merge-question surfacer) through the EXISTING land primitive
|
|
911
1127
|
* (`performIntegration` with `committedRecovery: true` + `freshWorktreeGate:
|
|
912
1128
|
* true`) via the EXISTING per-job worktree seam (`workspace.ts` `createJob`).
|
|
913
|
-
* The deterministic SIBLING of the agentic `decide()` content-decision (
|
|
1129
|
+
* The deterministic SIBLING of the agentic `decide()` content-decision (SPEC
|
|
914
1130
|
* `land-time-reverify-and-parallel-merge-ceiling`, task
|
|
915
1131
|
* `apply-rung-merge-disposition`; Stories #15, #16): a merge-acceptance has no
|
|
916
1132
|
* judgement content (the human's plain `merge | hold | drop` answer IS the
|
|
@@ -919,7 +1135,7 @@ async function applyRung(input: RungExecInput): Promise<RungExecResult> {
|
|
|
919
1135
|
*
|
|
920
1136
|
* Returns `undefined` when there is no answered `kind: merge` entry to dispatch
|
|
921
1137
|
* (the apply rung then proceeds to the existing path — agentic for
|
|
922
|
-
* observations, normal `applyAnsweredQuestions` for task/
|
|
1138
|
+
* observations, normal `applyAnsweredQuestions` for task/spec content questions).
|
|
923
1139
|
* Returns a {@link RungExecResult} when the dispatcher handled the rung:
|
|
924
1140
|
*
|
|
925
1141
|
* - `landed` / `already-integrated` ⇒ the kept commit landed on `main` (or
|
|
@@ -995,8 +1211,13 @@ async function maybeRunMergeAction(
|
|
|
995
1211
|
// bounce to needs-attention through its own shared seam). SHORT-CIRCUIT:
|
|
996
1212
|
// leave the sidecar so the open answer stays surfaced — the apply rung
|
|
997
1213
|
// MUST NOT also resolve it (the next surfacer / human will follow up).
|
|
1214
|
+
// Outcome tag `merge-refused` (task `merge-action-nits-followup` nit 2):
|
|
1215
|
+
// distinct from `usage-error` — which is reserved for genuine caller-usage
|
|
1216
|
+
// errors (e.g. the workspacesDir-unset guard above) — so reviewers can
|
|
1217
|
+
// grep the refusal-on-rebased-tip signal without confusing it with a
|
|
1218
|
+
// misuse. `exitCode: 1` preserved.
|
|
998
1219
|
note(result.message);
|
|
999
|
-
return {exitCode: 1, outcome: '
|
|
1220
|
+
return {exitCode: 1, outcome: 'merge-refused', message: result.message};
|
|
1000
1221
|
}
|
|
1001
1222
|
|
|
1002
1223
|
if (result.outcome === 'restale') {
|
|
@@ -1064,6 +1285,113 @@ async function maybeRunMergeAction(
|
|
|
1064
1285
|
}
|
|
1065
1286
|
}
|
|
1066
1287
|
|
|
1288
|
+
/**
|
|
1289
|
+
* Dispatch an answered STUCK-QUESTION (a sidecar entry stamped `kind: 'stuck'`
|
|
1290
|
+
* by the bounce-surface path) through the deterministic
|
|
1291
|
+
* {@link performStuckAction} sibling. The DIRECT SIBLING of
|
|
1292
|
+
* {@link maybeRunMergeAction} for the `kind: 'stuck'` axis (task
|
|
1293
|
+
* `apply-resolve-reset-flag-discards-work-branch`, spec
|
|
1294
|
+
* `surface-stuck-as-questions-and-retire-stuck-lock-state` decision #6):
|
|
1295
|
+
*
|
|
1296
|
+
* - `keep` -> fall through to today's normal `applyAnsweredQuestions`
|
|
1297
|
+
* resolve (branch untouched; continue-from-WIP);
|
|
1298
|
+
* - `reset` -> the SHARED `deleteRemoteWorkBranchIfPresent` primitive
|
|
1299
|
+
* discards the remote `work/task-<slug>` FIRST, then the
|
|
1300
|
+
* normal fall-through persist clears `needsAnswers`. Safely
|
|
1301
|
+
* IDEMPOTENT when no branch exists (an item never built) —
|
|
1302
|
+
* an `already-gone` push is tolerated as a no-op;
|
|
1303
|
+
* - `cancel` -> fall through to the normal persist with the `dispose`
|
|
1304
|
+
* option (`git mv -> tasks/cancelled/`), the answer text
|
|
1305
|
+
* recorded as the human's reason;
|
|
1306
|
+
* - `refused` -> a REAL push-delete failure aborted the discard;
|
|
1307
|
+
* SHORT-CIRCUIT with the sidecar left in place so the human
|
|
1308
|
+
* sees the failure and re-answers (matches the
|
|
1309
|
+
* `requeue --reset` abort-on-failed-delete contract).
|
|
1310
|
+
*
|
|
1311
|
+
* Returns `undefined` when no answered `kind: 'stuck'` entry is present (the
|
|
1312
|
+
* apply rung then proceeds to the existing path — an unrelated content
|
|
1313
|
+
* question falls through to the normal `applyAnsweredQuestions` /
|
|
1314
|
+
* agentic-decider path).
|
|
1315
|
+
*/
|
|
1316
|
+
async function maybeRunStuckAction(
|
|
1317
|
+
input: RungExecInput,
|
|
1318
|
+
itemPath: string,
|
|
1319
|
+
): Promise<RungExecResult | undefined> {
|
|
1320
|
+
const {item, context} = input;
|
|
1321
|
+
const cwd = context.cwd;
|
|
1322
|
+
const note = context.note ?? (() => {});
|
|
1323
|
+
|
|
1324
|
+
const detected = detectAnsweredStuckAction(cwd, item);
|
|
1325
|
+
if (detected === undefined) return undefined;
|
|
1326
|
+
|
|
1327
|
+
const handler = context.stuckAction ?? performStuckAction;
|
|
1328
|
+
|
|
1329
|
+
let result: StuckActionResult;
|
|
1330
|
+
try {
|
|
1331
|
+
result = await handler({
|
|
1332
|
+
action: detected,
|
|
1333
|
+
item,
|
|
1334
|
+
slug: input.slug,
|
|
1335
|
+
cwd,
|
|
1336
|
+
arbiter: context.arbiter ?? DEFAULT_ARBITER,
|
|
1337
|
+
note,
|
|
1338
|
+
});
|
|
1339
|
+
} catch (err) {
|
|
1340
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1341
|
+
const message =
|
|
1342
|
+
`apply ${item}: answered stuck-question dispatch raised (${detail}); NOT ` +
|
|
1343
|
+
`clearing needsAnswers; the sidecar stays surfaced.`;
|
|
1344
|
+
note(message);
|
|
1345
|
+
return {exitCode: 1, outcome: 'usage-error', message};
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
if (result.outcome === 'refused') {
|
|
1349
|
+
// The arbiter delete FAILED (not `already-gone`); we MUST NOT clear
|
|
1350
|
+
// needsAnswers, because that would leave the item claimable while still
|
|
1351
|
+
// carrying the WIP branch we meant to discard — the very stale-continue
|
|
1352
|
+
// trap the `requeue --reset` path guards against. Leave the sidecar in
|
|
1353
|
+
// place for a re-answer.
|
|
1354
|
+
note(result.message);
|
|
1355
|
+
return {exitCode: 1, outcome: 'usage-error', message: result.message};
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
note(result.message);
|
|
1359
|
+
const apply = context.applyPersist ?? applyAnsweredQuestions;
|
|
1360
|
+
try {
|
|
1361
|
+
const applyOptions: ApplyAnsweredQuestionsOptions = {
|
|
1362
|
+
cwd,
|
|
1363
|
+
item,
|
|
1364
|
+
itemPath,
|
|
1365
|
+
note,
|
|
1366
|
+
};
|
|
1367
|
+
if (result.outcome === 'cancel') {
|
|
1368
|
+
// The human's answer text is the dispose reason (verbatim), so the
|
|
1369
|
+
// terminal `tasks/cancelled/` body records WHY — the same reason
|
|
1370
|
+
// contract the agentic `dispose` verdict uses.
|
|
1371
|
+
applyOptions.dispose = {reason: detected.entry.answer.trim()};
|
|
1372
|
+
}
|
|
1373
|
+
const applied = apply(applyOptions);
|
|
1374
|
+
const mapped: AdvanceOutcome =
|
|
1375
|
+
applied.outcome === 'repaused'
|
|
1376
|
+
? 'no-op'
|
|
1377
|
+
: applied.outcome === 'vanished'
|
|
1378
|
+
? 'vanished'
|
|
1379
|
+
: 'advanced';
|
|
1380
|
+
return {
|
|
1381
|
+
exitCode: 0,
|
|
1382
|
+
outcome: mapped,
|
|
1383
|
+
message: `${result.message} ${applied.message}`,
|
|
1384
|
+
};
|
|
1385
|
+
} catch (err) {
|
|
1386
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1387
|
+
return {
|
|
1388
|
+
exitCode: 1,
|
|
1389
|
+
outcome: 'usage-error',
|
|
1390
|
+
message: `apply ${item}: ${detail}`,
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1067
1395
|
/**
|
|
1068
1396
|
* The cross-tick-window BENIGN SKIP shared by all three rungs that need to
|
|
1069
1397
|
* resolve an item file (surface / triage / apply). The lifecycle pool enumerated
|
|
@@ -1113,21 +1441,30 @@ function findItemPath(
|
|
|
1113
1441
|
* The AGENTIC apply DECISION for a fully-answered OBSERVATION (task
|
|
1114
1442
|
* `agentic-apply-retire-disposition-vocabulary`): run the shared
|
|
1115
1443
|
* `decide(input, allowedOutcomes)` engine over the answer(s) + source, then ROUTE
|
|
1116
|
-
* the verdict. The artifact-type selection (task vs
|
|
1444
|
+
* the verdict. The artifact-type selection (task vs spec) comes from the agent's
|
|
1117
1445
|
* VERDICT, NOT a human `promote-*` field (which is retired). Replaces the old
|
|
1118
1446
|
* `answeredPromoteArtifact` + disposition picker.
|
|
1119
1447
|
*
|
|
1120
1448
|
* - `ask` → append the follow-up question(s) + re-pause (the EXISTING loop, via
|
|
1121
1449
|
* {@link applyAnsweredQuestions}'s `appendQuestions`);
|
|
1122
|
-
* - `task` / `
|
|
1450
|
+
* - `task` / `spec` → {@link promoteObservation} (mint self-contained + delete
|
|
1123
1451
|
* source in the same atomic commit);
|
|
1124
1452
|
* - `adr` → {@link mintAdr} (mint a self-contained ADR into `docs/adr/` + delete
|
|
1125
1453
|
* source in the same atomic commit; the SIBLING route for the off-board target,
|
|
1126
1454
|
* task `agentic-apply-mint-adr-route`);
|
|
1127
|
-
* - `
|
|
1128
|
-
*
|
|
1455
|
+
* - `resolve` → {@link applyAnsweredQuestions}'s resolve-fully path (harvest the
|
|
1456
|
+
* answers into `## Applied answers`, strip the open-questions block, clear
|
|
1457
|
+
* `needsAnswers`, DELETE the sidecar — the note is RETAINED). The sibling of
|
|
1458
|
+
* `dispose` that KEEPS the note instead of moving/rm-ing it (task
|
|
1459
|
+
* `apply-decide-resolve-verdict-mint-nothing`);
|
|
1460
|
+
* - `dispose` → {@link applyAnsweredQuestions}'s regime-polymorphic disposal
|
|
1461
|
+
* (task `apply-disposition-delete-to-dispose-regime-polymorphic`): an
|
|
1462
|
+
* observation is `git rm`-ed in one revertible commit (reason in the
|
|
1463
|
+
* message); a task is `git mv`-ed to `tasks/cancelled/` (reason written into
|
|
1464
|
+
* the body); a spec is `git mv`-ed to `specs/dropped/`.
|
|
1129
1465
|
*
|
|
1130
|
-
* The allowed set is `{task |
|
|
1466
|
+
* The allowed set is `{task | spec | adr | dispose | resolve | ask}`; a verdict
|
|
1467
|
+
* outside it is
|
|
1131
1468
|
* rejected by the engine's allowed-outcome guard ({@link DisallowedOutcomeError})
|
|
1132
1469
|
* and mapped onto a usage-error — never dispatched.
|
|
1133
1470
|
*/
|
|
@@ -1234,11 +1571,13 @@ async function applyAgenticDecision(
|
|
|
1234
1571
|
outcome:
|
|
1235
1572
|
result.outcome === 'promoted'
|
|
1236
1573
|
? 'advanced'
|
|
1237
|
-
: result.outcome === '
|
|
1238
|
-
? '
|
|
1239
|
-
: result.outcome === '
|
|
1240
|
-
? '
|
|
1241
|
-
: '
|
|
1574
|
+
: result.outcome === 'already-triaged'
|
|
1575
|
+
? 'already-triaged'
|
|
1576
|
+
: result.outcome === 'lost'
|
|
1577
|
+
? 'lost'
|
|
1578
|
+
: result.outcome === 'contended'
|
|
1579
|
+
? 'contended'
|
|
1580
|
+
: 'usage-error',
|
|
1242
1581
|
message: result.message,
|
|
1243
1582
|
};
|
|
1244
1583
|
} catch (err) {
|
|
@@ -1303,16 +1642,53 @@ async function applyAgenticDecision(
|
|
|
1303
1642
|
}
|
|
1304
1643
|
}
|
|
1305
1644
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1645
|
+
if (verdict.outcome === 'resolve') {
|
|
1646
|
+
// resolve-no-mint → the EXISTING resolve-fully path (task
|
|
1647
|
+
// `apply-decide-resolve-verdict-mint-nothing`). The answer SETTLES the item
|
|
1648
|
+
// with NOTHING to mint and the note RETAINED: call `applyAnsweredQuestions`
|
|
1649
|
+
// with NEITHER `appendQuestions` (re-pause) NOR `dispose` (drop/terminal), so it
|
|
1650
|
+
// takes the default resolve-fully branch — harvest the answers into `##
|
|
1651
|
+
// Applied answers`, strip the marker-fenced open-questions block, clear
|
|
1652
|
+
// `needsAnswers`, and DELETE the sidecar in ONE atomic commit. Invariant-clean
|
|
1653
|
+
// (`needsAnswers:false` ⟺ no active sidecar). The note file is KEPT (this is
|
|
1654
|
+
// the sibling of `dispose`, which git-rm's it or moves it to a terminal). `resolveReason` is advisory
|
|
1655
|
+
// context only; the durable disposition record is the harvested `## Applied
|
|
1656
|
+
// answers` block the resolve-fully path writes (NOT a separate convention).
|
|
1657
|
+
const apply = context.applyPersist ?? applyAnsweredQuestions;
|
|
1658
|
+
try {
|
|
1659
|
+
const result = apply({cwd, item, itemPath, note});
|
|
1660
|
+
return {
|
|
1661
|
+
exitCode: 0,
|
|
1662
|
+
outcome: result.outcome === 'vanished' ? 'vanished' : 'advanced',
|
|
1663
|
+
message: result.message,
|
|
1664
|
+
};
|
|
1665
|
+
} catch (err) {
|
|
1666
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1667
|
+
return {
|
|
1668
|
+
exitCode: 1,
|
|
1669
|
+
outcome: 'usage-error',
|
|
1670
|
+
message: `apply ${item}: ${detail}`,
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
// dispose-source → regime-polymorphic disposal (DIRECT, no confirm — decision
|
|
1676
|
+
// 12; task `apply-disposition-delete-to-dispose-regime-polymorphic`, spec
|
|
1677
|
+
// `surface-stuck-as-questions-and-retire-stuck-lock-state` decision #5). The
|
|
1678
|
+
// human's answer is the source of truth; the persist chooses the on-disk
|
|
1679
|
+
// effect by the source's regime: an OBSERVATION is `git rm`-ed in one
|
|
1680
|
+
// revertible commit (reason in the message, git history = archive); a TASK is
|
|
1681
|
+
// `git mv`-ed to `tasks/cancelled/` (RETAINED, `reason:` written into the
|
|
1682
|
+
// moved body); a SPEC is `git mv`-ed to `specs/dropped/` (RETAINED). A task
|
|
1683
|
+
// cannot be hard-deleted by the apply rung — dispose is the only path off the
|
|
1684
|
+
// board.
|
|
1309
1685
|
const apply = context.applyPersist ?? applyAnsweredQuestions;
|
|
1310
1686
|
try {
|
|
1311
1687
|
const result = apply({
|
|
1312
1688
|
cwd,
|
|
1313
1689
|
item,
|
|
1314
1690
|
itemPath,
|
|
1315
|
-
|
|
1691
|
+
dispose: {reason: verdict.disposeReason ?? ''},
|
|
1316
1692
|
note,
|
|
1317
1693
|
});
|
|
1318
1694
|
return {
|
|
@@ -1353,13 +1729,13 @@ export async function performAdvance(
|
|
|
1353
1729
|
const message =
|
|
1354
1730
|
'`advance` with no item is the eligible-SET form (the one-shot driver). ' +
|
|
1355
1731
|
'The single-item tick needs a named item: ' +
|
|
1356
|
-
'`advance <slug>` / `advance
|
|
1732
|
+
'`advance <slug>` / `advance spec:<slug>` / `advance obs:<slug>`.';
|
|
1357
1733
|
note(message);
|
|
1358
1734
|
return {exitCode: 1, outcome: 'usage-error', message};
|
|
1359
1735
|
}
|
|
1360
1736
|
|
|
1361
1737
|
// 1. RESOLVE the arg via the SHARED resolver (extended with `obs:`). `advance`
|
|
1362
|
-
// spans task /
|
|
1738
|
+
// spans task / spec / observation; a collision / bad arg is a loud usage error.
|
|
1363
1739
|
let resolved;
|
|
1364
1740
|
try {
|
|
1365
1741
|
resolved = resolveAdvanceArg({
|
|
@@ -1419,7 +1795,7 @@ export async function performAdvance(
|
|
|
1419
1795
|
// 3. LOCK — take the `advancing` CAS borrow for the classified rung, keyed on
|
|
1420
1796
|
// the item's `<type>-<slug>` identity. The expensive phase is POST-lock.
|
|
1421
1797
|
//
|
|
1422
|
-
// UNIFIED PER-ITEM LOCK, TREE-LESS RUNGS ONLY (
|
|
1798
|
+
// UNIFIED PER-ITEM LOCK, TREE-LESS RUNGS ONLY (spec
|
|
1423
1799
|
// `ledger-status-per-item-lock-refs` US #1/#3/#18; ADR
|
|
1424
1800
|
// `ledger-status-on-per-item-lock-refs`). The rung kind is KNOWN here
|
|
1425
1801
|
// (`classification.kind`, classified pre-lock), so the tree-less-only policy
|
|
@@ -1428,7 +1804,7 @@ export async function performAdvance(
|
|
|
1428
1804
|
// TREE-LESS rung (`surface`/`apply`/`triage-observation`) the advancing acquire
|
|
1429
1805
|
// ALSO takes the item's unified lock (`action: advance`) — these rungs have NO
|
|
1430
1806
|
// inner `do`, so the unified hold is what realises advance∥claim / advance∥task
|
|
1431
|
-
// exclusion. For a BUILD-TASK / TASK-
|
|
1807
|
+
// exclusion. For a BUILD-TASK / TASK-SPEC rung we do NOT take the unified lock
|
|
1432
1808
|
// at the advance layer: `performAdvance` ORCHESTRATES an inner `performDo` that
|
|
1433
1809
|
// ITSELF acquires the SAME `task-<slug>`/`prd-<slug>` ref (the create-only CAS
|
|
1434
1810
|
// with NO re-entrancy/auto-steal, per the ADR), so taking it here too would
|
|
@@ -1506,6 +1882,10 @@ export async function performAdvance(
|
|
|
1506
1882
|
verify: options.verify,
|
|
1507
1883
|
strictMergeApproval: options.strictMergeApproval,
|
|
1508
1884
|
mergeAction: options.mergeAction,
|
|
1885
|
+
// The answered-stuck-question dispatch context (task
|
|
1886
|
+
// `apply-resolve-reset-flag-discards-work-branch`): threaded into the
|
|
1887
|
+
// apply rung's kind-check for a `kind: 'stuck'` runner-action.
|
|
1888
|
+
stuckAction: options.stuckAction,
|
|
1509
1889
|
note,
|
|
1510
1890
|
},
|
|
1511
1891
|
});
|