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/drop-source.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {resolveSidecarIdentity, sidecarPathFor} from './sidecar.js';
|
|
|
5
5
|
import {resolveItemPathByIdentity} from './item-path.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* The DIRECT-DELETE helper (
|
|
8
|
+
* The DIRECT-DELETE helper (spec
|
|
9
9
|
* `agentic-question-resolution-retire-disposition-vocabulary`, US #5/#11; task
|
|
10
10
|
* `direct-delete-question-cli-helper`) — the "I just want to throw this away"
|
|
11
11
|
* path that `git rm`s a source item AND its question sidecar (when present) in
|
|
@@ -33,7 +33,7 @@ export interface DropSourceOptions {
|
|
|
33
33
|
/** Working clone/worktree the deletion commits in. */
|
|
34
34
|
cwd: string;
|
|
35
35
|
/**
|
|
36
|
-
* The namespaced item identity to delete (`task:foo` / `
|
|
36
|
+
* The namespaced item identity to delete (`task:foo` / `spec:bar` /
|
|
37
37
|
* `observation:baz` / `obs:baz` / a bare `<slug>` = task). Resolved to its
|
|
38
38
|
* on-disk path by identity, folder-agnostic.
|
|
39
39
|
*/
|
package/src/env-config.ts
CHANGED
|
@@ -65,7 +65,7 @@ const KEY_COERCIONS: {[K in keyof Config]?: Coercion} = {
|
|
|
65
65
|
// LOUDLY naming the variable + the valid options.
|
|
66
66
|
observationTriage: {enum: ['off', 'ask', 'auto']},
|
|
67
67
|
// The merge-question SURFACER gate is a 3-state ENUM coercion (mirrors
|
|
68
|
-
// `observationTriage`'s shape; SEPARATE axis with a HIGHER default,
|
|
68
|
+
// `observationTriage`'s shape; SEPARATE axis with a HIGHER default, spec
|
|
69
69
|
// `land-time-reverify-and-parallel-merge-ceiling` Story 17 / task
|
|
70
70
|
// `merge-questions-gate-axis`), so `DORFL_MERGE_QUESTIONS=off|ask|auto`
|
|
71
71
|
// works and a typo FAILS LOUDLY naming the variable + the valid options.
|
|
@@ -74,8 +74,8 @@ const KEY_COERCIONS: {[K in keyof Config]?: Coercion} = {
|
|
|
74
74
|
// `DORFL_SURFACE_BLOCKERS=true|false` works and a typo FAILS LOUDLY.
|
|
75
75
|
surfaceBlockers: 'boolean',
|
|
76
76
|
// `surfaceStaging` (the BOOLEAN gate-family member that widens the SURFACE
|
|
77
|
-
// candidate set to include STAGING — `tasks/backlog/` + `
|
|
78
|
-
// not only the agent pool;
|
|
77
|
+
// candidate set to include STAGING — `tasks/backlog/` + `specs/proposed/` —
|
|
78
|
+
// not only the agent pool; spec
|
|
79
79
|
// `staging-surface-and-apply-promote-safety` F2) coerces as a BOOLEAN like
|
|
80
80
|
// `autoBuild`/`surfaceBlockers`, so `DORFL_SURFACE_STAGING=true|false`
|
|
81
81
|
// works and a typo FAILS LOUDLY. Resolution chain identical to the other
|
|
@@ -98,14 +98,14 @@ const KEY_COERCIONS: {[K in keyof Config]?: Coercion} = {
|
|
|
98
98
|
// works and a typo FAILS LOUDLY. Unset ⇒ the tasking transition falls back to
|
|
99
99
|
// `integration` (the flat value). It NEVER touches the build transition or intake.
|
|
100
100
|
taskingIntegration: {enum: ['propose', 'merge']},
|
|
101
|
-
// `tasksLandIn` (the per-repo TASK-PLACEMENT default —
|
|
101
|
+
// `tasksLandIn` (the per-repo TASK-PLACEMENT default — spec
|
|
102
102
|
// `staging-pool-position-gate-and-trust-model` US #5) coerces as the
|
|
103
|
-
// `
|
|
103
|
+
// `backlog`/`ready` enum, so `DORFL_TASKS_LAND_IN=ready` works and a
|
|
104
104
|
// typo FAILS LOUDLY. Same
|
|
105
105
|
// precedence chain as `taskingIntegration` (flag > env > per-repo > global >
|
|
106
|
-
// built-in `
|
|
106
|
+
// built-in `backlog`); fed into the runner-deterministic placement
|
|
107
107
|
// resolver (`src/placement.ts`) as the configured-default rung.
|
|
108
|
-
tasksLandIn: {enum: ['
|
|
108
|
+
tasksLandIn: {enum: ['backlog', 'ready']},
|
|
109
109
|
// `specsLandIn` (the per-repo SPEC-PLACEMENT default — spec
|
|
110
110
|
// `staging-pool-position-gate-and-trust-model` US #2/#5) coerces as the
|
|
111
111
|
// `pre-proposed`/`ready` enum, so `DORFL_SPECS_LAND_IN=ready` works and a typo
|
|
@@ -113,7 +113,7 @@ const KEY_COERCIONS: {[K in keyof Config]?: Coercion} = {
|
|
|
113
113
|
// > global > built-in `pre-proposed`); fed into the shared placement resolver
|
|
114
114
|
// (`src/placement.ts`) as the configured-default rung for the spec lifecycle.
|
|
115
115
|
// The legacy `prdsLandIn` key / `DORFL_PRDS_LAND_IN` env are GONE after the
|
|
116
|
-
// prd → spec hard cutover (clean break).
|
|
116
|
+
// ''prd'' → `spec` hard cutover (clean break).
|
|
117
117
|
specsLandIn: {enum: ['pre-proposed', 'ready']},
|
|
118
118
|
// `noPR` (the PR-INTENT axis) is a BOOLEAN coercion (like `review`), so
|
|
119
119
|
// `DORFL_NO_PR=true|false` works and a typo FAILS LOUDLY. The removed
|
|
@@ -139,19 +139,27 @@ const KEY_COERCIONS: {[K in keyof Config]?: Coercion} = {
|
|
|
139
139
|
// `freshWorktreeGate` is a BOOLEAN coercion (like `taskerLoop`), so
|
|
140
140
|
// `DORFL_FRESH_WORKTREE_GATE=true|false` works and a typo FAILS LOUDLY.
|
|
141
141
|
freshWorktreeGate: 'boolean',
|
|
142
|
-
// `mergeRetries` (the cross-job merge serialiser's CAS-retry cap —
|
|
142
|
+
// `mergeRetries` (the cross-job merge serialiser's CAS-retry cap — spec
|
|
143
143
|
// `land-time-reverify-and-parallel-merge-ceiling` Story 5 / Applied Answer q1 (a))
|
|
144
144
|
// coerces as a NUMBER (like `reviewMaxRounds`), so `DORFL_MERGE_RETRIES=20`
|
|
145
145
|
// works and a typo FAILS LOUDLY. A wide-matrix CI raises it; the default
|
|
146
146
|
// (1000 — the C2 large liveness ceiling) stays in place when unset.
|
|
147
147
|
mergeRetries: 'number',
|
|
148
148
|
// `strictMergeApproval` (the OPT-IN strictness layered on the OQ6
|
|
149
|
-
// stale-approval default —
|
|
149
|
+
// stale-approval default — spec `land-time-reverify-and-parallel-merge-ceiling`
|
|
150
150
|
// sidecar OQ6 / task `strict-merge-approval-gate`) coerces as a BOOLEAN
|
|
151
151
|
// (like `freshWorktreeGate`), so `DORFL_STRICT_MERGE_APPROVAL=true|false`
|
|
152
152
|
// works and a typo FAILS LOUDLY. Default OFF; ON re-surfaces the
|
|
153
153
|
// merge-question on a merge-base change instead of auto-landing.
|
|
154
154
|
strictMergeApproval: 'boolean',
|
|
155
|
+
// The dorfl-INTERNAL agent deadline (minutes) — spec
|
|
156
|
+
// `graceful-pre-timeout-wip-checkpoint`. NUMBER coercion (like
|
|
157
|
+
// `reviewMaxRounds`); range validation is fail-loud in `validateDeadlineConfig`
|
|
158
|
+
// (not clamped here). Env is a per-machine source, so CI can override the
|
|
159
|
+
// per-repo default without a config edit: `DORFL_AGENT_DEADLINE_MINUTES=90`.
|
|
160
|
+
agentDeadlineMinutes: 'number',
|
|
161
|
+
checkpointHeadroomMinutes: 'number',
|
|
162
|
+
maxAutoCheckpoints: 'number',
|
|
155
163
|
// `promptGuidance` is a STRUCTURED (nested) namespace, so it has no scalar env
|
|
156
164
|
// var of its own — each MEMBER carries its own env var (the nested-key form
|
|
157
165
|
// `DORFL_PROMPT_GUIDANCE_<MEMBER>`), handled out-of-band in
|
|
@@ -163,7 +171,7 @@ const KEY_COERCIONS: {[K in keyof Config]?: Coercion} = {
|
|
|
163
171
|
/**
|
|
164
172
|
* The env vars for the nested members of the `promptGuidance` namespace. The
|
|
165
173
|
* key is the env var name (`DORFL_PROMPT_GUIDANCE_<MEMBER>`, the nested
|
|
166
|
-
* naming the
|
|
174
|
+
* naming the spec specifies); the value is the (boolean) coercion. Each member
|
|
167
175
|
* present in env contributes `{<member>: bool}` into a single
|
|
168
176
|
* `promptGuidance` partial — mergeConfig replaces the whole namespace per the
|
|
169
177
|
* layered precedence, which is correct because EVERY member is included (env
|
package/src/failure-cause.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* **The failure-CAUSE classifier** (task `failure-cause-classification-model-vs-
|
|
3
3
|
* git-vs-agent`).
|
|
4
4
|
*
|
|
5
|
-
* When a claimed item
|
|
5
|
+
* When a claimed item is marked stuck on its per-item lock (post lock-cutover —
|
|
6
|
+
* `state: stuck`, no `work/needs-attention/` folder write), the runner records WHY.
|
|
6
7
|
* Historically several DIFFERENT causes collapsed into one undifferentiated label
|
|
7
8
|
* ("agent failed"), even though the CAUSE drives the RECOVERY:
|
|
8
9
|
*
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
*/
|
|
50
51
|
export type FailureCause =
|
|
51
52
|
| 'transient-infra' // a harness-surfaced model/connection outage (post-retry), or a git/provider outage — RETRY the same work
|
|
53
|
+
| 'needs-reauth' // a credential expired / was revoked (e.g. OAuth refresh token) — RETRY CANNOT help; a human must RE-AUTH
|
|
52
54
|
| 'config-error' // a thrown CORE wiring/config error (e.g. review on, no reviewGate) — fix the WIRING
|
|
53
55
|
| 'agent-failed'; // the conservative generic: the agent ran but produced bad/empty output, OR the cause is UNKNOWN
|
|
54
56
|
|
|
@@ -65,6 +67,24 @@ const CONFIG_ERROR_SIGNATURES = [
|
|
|
65
67
|
/no review gate is configured/i,
|
|
66
68
|
];
|
|
67
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Substrings that mark a CREDENTIAL-EXPIRY failure surfaced by the harness: an
|
|
72
|
+
* OAuth refresh token has expired or been revoked, or a 401 came back tagged
|
|
73
|
+
* `authentication_required`. Retry cannot help these — only a human RE-AUTH can.
|
|
74
|
+
* Kept SEPARATE from `TRANSIENT_INFRA_SIGNATURES` (see ADR
|
|
75
|
+
* `transient-infra-and-needs-reauth-routing`) so downstream routing can branch
|
|
76
|
+
* cleanly on the cause: retry-with-backoff for infra, straight to a needs-reauth
|
|
77
|
+
* surface for credentials. Conservative: unrelated 401s that don't mention auth /
|
|
78
|
+
* token stay `agent-failed`.
|
|
79
|
+
*/
|
|
80
|
+
const NEEDS_REAUTH_SIGNATURES = [
|
|
81
|
+
/authentication_required/i,
|
|
82
|
+
/OAuth (?:refresh )?token (?:expired|revoked|invalid)/i,
|
|
83
|
+
/refresh token (?:expired|revoked|invalid)/i,
|
|
84
|
+
/\b401\b[^\n]{0,80}(?:auth|token|credential|unauthori[sz]ed)/i,
|
|
85
|
+
/(?:auth|token|credential)[^\n]{0,40}\b401\b/i,
|
|
86
|
+
];
|
|
87
|
+
|
|
68
88
|
/**
|
|
69
89
|
* Substrings that mark a TRANSIENT-INFRA failure: a model endpoint / connection
|
|
70
90
|
* outage the harness surfaced AFTER exhausting its own retries, or a git/provider
|
|
@@ -117,6 +137,13 @@ export function classifyFailureCause(detail: string | undefined): FailureCause {
|
|
|
117
137
|
if (CONFIG_ERROR_SIGNATURES.some((re) => re.test(text))) {
|
|
118
138
|
return 'config-error';
|
|
119
139
|
}
|
|
140
|
+
// Credential-expiry is checked BEFORE `transient-infra` because a 401 body may
|
|
141
|
+
// also mention words like "unavailable"/network, and no amount of retry helps
|
|
142
|
+
// — a human must re-auth. Kept as its own cause per ADR
|
|
143
|
+
// `transient-infra-and-needs-reauth-routing`.
|
|
144
|
+
if (NEEDS_REAUTH_SIGNATURES.some((re) => re.test(text))) {
|
|
145
|
+
return 'needs-reauth';
|
|
146
|
+
}
|
|
120
147
|
if (TRANSIENT_INFRA_SIGNATURES.some((re) => re.test(text))) {
|
|
121
148
|
return 'transient-infra';
|
|
122
149
|
}
|
|
@@ -134,6 +161,8 @@ export function failureCauseLabel(cause: FailureCause): string {
|
|
|
134
161
|
switch (cause) {
|
|
135
162
|
case 'transient-infra':
|
|
136
163
|
return 'transient infra';
|
|
164
|
+
case 'needs-reauth':
|
|
165
|
+
return 'needs re-auth (credential expired)';
|
|
137
166
|
case 'config-error':
|
|
138
167
|
return 'config error';
|
|
139
168
|
case 'agent-failed':
|
package/src/format.ts
CHANGED
|
@@ -117,40 +117,20 @@ function cwdDivergenceLine(
|
|
|
117
117
|
* consistency rule): the two reads have different freshness + storage models.
|
|
118
118
|
*/
|
|
119
119
|
/**
|
|
120
|
-
* Render ONE held per-item lock entry
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
120
|
+
* Render ONE held per-item lock entry as an indented line, the SHARED renderer
|
|
121
|
+
* for the in-flight lock surface on BOTH the registry view (`formatStatus`) and
|
|
122
|
+
* the cwd section (`formatCwdSection`). Post-`retire-stuck-lock-state` the
|
|
123
|
+
* lock state is degenerate (`active` only) — a held lock is always an
|
|
124
|
+
* in-progress hold; a parked item is a `needsAnswers:true` pool item on `main`
|
|
125
|
+
* with a sidecar, rendered by the pool categoriser (NOT here).
|
|
126
126
|
*/
|
|
127
127
|
export function formatLockEntryLines(
|
|
128
128
|
entry: LockEntry,
|
|
129
129
|
indent: string,
|
|
130
130
|
): string[] {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
`${indent}${entry.entry} (${entry.action}/${entry.state} = ${view})`,
|
|
135
|
-
);
|
|
136
|
-
if (entry.state === 'stuck') {
|
|
137
|
-
const reason =
|
|
138
|
-
entry.reason && entry.reason !== ''
|
|
139
|
-
? entry.reason
|
|
140
|
-
: '(no reason recorded)';
|
|
141
|
-
const reasonLines = reason.split('\n');
|
|
142
|
-
lines.push(`${indent} reason: ${reasonLines[0]}`);
|
|
143
|
-
for (const extra of reasonLines.slice(1)) {
|
|
144
|
-
lines.push(`${indent} ${extra}`);
|
|
145
|
-
}
|
|
146
|
-
if (entry.questions && entry.questions.length > 0) {
|
|
147
|
-
lines.push(`${indent} questions:`);
|
|
148
|
-
for (const q of entry.questions) {
|
|
149
|
-
lines.push(`${indent} - ${q}`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return lines;
|
|
131
|
+
return [
|
|
132
|
+
`${indent}${entry.entry} (${entry.action}/${entry.state} = in-progress)`,
|
|
133
|
+
];
|
|
154
134
|
}
|
|
155
135
|
|
|
156
136
|
export function formatCwdSection(section: CwdSection): string[] {
|
|
@@ -202,7 +182,7 @@ export function formatCwdSection(section: CwdSection): string[] {
|
|
|
202
182
|
}
|
|
203
183
|
}
|
|
204
184
|
|
|
205
|
-
// The PER-ITEM LOCK in-flight SURFACE for the cwd (
|
|
185
|
+
// The PER-ITEM LOCK in-flight SURFACE for the cwd (spec
|
|
206
186
|
// `ledger-status-per-item-lock-refs` US #8): held (`active` = in-progress) and
|
|
207
187
|
// `stuck` (= needs-attention) lock entries + their reasons. This is what keeps a
|
|
208
188
|
// held/stuck cwd item from vanishing: the held-slug subtraction removes it from
|
|
@@ -214,7 +194,7 @@ export function formatCwdSection(section: CwdSection): string[] {
|
|
|
214
194
|
const lockHeld = section.repo.lockHeld ?? [];
|
|
215
195
|
if (lockHeld.length > 0) {
|
|
216
196
|
lines.push('');
|
|
217
|
-
lines.push(' In progress
|
|
197
|
+
lines.push(' In progress (lock held — refs/dorfl/lock/*):');
|
|
218
198
|
for (const entry of lockHeld) {
|
|
219
199
|
lines.push(...formatLockEntryLines(entry, ' '));
|
|
220
200
|
}
|
|
@@ -263,7 +243,7 @@ export function formatReport(report: ScanReport, cwd?: CwdSection): string {
|
|
|
263
243
|
lines.push('No participating repos found.');
|
|
264
244
|
lines.push(
|
|
265
245
|
'(A repo participates iff its work/ tree has lifecycle content in any ' +
|
|
266
|
-
'pool: tasks/ready, tasks/backlog,
|
|
246
|
+
'pool: tasks/ready, tasks/backlog, specs/proposed, specs/ready, ' +
|
|
267
247
|
'notes/observations, or questions.)',
|
|
268
248
|
);
|
|
269
249
|
} else {
|
|
@@ -281,7 +261,7 @@ export function formatReport(report: ScanReport, cwd?: CwdSection): string {
|
|
|
281
261
|
const groups = categoriseItems(repo.items);
|
|
282
262
|
allGroups.push(groups);
|
|
283
263
|
lines.push(...formatRepo(repo.path, groups));
|
|
284
|
-
// The one-slug-one-folder LINT (
|
|
264
|
+
// The one-slug-one-folder LINT (spec `ledger-integrity` story 3): WARN LOUDLY
|
|
285
265
|
// when any slug in this repo's ledger resides in >1 status folder (a corrupt
|
|
286
266
|
// ledger — never a silent pass). Clean ledgers add no lines.
|
|
287
267
|
const warnings = formatDuplicateWarnings(repo.ledgerDuplicates ?? []);
|
package/src/frontmatter.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* **Origin-trust PROVENANCE** (task `untrusted-origin-forces-build-propose`).
|
|
10
|
-
* How a
|
|
11
|
-
* signal SURVIVES the
|
|
10
|
+
* How a spec/task was BORN and the AUTHOR-TRUST verdict at birth, stamped so the
|
|
11
|
+
* signal SURVIVES the spec/task merge boundary (a landed-on-`main` artifact would
|
|
12
12
|
* otherwise erase how it was created — the laundering gap).
|
|
13
13
|
*
|
|
14
14
|
* - `origin` — `human` (the default / unset: a human authored it locally; a local
|
|
@@ -47,11 +47,12 @@ export interface Frontmatter {
|
|
|
47
47
|
* Source SPEC slug: the parent-spec pointer this artifact derives from; the
|
|
48
48
|
* spec lives at `work/specs/ready/<spec>.md`. The `spec` vocabulary name (spec
|
|
49
49
|
* `prd-to-spec-vocabulary-cutover-and-migration-command`). `parseFrontmatter`
|
|
50
|
-
* populates it from
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
50
|
+
* populates it from the canonical `spec:` key ONLY — the HARD CUTOVER (the last
|
|
51
|
+
* ''prd'' back-compat surface removed): the legacy ''prd:'' KEY read is GONE, so
|
|
52
|
+
* an un-migrated ''prd:'' field no longer silently resolves. A downstream repo
|
|
53
|
+
* migrates its data with `dorfl prd-to-spec` (a purely TEXTUAL `prd: → spec:`
|
|
54
|
+
* rewrite that does NOT go through this parser), after which the field reads
|
|
55
|
+
* canonically. There is no `fm.prd` FIELD.
|
|
55
56
|
*/
|
|
56
57
|
spec: string | undefined;
|
|
57
58
|
/**
|
|
@@ -105,19 +106,19 @@ export interface Frontmatter {
|
|
|
105
106
|
/** Slugs this item is blocked by; `[]` when omitted or empty. */
|
|
106
107
|
blockedBy: string[];
|
|
107
108
|
/**
|
|
108
|
-
*
|
|
109
|
-
* tasked (resolved against `work/
|
|
109
|
+
* Spec-only: slugs of prds that must already be TASKED before this spec may be
|
|
110
|
+
* tasked (resolved against `work/specs/tasked/` residence, NOT `done/`). `[]` when
|
|
110
111
|
* omitted or empty. Parsed here so the auto-tasker can read it; enforcement
|
|
111
112
|
* lives in the `auto-slice` capability, not in eligibility.
|
|
112
113
|
*/
|
|
113
114
|
taskedAfter: string[];
|
|
114
115
|
/**
|
|
115
116
|
* The per-item override layer for the `promptGuidance` NUDGE namespace
|
|
116
|
-
* (task `prompt-guidance-testfirst-item-override`,
|
|
117
|
-
*
|
|
117
|
+
* (task `prompt-guidance-testfirst-item-override`, spec US #5). A task or
|
|
118
|
+
* spec may carry one or more `promptGuidance.<member>: true | false`
|
|
118
119
|
* frontmatter lines to OVERRIDE the resolved repo policy for THAT ITEM only.
|
|
119
120
|
* Each member is `undefined` when omitted (⇒ inherit the next layer down in
|
|
120
|
-
* the precedence chain: per-task > per-
|
|
121
|
+
* the precedence chain: per-task > per-spec > repo-resolved). The override
|
|
121
122
|
* is honoured at the prompt-assembly seam (`prompt.ts`); it never affects
|
|
122
123
|
* the `verify` gate.
|
|
123
124
|
*
|
|
@@ -291,14 +292,44 @@ export function setFrontmatterMarker(
|
|
|
291
292
|
return lines.join('\n');
|
|
292
293
|
}
|
|
293
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Read ONE arbitrary top-level scalar frontmatter key by name, returning its
|
|
297
|
+
* unquoted value (or `undefined` when the doc has no fence, or the key is absent).
|
|
298
|
+
*
|
|
299
|
+
* A general-purpose companion to {@link parseFrontmatter} for keys that are NOT in
|
|
300
|
+
* the modelled {@link Frontmatter} shape — e.g. the observation→task provenance
|
|
301
|
+
* back-reference `promotedFrom:` the triage-promote path stamps and the create-CAS
|
|
302
|
+
* lost-race disambiguation reads (task
|
|
303
|
+
* `observation-triage-already-triaged-benign-skip`). Only top-level `key: value`
|
|
304
|
+
* lines are matched (block-list / nested values are out of scope for a scalar read).
|
|
305
|
+
*/
|
|
306
|
+
export function readFrontmatterField(
|
|
307
|
+
content: string,
|
|
308
|
+
key: string,
|
|
309
|
+
): string | undefined {
|
|
310
|
+
const block = extractBlock(content);
|
|
311
|
+
if (block === undefined) {
|
|
312
|
+
return undefined;
|
|
313
|
+
}
|
|
314
|
+
const pattern = new RegExp(`^${key}\\s*:\\s*(.*)$`);
|
|
315
|
+
for (const line of block.split('\n')) {
|
|
316
|
+
const match = pattern.exec(line);
|
|
317
|
+
if (match) {
|
|
318
|
+
const value = unquote(match[1]);
|
|
319
|
+
return value === '' ? undefined : value;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return undefined;
|
|
323
|
+
}
|
|
324
|
+
|
|
294
325
|
/**
|
|
295
326
|
* PROPAGATE the origin-trust PROVENANCE (`origin` + `originTrust`) from a SOURCE
|
|
296
|
-
* artifact (a
|
|
327
|
+
* artifact (a spec) onto a TARGET artifact's content (an emitted task), returning
|
|
297
328
|
* the new content (task `untrusted-origin-forces-build-propose`). The tasker
|
|
298
|
-
* calls this so a task born from an untrusted-origin
|
|
329
|
+
* calls this so a task born from an untrusted-origin spec carries the stamp the
|
|
299
330
|
* BUILD transition reads. IDEMPOTENT: each present source field is written via
|
|
300
331
|
* {@link setFrontmatterMarker} (replace-or-append). When the source has NO `origin`
|
|
301
|
-
* (a human/local-authored
|
|
332
|
+
* (a human/local-authored spec ⇒ trusted), the target is returned UNCHANGED — the
|
|
302
333
|
* normal path is never stamped (zero behaviour change). When a stamp IS applied to
|
|
303
334
|
* a fence-less target, {@link setFrontmatterMarker} now prepends a fence to carry
|
|
304
335
|
* it (tasks the tasker emits always have a fence, so this is the defensive path).
|
|
@@ -370,27 +401,16 @@ export function parseFrontmatter(content: string): Frontmatter {
|
|
|
370
401
|
result.originTrust = v === 'trusted' || v === 'untrusted' ? v : undefined;
|
|
371
402
|
} else if (key === 'slug') {
|
|
372
403
|
result.slug = rawValue === '' ? undefined : unquote(rawValue);
|
|
373
|
-
} else if (key === 'spec'
|
|
374
|
-
// The parent-spec pointer, read from
|
|
375
|
-
//
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
// NOT clobber a non-empty value already read from the other.
|
|
382
|
-
const value = rawValue === '' ? undefined : unquote(rawValue);
|
|
383
|
-
if (key === 'spec') {
|
|
384
|
-
// The canonical key wins: overwrite whatever `prd:` may have set.
|
|
385
|
-
if (value !== undefined) {
|
|
386
|
-
result.spec = value;
|
|
387
|
-
}
|
|
388
|
-
} else if (result.spec === undefined) {
|
|
389
|
-
// Legacy `prd:` only fills in when `spec:` has not already set it.
|
|
390
|
-
result.spec = value;
|
|
391
|
-
}
|
|
404
|
+
} else if (key === 'spec') {
|
|
405
|
+
// The parent-spec pointer, read from the canonical `spec:` key ONLY into the
|
|
406
|
+
// `fm.spec` field (spec `prd-to-spec-vocabulary-cutover-and-migration-command`,
|
|
407
|
+
// HARD CUTOVER). The legacy ''prd:'' KEY read is GONE — an un-migrated ''prd:''
|
|
408
|
+
// field no longer resolves here; a repo converts its data via the TEXTUAL
|
|
409
|
+
// `dorfl prd-to-spec` rewrite (which does not use this parser). An empty value
|
|
410
|
+
// leaves the field undefined.
|
|
411
|
+
result.spec = rawValue === '' ? undefined : unquote(rawValue);
|
|
392
412
|
} else if (key === 'issue') {
|
|
393
|
-
// Integer issue link (`intake`'s
|
|
413
|
+
// Integer issue link (`intake`'s spec-emit OR a lone-task emit). A
|
|
394
414
|
// non-integer / empty value reads as undefined (the field is absent rather
|
|
395
415
|
// than malformed).
|
|
396
416
|
const n = Number(unquote(rawValue));
|
package/src/gc.ts
CHANGED
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
removeJobRecord,
|
|
9
9
|
type JobRecord,
|
|
10
10
|
} from './workspace.js';
|
|
11
|
-
import {workBranchRef} from './slug-namespace.js';
|
|
11
|
+
import {parseWorkBranchRef, workBranchRef} from './slug-namespace.js';
|
|
12
|
+
import {terminalMainPaths} from './item-lock.js';
|
|
13
|
+
import type {SidecarType} from './sidecar.js';
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* The **reaper** for job worktrees, governed by the **provably-safe deletion
|
|
@@ -140,8 +142,23 @@ export function evaluateDeletionSafety(
|
|
|
140
142
|
|
|
141
143
|
const localTip = revParse(input.dir, 'HEAD', env);
|
|
142
144
|
|
|
143
|
-
// 2a. Merged: the tip is
|
|
144
|
-
|
|
145
|
+
// 2a. Merged: the tip is provably on <arbiter>/main — either a plain
|
|
146
|
+
// ancestor (fast path) OR squash-aware-equivalent (the durable done/
|
|
147
|
+
// dropped record is on main AND the branch carries nothing main lacks).
|
|
148
|
+
const arbiterMainRef = `refs/remotes/${arbiter}/main`;
|
|
149
|
+
const reapIdentity = reapIdentityFromBranch(input.branch);
|
|
150
|
+
if (
|
|
151
|
+
reapIdentity !== undefined
|
|
152
|
+
? isProvablyMergedForReap({
|
|
153
|
+
cwd: input.dir,
|
|
154
|
+
tip: localTip,
|
|
155
|
+
arbiterMain: arbiterMainRef,
|
|
156
|
+
namespace: reapIdentity.namespace,
|
|
157
|
+
slug: reapIdentity.slug,
|
|
158
|
+
env,
|
|
159
|
+
})
|
|
160
|
+
: isAncestor(input.dir, localTip, arbiterMainRef, env)
|
|
161
|
+
) {
|
|
145
162
|
return {safe: true, reachableVia: 'merged'};
|
|
146
163
|
}
|
|
147
164
|
|
|
@@ -432,14 +449,160 @@ function fetchTracking(
|
|
|
432
449
|
);
|
|
433
450
|
}
|
|
434
451
|
|
|
452
|
+
/**
|
|
453
|
+
* Extract the `{namespace, slug}` identity a reap decision needs from a work
|
|
454
|
+
* branch ref (`work/[<producer>-]<type>-<slug>`). Returns `undefined` for a
|
|
455
|
+
* non-namespaced branch — the caller then falls back to the ancestry-only
|
|
456
|
+
* fast-path (the squash-aware fallback has no anchor without an identity to
|
|
457
|
+
* look up on `<arbiter>/main`, so the safe direction is the strict old rule).
|
|
458
|
+
*/
|
|
459
|
+
function reapIdentityFromBranch(
|
|
460
|
+
branch: string,
|
|
461
|
+
): {namespace: SidecarType; slug: string} | undefined {
|
|
462
|
+
const parsed = parseWorkBranchRef(branch);
|
|
463
|
+
if (parsed === undefined) {
|
|
464
|
+
return undefined;
|
|
465
|
+
}
|
|
466
|
+
// `SlugNamespace` and `SidecarType` share the same three members
|
|
467
|
+
// (`'task' | 'spec' | 'observation'`); the cast is structural.
|
|
468
|
+
return {namespace: parsed.namespace as SidecarType, slug: parsed.slug};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/** Input for {@link isProvablyMergedForReap}. */
|
|
472
|
+
export interface ProvablyMergedForReapInput {
|
|
473
|
+
/** The local repo the git predicates run in. */
|
|
474
|
+
cwd: string;
|
|
475
|
+
/** The candidate branch tip (a 40-hex sha or a resolvable ref). */
|
|
476
|
+
tip: string;
|
|
477
|
+
/**
|
|
478
|
+
* The arbiter's main as a ref RESOLVABLE INSIDE `cwd` — a fetched
|
|
479
|
+
* remote-tracking ref (`refs/remotes/<arbiter>/main`) or short-form
|
|
480
|
+
* (`<arbiter>/main`). Callers are responsible for the fetch (best-effort;
|
|
481
|
+
* an unresolvable ref reads as not-provable ⇒ false).
|
|
482
|
+
*/
|
|
483
|
+
arbiterMain: string;
|
|
484
|
+
/**
|
|
485
|
+
* The item's type (task/spec/observation) — used to look up the durable
|
|
486
|
+
* terminal records on `<arbiter>/main`. Reap decisions on a
|
|
487
|
+
* `work/<type>-<slug>` branch pass the parsed type from
|
|
488
|
+
* {@link parseWorkBranchRef}.
|
|
489
|
+
*/
|
|
490
|
+
namespace: SidecarType;
|
|
491
|
+
/** The item's bare slug — the anchor for the squash-aware fallback. */
|
|
492
|
+
slug: string;
|
|
493
|
+
env?: NodeJS.ProcessEnv;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* The **squash-aware, provably-merged predicate** used to decide reap-safety
|
|
498
|
+
* across the workspace: `gc.ts`'s worktree reaper, `reap-branches.ts`'s remote
|
|
499
|
+
* `work/*` sweep, `integrator.ts`'s post-merge remote-head reap, and
|
|
500
|
+
* `complete.ts`'s local-branch reap. ONE decision point so the two rules can
|
|
501
|
+
* never drift apart, and NEVER `--force` — a merged-for-reap ref needs none.
|
|
502
|
+
*
|
|
503
|
+
* A remote/local `work/<slug>` branch is reapable iff EITHER:
|
|
504
|
+
*
|
|
505
|
+
* 1. **Fast path (unchanged).** `git merge-base --is-ancestor <tip>
|
|
506
|
+
* <arbiter>/main` succeeds — the fast-forward / true-merge land case.
|
|
507
|
+
* 2. **Squash-aware fallback (new).** BOTH of the following hold:
|
|
508
|
+
* - the item is TERMINAL on `<arbiter>/main` — i.e. the matching
|
|
509
|
+
* {@link terminalMainPaths} (`work/tasks/done/<slug>.md` or
|
|
510
|
+
* `work/tasks/cancelled/<slug>.md` for a task; `work/specs/tasked/…`
|
|
511
|
+
* or `work/specs/dropped/…` for a spec) exists in the fetched main
|
|
512
|
+
* tree; AND
|
|
513
|
+
* - the branch carries NOTHING main lacks — either `git diff --quiet
|
|
514
|
+
* <arbiter>/main <tip>` reads clean (content-equal / content-subset)
|
|
515
|
+
* OR `git cherry <arbiter>/main <tip>` reports every commit as
|
|
516
|
+
* already applied by patch-id (`-` on every line, catching
|
|
517
|
+
* rebase-lands the tree check misses).
|
|
518
|
+
*
|
|
519
|
+
* Grounding: the durable `<arbiter>/main` record is authoritative. It is what
|
|
520
|
+
* `complete` writes FIRST, so if the terminal record is there the item's work
|
|
521
|
+
* is at rest, and a branch that carries nothing main lacks has no in-flight
|
|
522
|
+
* work to protect. The safety floor is unchanged: NEVER reap an in-flight /
|
|
523
|
+
* unmerged branch (no terminal record ⇒ retain), NEVER reap a branch carrying
|
|
524
|
+
* commits main lacks (content check fails ⇒ retain), NEVER `--force`.
|
|
525
|
+
*
|
|
526
|
+
* This is the branch-side twin of the lock-side terminal-on-main recovery
|
|
527
|
+
* (`isTerminalOnMain` in `item-lock.ts`): same anchor, different subject.
|
|
528
|
+
*/
|
|
529
|
+
export function isProvablyMergedForReap(
|
|
530
|
+
input: ProvablyMergedForReapInput,
|
|
531
|
+
): boolean {
|
|
532
|
+
const {cwd, tip, arbiterMain, namespace, slug, env} = input;
|
|
533
|
+
|
|
534
|
+
// 1. Fast path — the fast-forward / true-merge land case. Cheap: one
|
|
535
|
+
// `git merge-base --is-ancestor`.
|
|
536
|
+
if (isAncestor(cwd, tip, arbiterMain, env)) {
|
|
537
|
+
return true;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// 2. Squash-aware fallback. Anchor: the durable terminal record on
|
|
541
|
+
// `<arbiter>/main`. No terminal record ⇒ the item is still in-flight (or
|
|
542
|
+
// an observation, which has no terminal folder) ⇒ retain (never reap
|
|
543
|
+
// in-flight work).
|
|
544
|
+
if (!terminalRecordOnMain(cwd, arbiterMain, namespace, slug, env)) {
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// 2a. Content-subset: `git diff --quiet <arbiterMain> <tip>` clean ⇒ the
|
|
549
|
+
// two trees are identical, so the branch carries nothing main lacks
|
|
550
|
+
// (the incident shape: the branch is a since-lagged snapshot of a
|
|
551
|
+
// squash-landed item, no new content on it).
|
|
552
|
+
const diff = run('git', ['diff', '--quiet', arbiterMain, tip], cwd, {env});
|
|
553
|
+
if (diff.status === 0) {
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// 2b. Patch-id equivalence via `git cherry <upstream> <head>`: every commit
|
|
558
|
+
// reachable from `<tip>` but not `<arbiterMain>` is reported as `-`
|
|
559
|
+
// (already applied upstream by patch-id). This catches rebase-lands
|
|
560
|
+
// where the tree diff is non-empty but every commit is already on main
|
|
561
|
+
// under a different sha.
|
|
562
|
+
const cherry = run('git', ['cherry', arbiterMain, tip], cwd, {env});
|
|
563
|
+
if (cherry.status === 0) {
|
|
564
|
+
const lines = cherry.stdout
|
|
565
|
+
.split('\n')
|
|
566
|
+
.map((l) => l.trim())
|
|
567
|
+
.filter((l) => l !== '');
|
|
568
|
+
if (lines.length > 0 && lines.every((l) => l.startsWith('- '))) {
|
|
569
|
+
return true;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/** True iff `<arbiterMain>` carries ANY of the item's durable terminal records
|
|
577
|
+
* ({@link terminalMainPaths}). Best-effort: `cat-file -e` is a cheap tree
|
|
578
|
+
* probe; an unresolvable `<arbiterMain>` reads as no-record ⇒ retain (the safe
|
|
579
|
+
* direction). Observations have no terminal folder ⇒ always false. */
|
|
580
|
+
function terminalRecordOnMain(
|
|
581
|
+
cwd: string,
|
|
582
|
+
arbiterMain: string,
|
|
583
|
+
namespace: SidecarType,
|
|
584
|
+
slug: string,
|
|
585
|
+
env: NodeJS.ProcessEnv | undefined,
|
|
586
|
+
): boolean {
|
|
587
|
+
for (const path of terminalMainPaths(namespace, slug)) {
|
|
588
|
+
const res = run('git', ['cat-file', '-e', `${arbiterMain}:${path}`], cwd, {
|
|
589
|
+
env,
|
|
590
|
+
});
|
|
591
|
+
if (res.status === 0) {
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
|
|
435
598
|
/**
|
|
436
599
|
* True iff `ancestor` is an ancestor of (or equal to) `descendant`, both
|
|
437
|
-
* resolved IN the local repo `dir` (`git merge-base --is-ancestor`).
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
600
|
+
* resolved IN the local repo `dir` (`git merge-base --is-ancestor`). The
|
|
601
|
+
* low-level fast-path building block for {@link isProvablyMergedForReap} —
|
|
602
|
+
* reap-safety decisions across the workspace go through the squash-aware
|
|
603
|
+
* wrapper, never this raw predicate. EXPORTED so callers that legitimately
|
|
604
|
+
* need pure ancestry (non-reap idempotency checks, claim-reachability guards)
|
|
605
|
+
* keep the one shared implementation.
|
|
443
606
|
*/
|
|
444
607
|
export function isAncestor(
|
|
445
608
|
dir: string,
|