dorfl 0.14.2 → 0.15.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 +17 -16
- package/dist/advance-ci-template.d.ts.map +1 -1
- package/dist/advance-ci-template.js +42 -53
- package/dist/advance-ci-template.js.map +1 -1
- package/dist/advance-drivers.d.ts +8 -0
- package/dist/advance-drivers.d.ts.map +1 -1
- package/dist/advance-drivers.js +28 -4
- package/dist/advance-drivers.js.map +1 -1
- package/dist/advance-isolated.d.ts.map +1 -1
- package/dist/advance-isolated.js +3 -2
- package/dist/advance-isolated.js.map +1 -1
- package/dist/advance-lifecycle-template.d.ts +41 -40
- package/dist/advance-lifecycle-template.d.ts.map +1 -1
- package/dist/advance-lifecycle-template.js +309 -489
- package/dist/advance-lifecycle-template.js.map +1 -1
- package/dist/advance-loop-driver.d.ts.map +1 -1
- package/dist/advance-loop-driver.js +3 -2
- package/dist/advance-loop-driver.js.map +1 -1
- package/dist/advance-treeless-publish.d.ts +31 -0
- package/dist/advance-treeless-publish.d.ts.map +1 -1
- package/dist/advance-treeless-publish.js +40 -0
- package/dist/advance-treeless-publish.js.map +1 -1
- package/dist/advance.d.ts +63 -3
- package/dist/advance.d.ts.map +1 -1
- package/dist/advance.js +80 -12
- package/dist/advance.js.map +1 -1
- package/dist/advancing-lock.d.ts +9 -0
- package/dist/advancing-lock.d.ts.map +1 -1
- package/dist/advancing-lock.js +21 -1
- package/dist/advancing-lock.js.map +1 -1
- package/dist/agent-env.d.ts +61 -0
- package/dist/agent-env.d.ts.map +1 -0
- package/dist/agent-env.js +144 -0
- package/dist/agent-env.js.map +1 -0
- package/dist/agent-stop.d.ts +35 -0
- package/dist/agent-stop.d.ts.map +1 -1
- package/dist/agent-stop.js +62 -0
- package/dist/agent-stop.js.map +1 -1
- package/dist/apply-merge-action.d.ts +76 -34
- package/dist/apply-merge-action.d.ts.map +1 -1
- package/dist/apply-merge-action.js +339 -129
- package/dist/apply-merge-action.js.map +1 -1
- package/dist/apply-stuck-action.js +2 -2
- package/dist/apply-stuck-action.js.map +1 -1
- package/dist/ci-agent-result.d.ts +151 -0
- package/dist/ci-agent-result.d.ts.map +1 -0
- package/dist/ci-agent-result.js +314 -0
- package/dist/ci-agent-result.js.map +1 -0
- package/dist/ci-handoff-apply.d.ts +162 -0
- package/dist/ci-handoff-apply.d.ts.map +1 -0
- package/dist/ci-handoff-apply.js +497 -0
- package/dist/ci-handoff-apply.js.map +1 -0
- package/dist/ci-handoff-format.d.ts +369 -0
- package/dist/ci-handoff-format.d.ts.map +1 -0
- package/dist/ci-handoff-format.js +646 -0
- package/dist/ci-handoff-format.js.map +1 -0
- package/dist/ci-handoff-lfs.d.ts +146 -0
- package/dist/ci-handoff-lfs.d.ts.map +1 -0
- package/dist/ci-handoff-lfs.js +397 -0
- package/dist/ci-handoff-lfs.js.map +1 -0
- package/dist/ci-handoff.d.ts +99 -0
- package/dist/ci-handoff.d.ts.map +1 -0
- package/dist/ci-handoff.js +277 -0
- package/dist/ci-handoff.js.map +1 -0
- package/dist/ci-lock-outputs.d.ts +93 -0
- package/dist/ci-lock-outputs.d.ts.map +1 -0
- package/dist/ci-lock-outputs.js +223 -0
- package/dist/ci-lock-outputs.js.map +1 -0
- package/dist/ci-phase-build.d.ts +178 -0
- package/dist/ci-phase-build.d.ts.map +1 -0
- package/dist/ci-phase-build.js +989 -0
- package/dist/ci-phase-build.js.map +1 -0
- package/dist/ci-phase-driver.d.ts +247 -0
- package/dist/ci-phase-driver.d.ts.map +1 -0
- package/dist/ci-phase-driver.js +370 -0
- package/dist/ci-phase-driver.js.map +1 -0
- package/dist/ci-phase-intake.d.ts +172 -0
- package/dist/ci-phase-intake.d.ts.map +1 -0
- package/dist/ci-phase-intake.js +555 -0
- package/dist/ci-phase-intake.js.map +1 -0
- package/dist/ci-phase-tasking.d.ts +205 -0
- package/dist/ci-phase-tasking.d.ts.map +1 -0
- package/dist/ci-phase-tasking.js +906 -0
- package/dist/ci-phase-tasking.js.map +1 -0
- package/dist/ci-phase-treeless.d.ts +224 -0
- package/dist/ci-phase-treeless.d.ts.map +1 -0
- package/dist/ci-phase-treeless.js +1482 -0
- package/dist/ci-phase-treeless.js.map +1 -0
- package/dist/ci-read-token.d.ts +47 -0
- package/dist/ci-read-token.d.ts.map +1 -0
- package/dist/ci-read-token.js +80 -0
- package/dist/ci-read-token.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +447 -22
- package/dist/cli.js.map +1 -1
- package/dist/close-job-template.d.ts +10 -1
- package/dist/close-job-template.d.ts.map +1 -1
- package/dist/close-job-template.js +46 -14
- package/dist/close-job-template.js.map +1 -1
- package/dist/close-job.d.ts +20 -5
- package/dist/close-job.d.ts.map +1 -1
- package/dist/close-job.js +65 -12
- package/dist/close-job.js.map +1 -1
- package/dist/complete.d.ts.map +1 -1
- package/dist/complete.js +20 -1
- package/dist/complete.js.map +1 -1
- package/dist/config.d.ts +41 -44
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -4
- package/dist/config.js.map +1 -1
- package/dist/continue-branch.d.ts +63 -15
- package/dist/continue-branch.d.ts.map +1 -1
- package/dist/continue-branch.js +75 -8
- package/dist/continue-branch.js.map +1 -1
- package/dist/crashed-run-save.d.ts +73 -0
- package/dist/crashed-run-save.d.ts.map +1 -0
- package/dist/crashed-run-save.js +86 -0
- package/dist/crashed-run-save.js.map +1 -0
- package/dist/cwd-section.d.ts.map +1 -1
- package/dist/cwd-section.js +10 -2
- package/dist/cwd-section.js.map +1 -1
- package/dist/decision-engine.d.ts +10 -0
- package/dist/decision-engine.d.ts.map +1 -1
- package/dist/decision-engine.js.map +1 -1
- package/dist/do-config.d.ts +2 -24
- package/dist/do-config.d.ts.map +1 -1
- package/dist/do-config.js +1 -33
- package/dist/do-config.js.map +1 -1
- package/dist/do.d.ts +149 -1
- package/dist/do.d.ts.map +1 -1
- package/dist/do.js +115 -25
- package/dist/do.js.map +1 -1
- package/dist/dorfl-item-template.d.ts +98 -0
- package/dist/dorfl-item-template.d.ts.map +1 -0
- package/dist/dorfl-item-template.js +504 -0
- package/dist/dorfl-item-template.js.map +1 -0
- package/dist/env-config.d.ts.map +1 -1
- package/dist/env-config.js +7 -5
- package/dist/env-config.js.map +1 -1
- package/dist/frontmatter.d.ts +52 -0
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +136 -2
- package/dist/frontmatter.js.map +1 -1
- package/dist/gc.d.ts +13 -0
- package/dist/gc.d.ts.map +1 -1
- package/dist/gc.js +16 -2
- package/dist/gc.js.map +1 -1
- package/dist/git.d.ts +18 -1
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +70 -5
- package/dist/git.js.map +1 -1
- package/dist/harness.d.ts +44 -5
- package/dist/harness.d.ts.map +1 -1
- package/dist/harness.js +24 -1
- package/dist/harness.js.map +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/install-ci-action-pins.d.ts +123 -0
- package/dist/install-ci-action-pins.d.ts.map +1 -0
- package/dist/install-ci-action-pins.js +190 -0
- package/dist/install-ci-action-pins.js.map +1 -0
- package/dist/install-ci-actions-pr-creation.d.ts +70 -0
- package/dist/install-ci-actions-pr-creation.d.ts.map +1 -0
- package/dist/install-ci-actions-pr-creation.js +106 -0
- package/dist/install-ci-actions-pr-creation.js.map +1 -0
- package/dist/install-ci-capabilities/dorfl-item.d.ts +12 -0
- package/dist/install-ci-capabilities/dorfl-item.d.ts.map +1 -0
- package/dist/install-ci-capabilities/dorfl-item.js +26 -0
- package/dist/install-ci-capabilities/dorfl-item.js.map +1 -0
- package/dist/install-ci-core.d.ts +155 -37
- package/dist/install-ci-core.d.ts.map +1 -1
- package/dist/install-ci-core.js +304 -200
- package/dist/install-ci-core.js.map +1 -1
- package/dist/install-ci-github.d.ts +47 -2
- package/dist/install-ci-github.d.ts.map +1 -1
- package/dist/install-ci-github.js +113 -1
- package/dist/install-ci-github.js.map +1 -1
- package/dist/install-ci.d.ts +25 -6
- package/dist/install-ci.d.ts.map +1 -1
- package/dist/install-ci.js +72 -36
- package/dist/install-ci.js.map +1 -1
- package/dist/intake-marker.d.ts +6 -1
- package/dist/intake-marker.d.ts.map +1 -1
- package/dist/intake-marker.js.map +1 -1
- package/dist/intake-triage.d.ts.map +1 -1
- package/dist/intake-triage.js +6 -2
- package/dist/intake-triage.js.map +1 -1
- package/dist/intake-trigger-template.d.ts +31 -25
- package/dist/intake-trigger-template.d.ts.map +1 -1
- package/dist/intake-trigger-template.js +107 -292
- package/dist/intake-trigger-template.js.map +1 -1
- package/dist/intake.d.ts +186 -4
- package/dist/intake.d.ts.map +1 -1
- package/dist/intake.js +533 -416
- package/dist/intake.js.map +1 -1
- package/dist/integration-core.d.ts +154 -1
- package/dist/integration-core.d.ts.map +1 -1
- package/dist/integration-core.js +759 -486
- package/dist/integration-core.js.map +1 -1
- package/dist/integrator.d.ts +12 -0
- package/dist/integrator.d.ts.map +1 -1
- package/dist/integrator.js +21 -0
- package/dist/integrator.js.map +1 -1
- package/dist/isolation.d.ts +12 -2
- package/dist/isolation.d.ts.map +1 -1
- package/dist/isolation.js +19 -8
- package/dist/isolation.js.map +1 -1
- package/dist/item-lock.d.ts +93 -11
- package/dist/item-lock.d.ts.map +1 -1
- package/dist/item-lock.js +233 -65
- package/dist/item-lock.js.map +1 -1
- package/dist/land-question-residue.d.ts +58 -0
- package/dist/land-question-residue.d.ts.map +1 -0
- package/dist/land-question-residue.js +95 -0
- package/dist/land-question-residue.js.map +1 -0
- package/dist/lifecycle-gather.d.ts +16 -0
- package/dist/lifecycle-gather.d.ts.map +1 -1
- package/dist/lifecycle-gather.js +54 -6
- package/dist/lifecycle-gather.js.map +1 -1
- package/dist/merge-answer.d.ts +41 -0
- package/dist/merge-answer.d.ts.map +1 -0
- package/dist/merge-answer.js +60 -0
- package/dist/merge-answer.js.map +1 -0
- package/dist/merge-question-surfacer.d.ts +105 -30
- package/dist/merge-question-surfacer.d.ts.map +1 -1
- package/dist/merge-question-surfacer.js +202 -91
- package/dist/merge-question-surfacer.js.map +1 -1
- package/dist/merge-question-tick.d.ts +64 -0
- package/dist/merge-question-tick.d.ts.map +1 -0
- package/dist/merge-question-tick.js +96 -0
- package/dist/merge-question-tick.js.map +1 -0
- package/dist/mint-adr.d.ts.map +1 -1
- package/dist/mint-adr.js +20 -2
- package/dist/mint-adr.js.map +1 -1
- package/dist/needs-attention.d.ts +72 -2
- package/dist/needs-attention.d.ts.map +1 -1
- package/dist/needs-attention.js +275 -38
- package/dist/needs-attention.js.map +1 -1
- package/dist/phase-recorder.d.ts +168 -0
- package/dist/phase-recorder.d.ts.map +1 -0
- package/dist/phase-recorder.js +218 -0
- package/dist/phase-recorder.js.map +1 -0
- package/dist/phase.d.ts +94 -0
- package/dist/phase.d.ts.map +1 -0
- package/dist/phase.js +148 -0
- package/dist/phase.js.map +1 -0
- package/dist/pi-harness.d.ts.map +1 -1
- package/dist/pi-harness.js +19 -6
- package/dist/pi-harness.js.map +1 -1
- package/dist/prepare.d.ts.map +1 -1
- package/dist/prepare.js +4 -0
- package/dist/prepare.js.map +1 -1
- package/dist/prompt.d.ts +4 -0
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +48 -1
- package/dist/prompt.js.map +1 -1
- package/dist/protocol/CLAIM-PROTOCOL.md +3 -1
- package/dist/protocol/WORK-CONTRACT.md +1 -1
- package/dist/reap-branches.d.ts +48 -0
- package/dist/reap-branches.d.ts.map +1 -1
- package/dist/reap-branches.js +64 -0
- package/dist/reap-branches.js.map +1 -1
- package/dist/ref-write.d.ts +198 -0
- package/dist/ref-write.d.ts.map +1 -0
- package/dist/ref-write.js +79 -0
- package/dist/ref-write.js.map +1 -0
- package/dist/repo-config.js +8 -8
- package/dist/repo-config.js.map +1 -1
- package/dist/repo-mirror.d.ts +36 -0
- package/dist/repo-mirror.d.ts.map +1 -1
- package/dist/repo-mirror.js +91 -1
- package/dist/repo-mirror.js.map +1 -1
- package/dist/review-verdict.d.ts +1 -1
- package/dist/review-verdict.js +1 -1
- package/dist/run.d.ts +7 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +28 -4
- package/dist/run.js.map +1 -1
- package/dist/scan.d.ts +19 -1
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +21 -8
- package/dist/scan.js.map +1 -1
- package/dist/sidecar.d.ts +14 -0
- package/dist/sidecar.d.ts.map +1 -1
- package/dist/sidecar.js +21 -0
- package/dist/sidecar.js.map +1 -1
- package/dist/single-job-warning.d.ts +63 -0
- package/dist/single-job-warning.d.ts.map +1 -0
- package/dist/single-job-warning.js +110 -0
- package/dist/single-job-warning.js.map +1 -0
- package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +3 -1
- package/dist/skills/setup/protocol/WORK-CONTRACT.md +1 -1
- package/dist/spec-complete.d.ts +13 -6
- package/dist/spec-complete.d.ts.map +1 -1
- package/dist/spec-complete.js +23 -11
- package/dist/spec-complete.js.map +1 -1
- package/dist/start.d.ts.map +1 -1
- package/dist/start.js +47 -14
- package/dist/start.js.map +1 -1
- package/dist/status.d.ts +12 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +11 -3
- package/dist/status.js.map +1 -1
- package/dist/tasking.d.ts +246 -1
- package/dist/tasking.d.ts.map +1 -1
- package/dist/tasking.js +452 -358
- package/dist/tasking.js.map +1 -1
- package/dist/triage-persist.d.ts.map +1 -1
- package/dist/triage-persist.js +21 -2
- package/dist/triage-persist.js.map +1 -1
- package/dist/verify-workflow-template.d.ts.map +1 -1
- package/dist/verify-workflow-template.js +8 -4
- package/dist/verify-workflow-template.js.map +1 -1
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +4 -0
- package/dist/verify.js.map +1 -1
- package/dist/watch-session.d.ts +42 -13
- package/dist/watch-session.d.ts.map +1 -1
- package/dist/watch-session.js +52 -23
- package/dist/watch-session.js.map +1 -1
- package/dist/work-layout.d.ts +10 -4
- package/dist/work-layout.d.ts.map +1 -1
- package/dist/work-layout.js +11 -3
- package/dist/work-layout.js.map +1 -1
- package/dist/workspace.d.ts +65 -4
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +159 -11
- package/dist/workspace.js.map +1 -1
- package/package.json +1 -1
- package/src/advance-ci-template.ts +80 -81
- package/src/advance-drivers.ts +45 -6
- package/src/advance-isolated.ts +3 -5
- package/src/advance-lifecycle-template.ts +375 -538
- package/src/advance-loop-driver.ts +3 -5
- package/src/advance-treeless-publish.ts +61 -0
- package/src/advance.ts +145 -15
- package/src/advancing-lock.ts +36 -1
- package/src/agent-env.ts +174 -0
- package/src/agent-stop.ts +81 -0
- package/src/apply-merge-action.ts +401 -154
- package/src/apply-stuck-action.ts +2 -2
- package/src/ci-agent-result.ts +421 -0
- package/src/ci-handoff-apply.ts +737 -0
- package/src/ci-handoff-format.ts +926 -0
- package/src/ci-handoff-lfs.ts +519 -0
- package/src/ci-handoff.ts +392 -0
- package/src/ci-lock-outputs.ts +273 -0
- package/src/ci-phase-build.ts +1328 -0
- package/src/ci-phase-driver.ts +548 -0
- package/src/ci-phase-intake.ts +861 -0
- package/src/ci-phase-tasking.ts +1255 -0
- package/src/ci-phase-treeless.ts +2028 -0
- package/src/ci-read-token.ts +90 -0
- package/src/cli.ts +591 -29
- package/src/close-job-template.ts +52 -15
- package/src/close-job.ts +77 -12
- package/src/complete.ts +19 -1
- package/src/config.ts +43 -48
- package/src/continue-branch.ts +121 -16
- package/src/crashed-run-save.ts +162 -0
- package/src/cwd-section.ts +10 -0
- package/src/decision-engine.ts +10 -0
- package/src/do-config.ts +2 -57
- package/src/do.ts +170 -28
- package/src/dorfl-item-template.ts +556 -0
- package/src/env-config.ts +7 -5
- package/src/frontmatter.ts +155 -2
- package/src/gc.ts +27 -2
- package/src/git.ts +83 -5
- package/src/harness.ts +58 -6
- package/src/index.ts +50 -1
- package/src/install-ci-action-pins.ts +209 -0
- package/src/install-ci-actions-pr-creation.ts +186 -0
- package/src/install-ci-capabilities/dorfl-item.ts +33 -0
- package/src/install-ci-core.ts +388 -233
- package/src/install-ci-github.ts +149 -1
- package/src/install-ci.ts +83 -37
- package/src/intake-marker.ts +6 -1
- package/src/intake-triage.ts +7 -3
- package/src/intake-trigger-template.ts +126 -327
- package/src/intake.ts +725 -544
- package/src/integration-core.ts +975 -523
- package/src/integrator.ts +28 -0
- package/src/isolation.ts +29 -10
- package/src/item-lock.ts +308 -92
- package/src/land-question-residue.ts +122 -0
- package/src/lifecycle-gather.ts +82 -10
- package/src/merge-answer.ts +82 -0
- package/src/merge-question-surfacer.ts +320 -112
- package/src/merge-question-tick.ts +182 -0
- package/src/mint-adr.ts +28 -2
- package/src/needs-attention.ts +350 -42
- package/src/phase-recorder.ts +350 -0
- package/src/phase.ts +177 -0
- package/src/pi-harness.ts +25 -5
- package/src/prepare.ts +4 -0
- package/src/prompt.ts +68 -1
- package/src/reap-branches.ts +107 -0
- package/src/ref-write.ts +324 -0
- package/src/repo-config.ts +8 -8
- package/src/repo-mirror.ts +107 -1
- package/src/review-verdict.ts +1 -1
- package/src/run.ts +47 -6
- package/src/scan.ts +37 -7
- package/src/sidecar.ts +36 -0
- package/src/single-job-warning.ts +135 -0
- package/src/spec-complete.ts +36 -14
- package/src/start.ts +55 -16
- package/src/status.ts +29 -3
- package/src/tasking.ts +593 -378
- package/src/triage-persist.ts +24 -2
- package/src/verify-workflow-template.ts +8 -4
- package/src/verify.ts +4 -0
- package/src/watch-session.ts +86 -28
- package/src/work-layout.ts +11 -3
- package/src/workspace.ts +210 -14
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {existsSync, readFileSync, rmSync, writeFileSync} from 'node:fs';
|
|
2
|
+
import {join} from 'node:path';
|
|
3
|
+
import {parseFrontmatter, setNeedsAnswersMarker} from './frontmatter.js';
|
|
4
|
+
import {isEntryAnswered, parseSidecar, sidecarPathFor} from './sidecar.js';
|
|
5
|
+
import {workItemPath, workItemRel} from './work-layout.js';
|
|
6
|
+
|
|
7
|
+
/** What {@link clearOwnQuestionResidueOnLand} did to the working tree. */
|
|
8
|
+
export interface LandQuestionResidueResult {
|
|
9
|
+
/**
|
|
10
|
+
* Repo-relative paths this pass CHANGED in the working tree (the done record
|
|
11
|
+
* whose `needsAnswers` it cleared, the sidecar it deleted). Empty ⇒ the tree is
|
|
12
|
+
* byte-for-byte untouched (the normal land).
|
|
13
|
+
*/
|
|
14
|
+
changed: string[];
|
|
15
|
+
/** True when the done record's `needsAnswers: true` was rewritten to `false`. */
|
|
16
|
+
unflagged: boolean;
|
|
17
|
+
/** True when the item's own unanswered question sidecar was deleted. */
|
|
18
|
+
drainedSidecar: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* True when the item's sidecar carries a human ANSWER (or could not be parsed)
|
|
21
|
+
* and was therefore left alone, together with the flag.
|
|
22
|
+
*/
|
|
23
|
+
held: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* CLEAR a landing task's OWN stale question state in the WORKING TREE, so it
|
|
28
|
+
* rides the land's completion commit instead of surviving onto `main` (task
|
|
29
|
+
* `a-recovered-task-lands-without-stale-question-state`).
|
|
30
|
+
*
|
|
31
|
+
* THE DEFECT. A task surfaced to needs-attention (`surfaceStuckToNeedsAttention`
|
|
32
|
+
* writes `needsAnswers: true` on the body AND `work/questions/task-<slug>.md` in
|
|
33
|
+
* one commit on `main`) and then RECOVERED (a re-dispatch, a requeue continue, a
|
|
34
|
+
* stranded-branch finish) carried both halves straight through its done-move:
|
|
35
|
+
* the `git mv ... → tasks/done/` kept the flag, and nothing deleted the sidecar.
|
|
36
|
+
* The claim-time reconcile (`reconcileTerminalQuestionResidue`) drained them only
|
|
37
|
+
* on the NEXT claim, so the ledger was wrong in between, and stayed wrong when no
|
|
38
|
+
* claim followed.
|
|
39
|
+
*
|
|
40
|
+
* WHY THE LAND and not a later sweep: the done-move is the moment the residue
|
|
41
|
+
* becomes residue, and the land already owns a commit that touches exactly this
|
|
42
|
+
* item. Folding the clean-up into it leaves no window at all.
|
|
43
|
+
*
|
|
44
|
+
* SAME SEMANTICS as the claim-time drain for a `completed` terminal, so the two
|
|
45
|
+
* never disagree about what "stale" means:
|
|
46
|
+
* - the flag is set to `needsAnswers: false` via the SAME
|
|
47
|
+
* {@link setNeedsAnswersMarker} writer, and only when it parses back as
|
|
48
|
+
* `false` (a body the writer cannot annotate is left alone rather than
|
|
49
|
+
* written as something we cannot vouch for);
|
|
50
|
+
* - a sidecar carrying ANY answered entry is never deleted, and the item's flag
|
|
51
|
+
* is then left alone too (a human's unapplied answer is data the tool did not
|
|
52
|
+
* author; the answered-merge apply rung owns that sidecar). An unparseable
|
|
53
|
+
* sidecar is held the same way (every uncertainty resolves to leaving state
|
|
54
|
+
* alone).
|
|
55
|
+
*
|
|
56
|
+
* Only ever touches the landing task's own two paths, and only when its done
|
|
57
|
+
* record exists in the tree. A normal land (no flag, no sidecar) changes nothing.
|
|
58
|
+
* Best-effort: never throws; a fault leaves the tree as it was.
|
|
59
|
+
*/
|
|
60
|
+
export function clearOwnQuestionResidueOnLand(params: {
|
|
61
|
+
cwd: string;
|
|
62
|
+
slug: string;
|
|
63
|
+
note?: (message: string) => void;
|
|
64
|
+
}): LandQuestionResidueResult {
|
|
65
|
+
const {cwd, slug} = params;
|
|
66
|
+
const note = params.note ?? (() => {});
|
|
67
|
+
const result: LandQuestionResidueResult = {
|
|
68
|
+
changed: [],
|
|
69
|
+
unflagged: false,
|
|
70
|
+
drainedSidecar: false,
|
|
71
|
+
held: false,
|
|
72
|
+
};
|
|
73
|
+
const donePath = workItemPath(cwd, 'done', slug);
|
|
74
|
+
if (!existsSync(donePath)) {
|
|
75
|
+
return result; // not a task done-move (a lifecycle / degenerate shape).
|
|
76
|
+
}
|
|
77
|
+
const sidecarRel = sidecarPathFor(`task:${slug}`);
|
|
78
|
+
const sidecarAbs = join(cwd, sidecarRel);
|
|
79
|
+
try {
|
|
80
|
+
if (existsSync(sidecarAbs)) {
|
|
81
|
+
let answered: boolean;
|
|
82
|
+
try {
|
|
83
|
+
answered = parseSidecar(readFileSync(sidecarAbs, 'utf8')).entries.some(
|
|
84
|
+
(e) => isEntryAnswered(e),
|
|
85
|
+
);
|
|
86
|
+
} catch {
|
|
87
|
+
answered = true; // unparseable: hold, never delete what we cannot read.
|
|
88
|
+
}
|
|
89
|
+
if (answered) {
|
|
90
|
+
result.held = true;
|
|
91
|
+
note(
|
|
92
|
+
`Left ${sidecarRel} and the needsAnswers flag of '${slug}' in place: ` +
|
|
93
|
+
'the sidecar carries an answer (or could not be read), which the ' +
|
|
94
|
+
'land never discards.',
|
|
95
|
+
);
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
rmSync(sidecarAbs, {force: true});
|
|
99
|
+
result.drainedSidecar = true;
|
|
100
|
+
result.changed.push(sidecarRel);
|
|
101
|
+
}
|
|
102
|
+
const body = readFileSync(donePath, 'utf8');
|
|
103
|
+
if (parseFrontmatter(body).needsAnswers === true) {
|
|
104
|
+
const cleared = setNeedsAnswersMarker(body, false);
|
|
105
|
+
if (parseFrontmatter(cleared).needsAnswers === false) {
|
|
106
|
+
writeFileSync(donePath, cleared);
|
|
107
|
+
result.unflagged = true;
|
|
108
|
+
result.changed.push(workItemRel('done', `${slug}.md`));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
// Best-effort: never fail a green land over question-state hygiene.
|
|
113
|
+
}
|
|
114
|
+
if (result.changed.length > 0) {
|
|
115
|
+
note(
|
|
116
|
+
`Cleared the stale question state of '${slug}' in its land ` +
|
|
117
|
+
`(${result.changed.join(', ')}): the task is done, so its ` +
|
|
118
|
+
'needs-attention question is moot.',
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
package/src/lifecycle-gather.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type SidecarModel,
|
|
8
8
|
} from './sidecar.js';
|
|
9
9
|
import {runAsync} from './git.js';
|
|
10
|
+
import {answeredMergeActionIn} from './merge-answer.js';
|
|
10
11
|
import {
|
|
11
12
|
buildLifecyclePools,
|
|
12
13
|
type LifecyclePoolGates,
|
|
@@ -153,10 +154,20 @@ export function gatherLifecycleInPlace(input: {
|
|
|
153
154
|
* behaviour) so callers who cannot resolve a lock read still work.
|
|
154
155
|
*/
|
|
155
156
|
heldSlugs?: Set<string>;
|
|
157
|
+
/**
|
|
158
|
+
* The held TASK slugs whose lock a finished propose build keeps (the
|
|
159
|
+
* `propose-pr` marker, `proposeKeptTaskSlugs`). Such a slug is NOT subtracted
|
|
160
|
+
* when its sidecar carries an answered `merge` (task
|
|
161
|
+
* `wire-merge-questions-into-the-advance-tick`, decision 6): the answered-merge
|
|
162
|
+
* apply takes that lock over. Every other held slug is still subtracted.
|
|
163
|
+
* Defaults empty.
|
|
164
|
+
*/
|
|
165
|
+
proposeKeptSlugs?: Set<string>;
|
|
156
166
|
}): SelectedLifecyclePools {
|
|
157
167
|
const read = input.read ?? ledgerRead;
|
|
158
168
|
const repoPath = input.repoPath;
|
|
159
169
|
const heldSlugs = input.heldSlugs ?? new Set<string>();
|
|
170
|
+
const proposeKept = input.proposeKeptSlugs ?? new Set<string>();
|
|
160
171
|
|
|
161
172
|
const rawObservations = read.resolveLocalState({repoPath}).observations;
|
|
162
173
|
const observations: ObservationCandidate[] = rawObservations.map((obs) => ({
|
|
@@ -165,21 +176,40 @@ export function gatherLifecycleInPlace(input: {
|
|
|
165
176
|
sidecar: readSidecarInPlace(repoPath, 'observation', obs.slug),
|
|
166
177
|
}));
|
|
167
178
|
const surfaceStaging = input.gates?.surfaceStaging === true;
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
surfaceStaging
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
179
|
+
const blocked = blockedItemsInPlace(read, repoPath, surfaceStaging);
|
|
180
|
+
if (!surfaceStaging) {
|
|
181
|
+
// A merge question on a STAGED body (`tasks/backlog/`) is applied even with
|
|
182
|
+
// `surfaceStaging` off (task `wire-merge-questions-into-the-advance-tick`,
|
|
183
|
+
// decision 6): the surfacer asked it, the staging gate did not.
|
|
184
|
+
for (const item of read.resolveLocalTaskStaging({repoPath})) {
|
|
185
|
+
if (
|
|
186
|
+
item.needsAnswers === true &&
|
|
187
|
+
hasMergeQuestion(readSidecarInPlace(repoPath, 'task', item.slug))
|
|
188
|
+
) {
|
|
189
|
+
blocked.push({namespace: 'task', slug: item.slug});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const needsAnswers: NeedsAnswersCandidate[] = blocked
|
|
177
194
|
.map((item) => ({
|
|
178
195
|
repoPath,
|
|
179
196
|
namespace: item.namespace,
|
|
180
197
|
slug: item.slug,
|
|
181
198
|
sidecar: readSidecarInPlace(repoPath, item.namespace, item.slug),
|
|
182
|
-
}))
|
|
199
|
+
}))
|
|
200
|
+
// Held-slug subtraction (task-scoped): a `needsAnswers` TASK whose per-item
|
|
201
|
+
// lock is currently held is EXCLUDED from surface/apply enumeration, unless
|
|
202
|
+
// the lock is kept for a propose PR and the item's merge question is
|
|
203
|
+
// answered `merge` (the answered-merge apply takes that lock over). Spec /
|
|
204
|
+
// observation entries are untouched: the lock ref set here is task-only.
|
|
205
|
+
.filter(
|
|
206
|
+
(item) =>
|
|
207
|
+
!(
|
|
208
|
+
item.namespace === 'task' &&
|
|
209
|
+
heldSlugs.has(item.slug) &&
|
|
210
|
+
!mayTakeOverProposeKept(proposeKept, item.slug, item.sidecar)
|
|
211
|
+
),
|
|
212
|
+
);
|
|
183
213
|
|
|
184
214
|
return buildLifecyclePools({
|
|
185
215
|
repoPath,
|
|
@@ -189,6 +219,28 @@ export function gatherLifecycleInPlace(input: {
|
|
|
189
219
|
});
|
|
190
220
|
}
|
|
191
221
|
|
|
222
|
+
/** Does the sidecar carry a merge question (answered or not)? */
|
|
223
|
+
function hasMergeQuestion(sidecar: SidecarModel | undefined): boolean {
|
|
224
|
+
return sidecar?.entries.some((e) => e.kind === 'merge') === true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* May a HELD task still be selected? Only when its lock is kept for a propose PR
|
|
229
|
+
* (`proposeKept`) and its merge question is answered `merge`: that apply takes
|
|
230
|
+
* the lock over (task `wire-merge-questions-into-the-advance-tick`, decision 6).
|
|
231
|
+
*/
|
|
232
|
+
export function mayTakeOverProposeKept(
|
|
233
|
+
proposeKept: Set<string>,
|
|
234
|
+
slug: string,
|
|
235
|
+
sidecar: SidecarModel | undefined,
|
|
236
|
+
): boolean {
|
|
237
|
+
return (
|
|
238
|
+
proposeKept.has(slug) &&
|
|
239
|
+
sidecar !== undefined &&
|
|
240
|
+
answeredMergeActionIn(sidecar)?.verb === 'merge'
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
192
244
|
/**
|
|
193
245
|
* Read the ACTIVE sidecar for `<namespace>:<slug>` from a BARE hub mirror's
|
|
194
246
|
* committed `<ref>:work/questions/<type>-<slug>.md` via `git show` (the SAME
|
|
@@ -243,6 +295,25 @@ export async function gatherLifecycleMirror(input: {
|
|
|
243
295
|
read.resolveMirrorSpecStaging({mirrorPath, ref, env}),
|
|
244
296
|
])
|
|
245
297
|
: [[], []];
|
|
298
|
+
// A merge question on a STAGED body is applied even with `surfaceStaging`
|
|
299
|
+
// off (decision 6), as in {@link gatherLifecycleInPlace}.
|
|
300
|
+
const stagedMergeQuestions: BlockedItem[] = [];
|
|
301
|
+
if (!surfaceStaging) {
|
|
302
|
+
for (const item of await read.resolveMirrorTaskStaging({
|
|
303
|
+
mirrorPath,
|
|
304
|
+
ref,
|
|
305
|
+
env,
|
|
306
|
+
})) {
|
|
307
|
+
if (
|
|
308
|
+
item.needsAnswers === true &&
|
|
309
|
+
hasMergeQuestion(
|
|
310
|
+
await readSidecarMirror(mirrorPath, ref, 'task', item.slug, env),
|
|
311
|
+
)
|
|
312
|
+
) {
|
|
313
|
+
stagedMergeQuestions.push({namespace: 'task', slug: item.slug});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
246
317
|
|
|
247
318
|
const blocked: BlockedItem[] = [];
|
|
248
319
|
for (const item of state.ready) {
|
|
@@ -282,6 +353,7 @@ export async function gatherLifecycleMirror(input: {
|
|
|
282
353
|
blocked.push({namespace: 'spec', slug: spec.slug});
|
|
283
354
|
}
|
|
284
355
|
}
|
|
356
|
+
blocked.push(...stagedMergeQuestions);
|
|
285
357
|
|
|
286
358
|
const needsAnswers: NeedsAnswersCandidate[] = await Promise.all(
|
|
287
359
|
blocked.map(async (item) => ({
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isEntryAnswered,
|
|
3
|
+
type SidecarEntry,
|
|
4
|
+
type SidecarModel,
|
|
5
|
+
} from './sidecar.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The PARSE half of the answered merge-question (task
|
|
9
|
+
* `apply-rung-merge-disposition`): the human's plain `merge | hold | drop`
|
|
10
|
+
* answer to a `kind: merge` sidecar entry, read deterministically. Kept apart
|
|
11
|
+
* from the dispatcher (`apply-merge-action.ts`, which re-exports it) so the
|
|
12
|
+
* selection path can read an answered merge without importing the land
|
|
13
|
+
* machinery.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** The three deterministic actions a merge-question answer encodes. */
|
|
17
|
+
export type MergeActionVerb = 'merge' | 'hold' | 'drop';
|
|
18
|
+
|
|
19
|
+
/** A detected, answer-driven merge-action keyed off ONE answered `kind: merge` entry. */
|
|
20
|
+
export interface DetectedMergeAction {
|
|
21
|
+
/** The deterministic verb parsed from the entry's answer text. */
|
|
22
|
+
verb: MergeActionVerb;
|
|
23
|
+
/** The answered `kind: merge` entry the verb came from. */
|
|
24
|
+
entry: SidecarEntry;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parse the human's plain free-text answer into the deterministic
|
|
29
|
+
* {@link MergeActionVerb}. The merge-question surfacer renders
|
|
30
|
+
* `merge | hold | drop` in the entry's `default` as a human-readable hint, so
|
|
31
|
+
* the human typically types one of those words verbatim. We accept any text
|
|
32
|
+
* whose first whole word (case-insensitive) is one of `merge` / `hold` / `drop`
|
|
33
|
+
* — this is the same machine-parseable choice shape the surfacer's applied
|
|
34
|
+
* answer q1 documents ("a DETERMINISTIC CHOICE shape … the human picks and the
|
|
35
|
+
* system parses unambiguously").
|
|
36
|
+
*
|
|
37
|
+
* Returns `undefined` on ANYTHING else (an empty answer, a typo, a long
|
|
38
|
+
* narrative without one of the three words at the start) so the caller can
|
|
39
|
+
* route the ambiguity HONESTLY — never default-to-merge on a malformed answer,
|
|
40
|
+
* which would invent a land the human did not authorise.
|
|
41
|
+
*/
|
|
42
|
+
export function parseMergeAnswer(text: string): MergeActionVerb | undefined {
|
|
43
|
+
const trimmed = text.trim().toLowerCase();
|
|
44
|
+
if (trimmed === '') return undefined;
|
|
45
|
+
// The first whole alphabetic word; trailing punctuation (a comma, em-dash,
|
|
46
|
+
// period) and any following commentary are tolerated. We deliberately do
|
|
47
|
+
// NOT consume hyphens / apostrophes — the three verbs are plain ASCII
|
|
48
|
+
// words, and over-tolerating typo-shapes would silently invent a verb the
|
|
49
|
+
// human did not pick.
|
|
50
|
+
const match = /^([a-z]+)/.exec(trimmed);
|
|
51
|
+
if (match === null) return undefined;
|
|
52
|
+
const word = match[1];
|
|
53
|
+
if (word === 'merge' || word === 'hold' || word === 'drop') {
|
|
54
|
+
return word;
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* `detectAnsweredMergeAction` (`apply-merge-action.ts`) over an already-parsed sidecar model (the
|
|
61
|
+
* selection path reads sidecars itself, in place and from a mirror ref). The
|
|
62
|
+
* same ordering rules apply.
|
|
63
|
+
*/
|
|
64
|
+
export function answeredMergeActionIn(
|
|
65
|
+
model: SidecarModel,
|
|
66
|
+
): DetectedMergeAction | undefined {
|
|
67
|
+
// (1) Unanswered `kind: merge` follow-up ⇒ re-paused; the apply must NOT fire.
|
|
68
|
+
for (const entry of model.entries) {
|
|
69
|
+
if (entry.kind === 'merge' && !isEntryAnswered(entry)) return undefined;
|
|
70
|
+
}
|
|
71
|
+
// (2) LATEST answered `kind: merge` entry — a fresh follow-up beats a stale one.
|
|
72
|
+
for (let i = model.entries.length - 1; i >= 0; i--) {
|
|
73
|
+
const entry = model.entries[i];
|
|
74
|
+
if (entry.kind !== 'merge') continue;
|
|
75
|
+
if (!isEntryAnswered(entry)) continue;
|
|
76
|
+
const verb = parseMergeAnswer(entry.answer);
|
|
77
|
+
if (verb !== undefined) {
|
|
78
|
+
return {verb, entry};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|