dorfl 0.0.0 → 0.1.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 +73 -0
- package/dist/advance-ci-template.d.ts.map +1 -0
- package/dist/advance-ci-template.js +104 -0
- package/dist/advance-ci-template.js.map +1 -0
- package/dist/advance-classify.d.ts +132 -0
- package/dist/advance-classify.d.ts.map +1 -0
- package/dist/advance-classify.js +120 -0
- package/dist/advance-classify.js.map +1 -0
- package/dist/advance-drivers.d.ts +182 -0
- package/dist/advance-drivers.d.ts.map +1 -0
- package/dist/advance-drivers.js +231 -0
- package/dist/advance-drivers.js.map +1 -0
- package/dist/advance-isolated.d.ts +156 -0
- package/dist/advance-isolated.d.ts.map +1 -0
- package/dist/advance-isolated.js +256 -0
- package/dist/advance-isolated.js.map +1 -0
- package/dist/advance-lifecycle-template.d.ts +107 -0
- package/dist/advance-lifecycle-template.d.ts.map +1 -0
- package/dist/advance-lifecycle-template.js +668 -0
- package/dist/advance-lifecycle-template.js.map +1 -0
- package/dist/advance-loop-driver.d.ts +325 -0
- package/dist/advance-loop-driver.d.ts.map +1 -0
- package/dist/advance-loop-driver.js +437 -0
- package/dist/advance-loop-driver.js.map +1 -0
- package/dist/advance-treeless-publish.d.ts +108 -0
- package/dist/advance-treeless-publish.d.ts.map +1 -0
- package/dist/advance-treeless-publish.js +71 -0
- package/dist/advance-treeless-publish.js.map +1 -0
- package/dist/advance.d.ts +340 -0
- package/dist/advance.d.ts.map +1 -0
- package/dist/advance.js +1122 -0
- package/dist/advance.js.map +1 -0
- package/dist/advancing-lock.d.ts +294 -0
- package/dist/advancing-lock.d.ts.map +1 -0
- package/dist/advancing-lock.js +594 -0
- package/dist/advancing-lock.js.map +1 -0
- package/dist/agent-launch.d.ts +79 -0
- package/dist/agent-launch.d.ts.map +1 -0
- package/dist/agent-launch.js +61 -0
- package/dist/agent-launch.js.map +1 -0
- package/dist/agent-stop.d.ts +149 -0
- package/dist/agent-stop.d.ts.map +1 -0
- package/dist/agent-stop.js +307 -0
- package/dist/agent-stop.js.map +1 -0
- package/dist/apply-decide.d.ts +127 -0
- package/dist/apply-decide.d.ts.map +1 -0
- package/dist/apply-decide.js +176 -0
- package/dist/apply-decide.js.map +1 -0
- package/dist/apply-merge-action.d.ts +206 -0
- package/dist/apply-merge-action.d.ts.map +1 -0
- package/dist/apply-merge-action.js +307 -0
- package/dist/apply-merge-action.js.map +1 -0
- package/dist/apply-persist.d.ts +174 -0
- package/dist/apply-persist.d.ts.map +1 -0
- package/dist/apply-persist.js +359 -0
- package/dist/apply-persist.js.map +1 -0
- package/dist/arbiter.d.ts +120 -0
- package/dist/arbiter.d.ts.map +1 -0
- package/dist/arbiter.js +255 -0
- package/dist/arbiter.js.map +1 -0
- package/dist/brand.d.ts +70 -0
- package/dist/brand.d.ts.map +1 -0
- package/dist/brand.js +84 -0
- package/dist/brand.js.map +1 -0
- package/dist/buildable-body.d.ts +132 -0
- package/dist/buildable-body.d.ts.map +1 -0
- package/dist/buildable-body.js +131 -0
- package/dist/buildable-body.js.map +1 -0
- package/dist/categorise.d.ts +66 -0
- package/dist/categorise.d.ts.map +1 -0
- package/dist/categorise.js +106 -0
- package/dist/categorise.js.map +1 -0
- package/dist/claim-cas.d.ts +117 -0
- package/dist/claim-cas.d.ts.map +1 -0
- package/dist/claim-cas.js +312 -0
- package/dist/claim-cas.js.map +1 -0
- package/dist/cli-spinner.d.ts +112 -0
- package/dist/cli-spinner.d.ts.map +1 -0
- package/dist/cli-spinner.js +157 -0
- package/dist/cli-spinner.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +3094 -0
- package/dist/cli.js.map +1 -0
- package/dist/close-job-template.d.ts +70 -0
- package/dist/close-job-template.d.ts.map +1 -0
- package/dist/close-job-template.js +180 -0
- package/dist/close-job-template.js.map +1 -0
- package/dist/close-job.d.ts +95 -0
- package/dist/close-job.d.ts.map +1 -0
- package/dist/close-job.js +226 -0
- package/dist/close-job.js.map +1 -0
- package/dist/complete.d.ts +361 -0
- package/dist/complete.d.ts.map +1 -0
- package/dist/complete.js +885 -0
- package/dist/complete.js.map +1 -0
- package/dist/concurrency.d.ts +68 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +112 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/config-override.d.ts +76 -0
- package/dist/config-override.d.ts.map +1 -0
- package/dist/config-override.js +50 -0
- package/dist/config-override.js.map +1 -0
- package/dist/config.d.ts +668 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +241 -0
- package/dist/config.js.map +1 -0
- package/dist/continue-branch.d.ts +249 -0
- package/dist/continue-branch.d.ts.map +1 -0
- package/dist/continue-branch.js +389 -0
- package/dist/continue-branch.js.map +1 -0
- package/dist/cwd-section.d.ts +186 -0
- package/dist/cwd-section.d.ts.map +1 -0
- package/dist/cwd-section.js +209 -0
- package/dist/cwd-section.js.map +1 -0
- package/dist/decision-engine.d.ts +170 -0
- package/dist/decision-engine.d.ts.map +1 -0
- package/dist/decision-engine.js +136 -0
- package/dist/decision-engine.js.map +1 -0
- package/dist/detect.d.ts +17 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +118 -0
- package/dist/detect.js.map +1 -0
- package/dist/do-autopick.d.ts +85 -0
- package/dist/do-autopick.d.ts.map +1 -0
- package/dist/do-autopick.js +112 -0
- package/dist/do-autopick.js.map +1 -0
- package/dist/do-config.d.ts +312 -0
- package/dist/do-config.d.ts.map +1 -0
- package/dist/do-config.js +358 -0
- package/dist/do-config.js.map +1 -0
- package/dist/do-remote-auto.d.ts +75 -0
- package/dist/do-remote-auto.d.ts.map +1 -0
- package/dist/do-remote-auto.js +111 -0
- package/dist/do-remote-auto.js.map +1 -0
- package/dist/do.d.ts +621 -0
- package/dist/do.d.ts.map +1 -0
- package/dist/do.js +1882 -0
- package/dist/do.js.map +1 -0
- package/dist/drop-source.d.ts +96 -0
- package/dist/drop-source.d.ts.map +1 -0
- package/dist/drop-source.js +91 -0
- package/dist/drop-source.js.map +1 -0
- package/dist/eligibility.d.ts +46 -0
- package/dist/eligibility.d.ts.map +1 -0
- package/dist/eligibility.js +34 -0
- package/dist/eligibility.js.map +1 -0
- package/dist/env-config.d.ts +51 -0
- package/dist/env-config.d.ts.map +1 -0
- package/dist/env-config.js +272 -0
- package/dist/env-config.js.map +1 -0
- package/dist/failure-cause.d.ts +70 -0
- package/dist/failure-cause.d.ts.map +1 -0
- package/dist/failure-cause.js +126 -0
- package/dist/failure-cause.js.map +1 -0
- package/dist/format.d.ts +43 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +256 -0
- package/dist/format.js.map +1 -0
- package/dist/frontmatter.d.ts +215 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +345 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/gate-readiness.d.ts +84 -0
- package/dist/gate-readiness.d.ts.map +1 -0
- package/dist/gate-readiness.js +103 -0
- package/dist/gate-readiness.js.map +1 -0
- package/dist/gc.d.ts +165 -0
- package/dist/gc.d.ts.map +1 -0
- package/dist/gc.js +313 -0
- package/dist/gc.js.map +1 -0
- package/dist/gh-failure.d.ts +42 -0
- package/dist/gh-failure.d.ts.map +1 -0
- package/dist/gh-failure.js +49 -0
- package/dist/gh-failure.js.map +1 -0
- package/dist/git.d.ts +75 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +130 -0
- package/dist/git.js.map +1 -0
- package/dist/github.d.ts +187 -0
- package/dist/github.d.ts.map +1 -0
- package/dist/github.js +343 -0
- package/dist/github.js.map +1 -0
- package/dist/harness.d.ts +242 -0
- package/dist/harness.d.ts.map +1 -0
- package/dist/harness.js +157 -0
- package/dist/harness.js.map +1 -0
- package/dist/identity.d.ts +167 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +231 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +147 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/install-ci-branch-protection.d.ts +147 -0
- package/dist/install-ci-branch-protection.d.ts.map +1 -0
- package/dist/install-ci-branch-protection.js +166 -0
- package/dist/install-ci-branch-protection.js.map +1 -0
- package/dist/install-ci-capabilities/advance-lifecycle.d.ts +15 -0
- package/dist/install-ci-capabilities/advance-lifecycle.d.ts.map +1 -0
- package/dist/install-ci-capabilities/advance-lifecycle.js +28 -0
- package/dist/install-ci-capabilities/advance-lifecycle.js.map +1 -0
- package/dist/install-ci-capabilities/close-job.d.ts +13 -0
- package/dist/install-ci-capabilities/close-job.d.ts.map +1 -0
- package/dist/install-ci-capabilities/close-job.js +26 -0
- package/dist/install-ci-capabilities/close-job.js.map +1 -0
- package/dist/install-ci-capabilities/example-noop.d.ts +16 -0
- package/dist/install-ci-capabilities/example-noop.d.ts.map +1 -0
- package/dist/install-ci-capabilities/example-noop.js +23 -0
- package/dist/install-ci-capabilities/example-noop.js.map +1 -0
- package/dist/install-ci-capabilities/intake.d.ts +15 -0
- package/dist/install-ci-capabilities/intake.d.ts.map +1 -0
- package/dist/install-ci-capabilities/intake.js +28 -0
- package/dist/install-ci-capabilities/intake.js.map +1 -0
- package/dist/install-ci-capabilities/verify.d.ts +14 -0
- package/dist/install-ci-capabilities/verify.d.ts.map +1 -0
- package/dist/install-ci-capabilities/verify.js +27 -0
- package/dist/install-ci-capabilities/verify.js.map +1 -0
- package/dist/install-ci-core.d.ts +446 -0
- package/dist/install-ci-core.d.ts.map +1 -0
- package/dist/install-ci-core.js +760 -0
- package/dist/install-ci-core.js.map +1 -0
- package/dist/install-ci-github.d.ts +167 -0
- package/dist/install-ci-github.d.ts.map +1 -0
- package/dist/install-ci-github.js +315 -0
- package/dist/install-ci-github.js.map +1 -0
- package/dist/install-ci.d.ts +105 -0
- package/dist/install-ci.d.ts.map +1 -0
- package/dist/install-ci.js +363 -0
- package/dist/install-ci.js.map +1 -0
- package/dist/intake-event.d.ts +88 -0
- package/dist/intake-event.d.ts.map +1 -0
- package/dist/intake-event.js +66 -0
- package/dist/intake-event.js.map +1 -0
- package/dist/intake-marker.d.ts +95 -0
- package/dist/intake-marker.d.ts.map +1 -0
- package/dist/intake-marker.js +127 -0
- package/dist/intake-marker.js.map +1 -0
- package/dist/intake-triage.d.ts +48 -0
- package/dist/intake-triage.d.ts.map +1 -0
- package/dist/intake-triage.js +95 -0
- package/dist/intake-triage.js.map +1 -0
- package/dist/intake-trigger-template.d.ts +185 -0
- package/dist/intake-trigger-template.d.ts.map +1 -0
- package/dist/intake-trigger-template.js +449 -0
- package/dist/intake-trigger-template.js.map +1 -0
- package/dist/intake.d.ts +569 -0
- package/dist/intake.d.ts.map +1 -0
- package/dist/intake.js +1628 -0
- package/dist/intake.js.map +1 -0
- package/dist/integration-core.d.ts +539 -0
- package/dist/integration-core.d.ts.map +1 -0
- package/dist/integration-core.js +2195 -0
- package/dist/integration-core.js.map +1 -0
- package/dist/integrator.d.ts +343 -0
- package/dist/integrator.d.ts.map +1 -0
- package/dist/integrator.js +400 -0
- package/dist/integrator.js.map +1 -0
- package/dist/isolation.d.ts +219 -0
- package/dist/isolation.d.ts.map +1 -0
- package/dist/isolation.js +261 -0
- package/dist/isolation.js.map +1 -0
- package/dist/issue-provider.d.ts +349 -0
- package/dist/issue-provider.d.ts.map +1 -0
- package/dist/issue-provider.js +360 -0
- package/dist/issue-provider.js.map +1 -0
- package/dist/item-lock.d.ts +626 -0
- package/dist/item-lock.d.ts.map +1 -0
- package/dist/item-lock.js +1381 -0
- package/dist/item-lock.js.map +1 -0
- package/dist/item-path.d.ts +49 -0
- package/dist/item-path.d.ts.map +1 -0
- package/dist/item-path.js +66 -0
- package/dist/item-path.js.map +1 -0
- package/dist/ledger-lint.d.ts +129 -0
- package/dist/ledger-lint.d.ts.map +1 -0
- package/dist/ledger-lint.js +249 -0
- package/dist/ledger-lint.js.map +1 -0
- package/dist/ledger-read.d.ts +357 -0
- package/dist/ledger-read.d.ts.map +1 -0
- package/dist/ledger-read.js +442 -0
- package/dist/ledger-read.js.map +1 -0
- package/dist/ledger-write.d.ts +330 -0
- package/dist/ledger-write.d.ts.map +1 -0
- package/dist/ledger-write.js +411 -0
- package/dist/ledger-write.js.map +1 -0
- package/dist/lifecycle-gather.d.ts +30 -0
- package/dist/lifecycle-gather.d.ts.map +1 -0
- package/dist/lifecycle-gather.js +205 -0
- package/dist/lifecycle-gather.js.map +1 -0
- package/dist/lifecycle-pools.d.ts +180 -0
- package/dist/lifecycle-pools.d.ts.map +1 -0
- package/dist/lifecycle-pools.js +78 -0
- package/dist/lifecycle-pools.js.map +1 -0
- package/dist/merge-question-surfacer.d.ts +166 -0
- package/dist/merge-question-surfacer.d.ts.map +1 -0
- package/dist/merge-question-surfacer.js +297 -0
- package/dist/merge-question-surfacer.js.map +1 -0
- package/dist/mint-adr.d.ts +126 -0
- package/dist/mint-adr.d.ts.map +1 -0
- package/dist/mint-adr.js +257 -0
- package/dist/mint-adr.js.map +1 -0
- package/dist/mirror-pool-scan.d.ts +125 -0
- package/dist/mirror-pool-scan.d.ts.map +1 -0
- package/dist/mirror-pool-scan.js +104 -0
- package/dist/mirror-pool-scan.js.map +1 -0
- package/dist/needs-attention.d.ts +341 -0
- package/dist/needs-attention.d.ts.map +1 -0
- package/dist/needs-attention.js +900 -0
- package/dist/needs-attention.js.map +1 -0
- package/dist/orphan-sidecar.d.ts +79 -0
- package/dist/orphan-sidecar.d.ts.map +1 -0
- package/dist/orphan-sidecar.js +71 -0
- package/dist/orphan-sidecar.js.map +1 -0
- package/dist/output.d.ts +48 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +66 -0
- package/dist/output.js.map +1 -0
- package/dist/pi-harness.d.ts +179 -0
- package/dist/pi-harness.d.ts.map +1 -0
- package/dist/pi-harness.js +342 -0
- package/dist/pi-harness.js.map +1 -0
- package/dist/placement.d.ts +99 -0
- package/dist/placement.d.ts.map +1 -0
- package/dist/placement.js +67 -0
- package/dist/placement.js.map +1 -0
- package/dist/prd-to-spec.d.ts +315 -0
- package/dist/prd-to-spec.d.ts.map +1 -0
- package/dist/prd-to-spec.js +684 -0
- package/dist/prd-to-spec.js.map +1 -0
- package/dist/prepare.d.ts +121 -0
- package/dist/prepare.d.ts.map +1 -0
- package/dist/prepare.js +140 -0
- package/dist/prepare.js.map +1 -0
- package/dist/prompt.d.ts +360 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +499 -0
- package/dist/prompt.js.map +1 -0
- package/dist/protocol/ADR-FORMAT.md +47 -0
- package/dist/protocol/CLAIM-PROTOCOL.md +217 -0
- package/dist/protocol/REVIEW-PROTOCOL.md +119 -0
- package/dist/protocol/SURFACE-PROTOCOL.md +121 -0
- package/dist/protocol/TASKING-PROTOCOL.md +122 -0
- package/dist/protocol/WORK-CONTRACT.md +276 -0
- package/dist/protocol/spec-template.md +71 -0
- package/dist/protocol/task-template.md +65 -0
- package/dist/readiness.d.ts +66 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +36 -0
- package/dist/readiness.js.map +1 -0
- package/dist/reap-branches.d.ts +102 -0
- package/dist/reap-branches.d.ts.map +1 -0
- package/dist/reap-branches.js +149 -0
- package/dist/reap-branches.js.map +1 -0
- package/dist/recover-isolated.d.ts +72 -0
- package/dist/recover-isolated.d.ts.map +1 -0
- package/dist/recover-isolated.js +188 -0
- package/dist/recover-isolated.js.map +1 -0
- package/dist/registry.d.ts +172 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +296 -0
- package/dist/registry.js.map +1 -0
- package/dist/repo-config.d.ts +201 -0
- package/dist/repo-config.d.ts.map +1 -0
- package/dist/repo-config.js +414 -0
- package/dist/repo-config.js.map +1 -0
- package/dist/repo-key.d.ts +20 -0
- package/dist/repo-key.d.ts.map +1 -0
- package/dist/repo-key.js +68 -0
- package/dist/repo-key.js.map +1 -0
- package/dist/repo-mirror.d.ts +177 -0
- package/dist/repo-mirror.d.ts.map +1 -0
- package/dist/repo-mirror.js +271 -0
- package/dist/repo-mirror.js.map +1 -0
- package/dist/retry-backoff.d.ts +90 -0
- package/dist/retry-backoff.d.ts.map +1 -0
- package/dist/retry-backoff.js +98 -0
- package/dist/retry-backoff.js.map +1 -0
- package/dist/review-gate.d.ts +173 -0
- package/dist/review-gate.d.ts.map +1 -0
- package/dist/review-gate.js +261 -0
- package/dist/review-gate.js.map +1 -0
- package/dist/review-verdict.d.ts +149 -0
- package/dist/review-verdict.d.ts.map +1 -0
- package/dist/review-verdict.js +332 -0
- package/dist/review-verdict.js.map +1 -0
- package/dist/run.d.ts +221 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +963 -0
- package/dist/run.js.map +1 -0
- package/dist/scan.d.ts +308 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +374 -0
- package/dist/scan.js.map +1 -0
- package/dist/select-order.d.ts +75 -0
- package/dist/select-order.d.ts.map +1 -0
- package/dist/select-order.js +108 -0
- package/dist/select-order.js.map +1 -0
- package/dist/select-priority.d.ts +188 -0
- package/dist/select-priority.d.ts.map +1 -0
- package/dist/select-priority.js +80 -0
- package/dist/select-priority.js.map +1 -0
- package/dist/select.d.ts +25 -0
- package/dist/select.d.ts.map +1 -0
- package/dist/select.js +43 -0
- package/dist/select.js.map +1 -0
- package/dist/session-path.d.ts +36 -0
- package/dist/session-path.d.ts.map +1 -0
- package/dist/session-path.js +129 -0
- package/dist/session-path.js.map +1 -0
- package/dist/sidecar-apply.d.ts +83 -0
- package/dist/sidecar-apply.d.ts.map +1 -0
- package/dist/sidecar-apply.js +111 -0
- package/dist/sidecar-apply.js.map +1 -0
- package/dist/sidecar.d.ts +245 -0
- package/dist/sidecar.d.ts.map +1 -0
- package/dist/sidecar.js +481 -0
- package/dist/sidecar.js.map +1 -0
- package/dist/slug-namespace.d.ts +204 -0
- package/dist/slug-namespace.d.ts.map +1 -0
- package/dist/slug-namespace.js +229 -0
- package/dist/slug-namespace.js.map +1 -0
- package/dist/spec-complete.d.ts +44 -0
- package/dist/spec-complete.d.ts.map +1 -0
- package/dist/spec-complete.js +69 -0
- package/dist/spec-complete.js.map +1 -0
- package/dist/start.d.ts +97 -0
- package/dist/start.d.ts.map +1 -0
- package/dist/start.js +633 -0
- package/dist/start.js.map +1 -0
- package/dist/status.d.ts +199 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +228 -0
- package/dist/status.js.map +1 -0
- package/dist/surface-gate.d.ts +162 -0
- package/dist/surface-gate.d.ts.map +1 -0
- package/dist/surface-gate.js +206 -0
- package/dist/surface-gate.js.map +1 -0
- package/dist/surface-persist.d.ts +86 -0
- package/dist/surface-persist.d.ts.map +1 -0
- package/dist/surface-persist.js +129 -0
- package/dist/surface-persist.js.map +1 -0
- package/dist/tasker-review-loop.d.ts +249 -0
- package/dist/tasker-review-loop.d.ts.map +1 -0
- package/dist/tasker-review-loop.js +369 -0
- package/dist/tasker-review-loop.js.map +1 -0
- package/dist/tasking-eligibility.d.ts +74 -0
- package/dist/tasking-eligibility.d.ts.map +1 -0
- package/dist/tasking-eligibility.js +52 -0
- package/dist/tasking-eligibility.js.map +1 -0
- package/dist/tasking-lock.d.ts +111 -0
- package/dist/tasking-lock.d.ts.map +1 -0
- package/dist/tasking-lock.js +256 -0
- package/dist/tasking-lock.js.map +1 -0
- package/dist/tasking.d.ts +275 -0
- package/dist/tasking.d.ts.map +1 -0
- package/dist/tasking.js +952 -0
- package/dist/tasking.js.map +1 -0
- package/dist/triage-gate.d.ts +127 -0
- package/dist/triage-gate.d.ts.map +1 -0
- package/dist/triage-gate.js +139 -0
- package/dist/triage-gate.js.map +1 -0
- package/dist/triage-persist.d.ts +163 -0
- package/dist/triage-persist.d.ts.map +1 -0
- package/dist/triage-persist.js +387 -0
- package/dist/triage-persist.js.map +1 -0
- package/dist/verdict-json.d.ts +32 -0
- package/dist/verdict-json.d.ts.map +1 -0
- package/dist/verdict-json.js +74 -0
- package/dist/verdict-json.js.map +1 -0
- package/dist/verify-workflow-template.d.ts +60 -0
- package/dist/verify-workflow-template.d.ts.map +1 -0
- package/dist/verify-workflow-template.js +126 -0
- package/dist/verify-workflow-template.js.map +1 -0
- package/dist/verify.d.ts +60 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +62 -0
- package/dist/verify.js.map +1 -0
- package/dist/watch-session.d.ts +112 -0
- package/dist/watch-session.d.ts.map +1 -0
- package/dist/watch-session.js +347 -0
- package/dist/watch-session.js.map +1 -0
- package/dist/work-layout.d.ts +198 -0
- package/dist/work-layout.d.ts.map +1 -0
- package/dist/work-layout.js +217 -0
- package/dist/work-layout.js.map +1 -0
- package/dist/work-on.d.ts +154 -0
- package/dist/work-on.d.ts.map +1 -0
- package/dist/work-on.js +387 -0
- package/dist/work-on.js.map +1 -0
- package/dist/workspace.d.ts +224 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +325 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +46 -2
- package/src/advance-ci-template.ts +203 -0
- package/src/advance-classify.ts +197 -0
- package/src/advance-drivers.ts +414 -0
- package/src/advance-isolated.ts +432 -0
- package/src/advance-lifecycle-template.ts +791 -0
- package/src/advance-loop-driver.ts +745 -0
- package/src/advance-treeless-publish.ts +177 -0
- package/src/advance.ts +1564 -0
- package/src/advancing-lock.ts +988 -0
- package/src/agent-launch.ts +137 -0
- package/src/agent-stop.ts +361 -0
- package/src/apply-decide.ts +242 -0
- package/src/apply-merge-action.ts +502 -0
- package/src/apply-persist.ts +518 -0
- package/src/arbiter.ts +372 -0
- package/src/brand.ts +111 -0
- package/src/buildable-body.ts +196 -0
- package/src/categorise.ts +158 -0
- package/src/claim-cas.ts +513 -0
- package/src/cli-spinner.ts +225 -0
- package/src/cli.ts +4369 -0
- package/src/close-job-template.ts +236 -0
- package/src/close-job.ts +319 -0
- package/src/complete.ts +1379 -0
- package/src/concurrency.ts +151 -0
- package/src/config-override.ts +116 -0
- package/src/config.ts +883 -0
- package/src/continue-branch.ts +542 -0
- package/src/cwd-section.ts +392 -0
- package/src/decision-engine.ts +272 -0
- package/src/detect.ts +124 -0
- package/src/do-autopick.ts +223 -0
- package/src/do-config.ts +589 -0
- package/src/do-remote-auto.ts +197 -0
- package/src/do.ts +2623 -0
- package/src/drop-source.ts +194 -0
- package/src/eligibility.ts +79 -0
- package/src/env-config.ts +305 -0
- package/src/failure-cause.ts +142 -0
- package/src/format.ts +313 -0
- package/src/frontmatter.ts +485 -0
- package/src/gate-readiness.ts +147 -0
- package/src/gc.ts +510 -0
- package/src/gh-failure.ts +53 -0
- package/src/git.ts +186 -0
- package/src/github.ts +468 -0
- package/src/harness.ts +355 -0
- package/src/identity.ts +322 -0
- package/src/index.ts +785 -0
- package/src/install-ci-branch-protection.ts +255 -0
- package/src/install-ci-capabilities/advance-lifecycle.ts +34 -0
- package/src/install-ci-capabilities/close-job.ts +32 -0
- package/src/install-ci-capabilities/example-noop.ts +24 -0
- package/src/install-ci-capabilities/intake.ts +34 -0
- package/src/install-ci-capabilities/verify.ts +33 -0
- package/src/install-ci-core.ts +1088 -0
- package/src/install-ci-github.ts +376 -0
- package/src/install-ci.ts +552 -0
- package/src/intake-event.ts +102 -0
- package/src/intake-marker.ts +195 -0
- package/src/intake-triage.ts +138 -0
- package/src/intake-trigger-template.ts +591 -0
- package/src/intake.ts +2445 -0
- package/src/integration-core.ts +3065 -0
- package/src/integrator.ts +771 -0
- package/src/isolation.ts +484 -0
- package/src/issue-provider.ts +733 -0
- package/src/item-lock.ts +1858 -0
- package/src/item-path.ts +75 -0
- package/src/ledger-lint.ts +332 -0
- package/src/ledger-read.ts +924 -0
- package/src/ledger-write.ts +865 -0
- package/src/lifecycle-gather.ts +298 -0
- package/src/lifecycle-pools.ts +250 -0
- package/src/merge-question-surfacer.ts +496 -0
- package/src/mint-adr.ts +362 -0
- package/src/mirror-pool-scan.ts +240 -0
- package/src/needs-attention.ts +1506 -0
- package/src/orphan-sidecar.ts +150 -0
- package/src/output.ts +89 -0
- package/src/pi-harness.ts +403 -0
- package/src/placement.ts +131 -0
- package/src/prd-to-spec.ts +1023 -0
- package/src/prepare.ts +230 -0
- package/src/prompt.ts +760 -0
- package/src/readiness.ts +98 -0
- package/src/reap-branches.ts +278 -0
- package/src/recover-isolated.ts +276 -0
- package/src/registry.ts +475 -0
- package/src/repo-config.ts +550 -0
- package/src/repo-key.ts +74 -0
- package/src/repo-mirror.ts +367 -0
- package/src/retry-backoff.ts +130 -0
- package/src/review-gate.ts +389 -0
- package/src/review-verdict.ts +422 -0
- package/src/run.ts +1430 -0
- package/src/scan.ts +611 -0
- package/src/select-order.ts +143 -0
- package/src/select-priority.ts +266 -0
- package/src/select.ts +62 -0
- package/src/session-path.ts +153 -0
- package/src/sidecar-apply.ts +216 -0
- package/src/sidecar.ts +700 -0
- package/src/slug-namespace.ts +367 -0
- package/src/spec-complete.ts +118 -0
- package/src/start.ts +974 -0
- package/src/status.ts +441 -0
- package/src/surface-gate.ts +337 -0
- package/src/surface-persist.ts +241 -0
- package/src/tasker-review-loop.ts +671 -0
- package/src/tasking-eligibility.ts +114 -0
- package/src/tasking-lock.ts +416 -0
- package/src/tasking.ts +1438 -0
- package/src/triage-gate.ts +248 -0
- package/src/triage-persist.ts +570 -0
- package/src/verdict-json.ts +73 -0
- package/src/verify-workflow-template.ts +159 -0
- package/src/verify.ts +123 -0
- package/src/watch-session.ts +397 -0
- package/src/work-layout.ts +262 -0
- package/src/work-on.ts +660 -0
- package/src/workspace.ts +502 -0
package/dist/tasking.js
ADDED
|
@@ -0,0 +1,952 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync, } from 'node:fs';
|
|
2
|
+
import { basename, dirname, join } from 'node:path';
|
|
3
|
+
import { parseFrontmatter } from './frontmatter.js';
|
|
4
|
+
import { runAsync } from './git.js';
|
|
5
|
+
import { workFolderRel, workFolderPath, workItemPath, workItemRel, isWorkItemFile, } from './work-layout.js';
|
|
6
|
+
import { performIntegration, } from './integration-core.js';
|
|
7
|
+
import { resolveTaskingEligibility, } from './tasking-eligibility.js';
|
|
8
|
+
import { acquireTaskingLock, releaseTaskingLock, } from './tasking-lock.js';
|
|
9
|
+
import { releaseItemLock } from './item-lock.js';
|
|
10
|
+
import { NullHarness } from './harness.js';
|
|
11
|
+
import { launchWithOptionalWatch } from './agent-launch.js';
|
|
12
|
+
import { placementFolder, resolvePlacement } from './placement.js';
|
|
13
|
+
import { setNeedsAnswersMarker, propagateOrigin } from './frontmatter.js';
|
|
14
|
+
import { workBranchRef } from './slug-namespace.js';
|
|
15
|
+
import { runTaskReviewLoop, } from './tasker-review-loop.js';
|
|
16
|
+
const DEFAULT_LOCK_SEAM = {
|
|
17
|
+
acquire: acquireTaskingLock,
|
|
18
|
+
release: releaseTaskingLock,
|
|
19
|
+
};
|
|
20
|
+
const DEFAULT_ARBITER = 'origin';
|
|
21
|
+
/**
|
|
22
|
+
* **The STAGED-TASKS dir** (prd `staging-pool-position-gate-and-trust-model`,
|
|
23
|
+
* task `pre-backlog-staging-folder-and-promote-step-a`, governing ADR
|
|
24
|
+
* `placement-is-runner-deterministic-humanonly-is-agent-judgement`). The runner
|
|
25
|
+
* lands the tasker's emitted task files HERE, NOT in `work/tasks/ready/`: an item
|
|
26
|
+
* born in `pre-backlog/` is durable + readable but NOT in the agent-eligible
|
|
27
|
+
* pool (`work/tasks/ready/` STILL means the pool — every reader is byte-for-byte
|
|
28
|
+
* unchanged). A runner/human-owned promotion (`promoteFromPreBacklog` in
|
|
29
|
+
* `needs-attention.ts`) moves an approved item `pre-backlog/ → backlog/` to make
|
|
30
|
+
* it claimable. STEP A: ADDITIVE — no `work/tasks/ready/` reader changes here.
|
|
31
|
+
*/
|
|
32
|
+
export const STAGED_TASKS_DIR = workFolderRel('tasks-backlog');
|
|
33
|
+
/**
|
|
34
|
+
* The POOL folder tasks land in when the runner-deterministic placement
|
|
35
|
+
* resolver chooses the pool side (`tasksLandIn: 'ready'` and a trusted
|
|
36
|
+
* origin, or an `--tasks-land-in ready` operator override). The agent NEVER
|
|
37
|
+
* writes here — it always writes to {@link STAGED_TASKS_DIR}; the runner
|
|
38
|
+
* redirects the emitted files to the resolved destination at integrate-stage
|
|
39
|
+
* time. prd US #4 / the governing ADR: the agent cannot self-place into the
|
|
40
|
+
* pool. Task `runner-deterministic-slice-placement-policy-and-precedence`.
|
|
41
|
+
*/
|
|
42
|
+
const POOL_TASKS_DIR = workFolderRel('tasks-ready');
|
|
43
|
+
/** The placement slots for the TASK lifecycle (folder names). */
|
|
44
|
+
const TASK_PLACEMENT_SLOTS = {
|
|
45
|
+
staging: STAGED_TASKS_DIR,
|
|
46
|
+
pool: POOL_TASKS_DIR,
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Map the `tasksLandIn` value spelling (`pre-backlog` | `ready`) onto the
|
|
50
|
+
* resolver's lifecycle-generic side enum (`staging` | `pool`). Returns
|
|
51
|
+
* `undefined` when no value is set, so the resolver's next precedence rung
|
|
52
|
+
* applies (the built-in floor). The legacy `'backlog'`/`'todo'` pool spellings
|
|
53
|
+
* are NOT accepted (clean break — the value was renamed `'backlog'` → `'todo'`
|
|
54
|
+
* → `'ready'`, ADR `rename-task-pool-folder-todo-to-ready`).
|
|
55
|
+
*/
|
|
56
|
+
function landingToSide(landing) {
|
|
57
|
+
if (landing === 'pre-backlog')
|
|
58
|
+
return 'staging';
|
|
59
|
+
if (landing === 'ready')
|
|
60
|
+
return 'pool';
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
/** The repo-relative path of a staged task's `.md` (per {@link STAGED_TASKS_DIR}). */
|
|
64
|
+
function stagedTaskPath(name) {
|
|
65
|
+
return `${STAGED_TASKS_DIR}/${name}`;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Run the `do prd:<slug>` tasking path end-to-end. Never throws for the expected
|
|
69
|
+
* gate-refused / lock-lost / agent-failed / stale cases — those are returned with
|
|
70
|
+
* the appropriate exit code and outcome. The runner owns all git; the agent only
|
|
71
|
+
* writes task files.
|
|
72
|
+
*/
|
|
73
|
+
export async function performTask(options) {
|
|
74
|
+
const note = options.note ?? (() => { });
|
|
75
|
+
const arbiter = options.arbiter ?? DEFAULT_ARBITER;
|
|
76
|
+
const cwd = options.cwd;
|
|
77
|
+
// `env` is the runner's GIT/provider env (identity-scoped). `agentEnv` is the
|
|
78
|
+
// AMBIENT env for AGENT launches (tasker agent, review/improver agents) — an
|
|
79
|
+
// agent must not act as the bot. Falls back to `env` when no identity.
|
|
80
|
+
const env = options.env;
|
|
81
|
+
const agentEnv = options.agentEnv ?? options.env;
|
|
82
|
+
const slug = options.slug;
|
|
83
|
+
const doer = options.doer ?? 'agent';
|
|
84
|
+
const lock = options.lock ?? DEFAULT_LOCK_SEAM;
|
|
85
|
+
// 0. The prd must exist in the checkout (`work/prds/ready/<slug>.md`) — it is the
|
|
86
|
+
// source the agent tasks + the file the lock holds.
|
|
87
|
+
const specPath = workItemPath(cwd, 'specs-ready', slug);
|
|
88
|
+
if (!existsSync(specPath)) {
|
|
89
|
+
const message = `no prd '${slug}' found at ${workFolderRel('specs-ready')}/${slug}.md.`;
|
|
90
|
+
note(message);
|
|
91
|
+
return { exitCode: 1, outcome: 'usage-error', slug, message };
|
|
92
|
+
}
|
|
93
|
+
const specContent = readFileSync(specPath, 'utf8');
|
|
94
|
+
const specFm = parseFrontmatter(specContent);
|
|
95
|
+
// 1. RESOLVE THE GATE (agent path only). The human path is UNBOUND — a human
|
|
96
|
+
// decides for themselves whether a prd is taskable.
|
|
97
|
+
if (doer === 'agent') {
|
|
98
|
+
const eligibility = resolveAgentGate(cwd, slug, specFm, options.autoTask, options.explicit ?? false);
|
|
99
|
+
if (!eligibility.taskable) {
|
|
100
|
+
const message = gateRefusalReason(slug, specFm, eligibility, options);
|
|
101
|
+
note(message);
|
|
102
|
+
return { exitCode: 1, outcome: 'gate-refused', slug, message };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// 2. ACQUIRE THE LOCK (agent path; concurrency serialisation). The human path
|
|
106
|
+
// with no contention may task on `main` directly WITHOUT the lock.
|
|
107
|
+
let lockedBlob;
|
|
108
|
+
const useLock = doer === 'agent';
|
|
109
|
+
if (useLock) {
|
|
110
|
+
const acquired = await lock.acquire({ slug, cwd, arbiter, env, note });
|
|
111
|
+
if (acquired.outcome === 'lost') {
|
|
112
|
+
return {
|
|
113
|
+
exitCode: 2,
|
|
114
|
+
outcome: 'lock-lost',
|
|
115
|
+
slug,
|
|
116
|
+
message: acquired.message,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (acquired.outcome === 'contended') {
|
|
120
|
+
return {
|
|
121
|
+
exitCode: 3,
|
|
122
|
+
outcome: 'lock-lost',
|
|
123
|
+
slug,
|
|
124
|
+
message: acquired.message,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (acquired.exitCode !== 0) {
|
|
128
|
+
return {
|
|
129
|
+
exitCode: 1,
|
|
130
|
+
outcome: 'usage-error',
|
|
131
|
+
slug,
|
|
132
|
+
message: acquired.message,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
lockedBlob = acquired.lockedBlob;
|
|
136
|
+
}
|
|
137
|
+
// 2b. ONBOARD the agent's tasking work onto a `work/<slug>` BRANCH cut from the
|
|
138
|
+
// freshly-fetched `<arbiter>/main` (task `slice-output-through-integration`).
|
|
139
|
+
// The prd body rests in `work/prds/ready/<slug>.md` on `<arbiter>/main` (the lock no
|
|
140
|
+
// longer moves it), so the branch's base HOLDS the held prd — the lifecycle
|
|
141
|
+
// stage below moves it `prd/ → prd-tasked/` ON THIS BRANCH and the shared integrate core (`--propose`
|
|
142
|
+
// PR / `--merge` main) lands the whole transition, WITHOUT the lock release
|
|
143
|
+
// committing tasks straight to `main`. The agent runs IN-PLACE on this branch
|
|
144
|
+
// (branch ≠ worktree; the isolation seam upgrades it). The HUMAN path stays on
|
|
145
|
+
// its own branch and commits its output itself (no integrate, no branch cut).
|
|
146
|
+
if (useLock) {
|
|
147
|
+
await switchToWorkBranch(cwd, arbiter, slug, env);
|
|
148
|
+
}
|
|
149
|
+
// 3. INVOKE THE AGENT with the to-task prd. It WRITES
|
|
150
|
+
// `work/tasks/backlog/*.md` task files (the STAGED area — NOT `work/tasks/ready/`,
|
|
151
|
+
// which is the agent-eligible pool the runner owns the promotion into; task
|
|
152
|
+
// `pre-backlog-staging-folder-and-promote-step-a`); it does NO git. We
|
|
153
|
+
// snapshot the staged-tasks folder before/after so the runner (not the
|
|
154
|
+
// agent) captures + commits exactly what was produced.
|
|
155
|
+
const before = snapshotStagedTasks(cwd);
|
|
156
|
+
// Also snapshot the POOL `work/tasks/ready/` BEFORE the agent runs: the runner's
|
|
157
|
+
// final commit must scrub any agent writes there (an attempt to self-place into
|
|
158
|
+
// the pool, prd US #4) before `git add -A` would sweep them in.
|
|
159
|
+
const poolBefore = snapshotPool(cwd);
|
|
160
|
+
// MIGRATE step (prd `prd-to-spec-vocabulary-cutover-and-migration-command`):
|
|
161
|
+
// read the parent-spec self-pointer off `prdFm.spec` (populated beside
|
|
162
|
+
// `prdFm.prd` by the expand task).
|
|
163
|
+
const prompt = buildTaskingSpec(slug, specFm.spec);
|
|
164
|
+
let agent;
|
|
165
|
+
try {
|
|
166
|
+
agent = await runTaskAgent(options, cwd, prompt, slug);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
agent = {
|
|
170
|
+
ok: false,
|
|
171
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (!agent.ok) {
|
|
175
|
+
const detail = agent.detail ?? `the agent failed to task '${slug}'.`;
|
|
176
|
+
const message = `Agent failed tasking '${slug}' (${detail}).`;
|
|
177
|
+
note(message);
|
|
178
|
+
// The lock stays held (the runner did not release it): a stuck tasking is
|
|
179
|
+
// recoverable / re-runnable. Surfacing it is the review/edit loop's job.
|
|
180
|
+
return { exitCode: 1, outcome: 'agent-failed', slug, message };
|
|
181
|
+
}
|
|
182
|
+
// 3.5 THE TASKER REVIEW→EDIT→CONVERGE LOOP (`slicer-review-edit-loop`, Shape 2 /
|
|
183
|
+
// insertion point A): when a loop seam is wired, run the `review` SKILL as a
|
|
184
|
+
// review→edit→re-review loop that IMPROVES the candidate tasks in place, then
|
|
185
|
+
// determines the disposition (the three outcomes). This plugs in AFTER the
|
|
186
|
+
// candidate tasks are produced and BEFORE they are finalised. The agent makes
|
|
187
|
+
// the review/edit JUDGEMENTS; the loop applies edits to the candidate files
|
|
188
|
+
// and routes the verdict; the runner (below) owns the git transition. Only the
|
|
189
|
+
// AGENT path runs the loop — the human tasking path is unaffected.
|
|
190
|
+
let loopDisposition;
|
|
191
|
+
if (options.reviewLoop && doer === 'agent') {
|
|
192
|
+
loopDisposition = await runTaskReviewLoop({
|
|
193
|
+
slug,
|
|
194
|
+
cwd,
|
|
195
|
+
gate: options.reviewLoop,
|
|
196
|
+
// SCOPING FENCE (the requeue fix): the loop reviews/edits/flags ONLY the
|
|
197
|
+
// tasks THIS run produced (new-or-changed vs `before`), never the
|
|
198
|
+
// pre-existing staged tasks that share `work/tasks/backlog/`.
|
|
199
|
+
before,
|
|
200
|
+
taskerLoopMax: options.taskerLoopMax ?? 3,
|
|
201
|
+
executions: options.reviewExecutions,
|
|
202
|
+
taskerLoopModel: options.taskerLoopModel,
|
|
203
|
+
sessionsDir: options.sessionsDir,
|
|
204
|
+
// The improver loop's review AGENT launches AMBIENT, never the identity.
|
|
205
|
+
env: agentEnv,
|
|
206
|
+
note,
|
|
207
|
+
});
|
|
208
|
+
// DECOMPOSITION UNCLEAR: emit NO guessed tasks — route the held prd to
|
|
209
|
+
// needs-attention with the questions as the reason. The lock release amends the
|
|
210
|
+
// `prd:<slug>` unified lock `active → stuck` (the tasking needs-attention surface
|
|
211
|
+
// is the stuck lock now — NO folder write; the prd body stays in `work/prds/ready/`).
|
|
212
|
+
if (loopDisposition.outcome === 'decomposition-unclear') {
|
|
213
|
+
const reason = decompositionUnclearReason(slug, loopDisposition.specQuestions);
|
|
214
|
+
if (useLock) {
|
|
215
|
+
const routed = await lock.release({
|
|
216
|
+
slug,
|
|
217
|
+
cwd,
|
|
218
|
+
arbiter,
|
|
219
|
+
lockedBlob,
|
|
220
|
+
routeToNeedsAttention: { reason },
|
|
221
|
+
env,
|
|
222
|
+
note,
|
|
223
|
+
});
|
|
224
|
+
if (routed.outcome !== 'released') {
|
|
225
|
+
return releaseFailureToResult(routed, slug);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
note(loopDisposition.message);
|
|
229
|
+
return {
|
|
230
|
+
exitCode: 1,
|
|
231
|
+
outcome: 'needs-attention',
|
|
232
|
+
slug,
|
|
233
|
+
message: loopDisposition.message,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
// UNCERTAIN TASKS: mark each named candidate `needsAnswers: true` + record
|
|
237
|
+
// its questions in the body, so it lands but is not agent-buildable. The
|
|
238
|
+
// runner writes the marker (the agent does no git/disk-escape).
|
|
239
|
+
if (loopDisposition.outcome === 'uncertain-tasks') {
|
|
240
|
+
for (const uncertain of loopDisposition.uncertainTasks) {
|
|
241
|
+
markTaskNeedsAnswers(cwd, uncertain.path, uncertain.questions, note);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// 4. The RUNNER commits the COMPLETING transition: drop the produced backlog
|
|
246
|
+
// tasks IN + move the prd tasking/ -> prd-tasked/ (residence = tasked-ness) — now
|
|
247
|
+
// through the SHARED integrate core (`--propose` PR / `--merge` main), NOT a
|
|
248
|
+
// direct commit to `main`. The agent never does git. (The backlog snapshot is
|
|
249
|
+
// taken AFTER any loop edits, so the runner integrates the IMPROVED tasks,
|
|
250
|
+
// not the pre-loop candidates.)
|
|
251
|
+
const stagedEmitted = newOrChangedStagedTasks(cwd, before);
|
|
252
|
+
const emitTasks = collectEmittedTasks(cwd, stagedEmitted);
|
|
253
|
+
// RUNNER-DETERMINISTIC PLACEMENT (task
|
|
254
|
+
// `runner-deterministic-slice-placement-policy-and-precedence`). Resolve which
|
|
255
|
+
// folder the runner lands the emitted tasks in BEFORE handing them to the
|
|
256
|
+
// shared integrate band: precedence `explicit > untrusted-origin ⇒ staging >
|
|
257
|
+
// tasksLandIn > built-in (staging)`, all from unforgeable inputs (the prd's
|
|
258
|
+
// stamped `originTrust:` + the resolved per-repo default + the operator's
|
|
259
|
+
// explicit flag). The agent NEVER influences this; it always writes to
|
|
260
|
+
// `work/tasks/backlog/`, and the runner redirects at `stage()` time.
|
|
261
|
+
const placementDecision = resolvePlacement({
|
|
262
|
+
explicit: landingToSide(options.explicitTasksLandIn),
|
|
263
|
+
originTrust: specFm.originTrust,
|
|
264
|
+
configuredDefault: landingToSide(options.tasksLandIn),
|
|
265
|
+
});
|
|
266
|
+
const placementDir = placementFolder(TASK_PLACEMENT_SLOTS, placementDecision.choice);
|
|
267
|
+
// REWRITE the emitted list to the RUNNER-RESOLVED destination so callers see
|
|
268
|
+
// where the runner actually placed the files (not where the agent wrote them).
|
|
269
|
+
const emitted = stagedEmitted.map((rel) => `${placementDir}/${basename(rel)}`);
|
|
270
|
+
const loopTag = loopDisposition?.outcome === 'converged'
|
|
271
|
+
? 'converged'
|
|
272
|
+
: loopDisposition?.outcome === 'uncertain-tasks'
|
|
273
|
+
? 'uncertain-tasks'
|
|
274
|
+
: undefined;
|
|
275
|
+
if (useLock) {
|
|
276
|
+
// READ-STABILITY BACKSTOP (the lock's content-identity check, now owned at the
|
|
277
|
+
// integrate seam): the OUTPUT no longer rides the lock release, so the band
|
|
278
|
+
// below would otherwise rebase a concurrent edit of the held prd body CLEANLY
|
|
279
|
+
// into prd/ (a rename+edit merge) while the tasks were cut from the OLD body —
|
|
280
|
+
// the exact silent stale-task drift the lock forbids
|
|
281
|
+
// (`work/notes/observations/tasking-lock-does-not-stabilise-prd-content.md`). So we
|
|
282
|
+
// compare the CURRENTLY held `work/prds/ready/<slug>.md` blob on the arbiter against
|
|
283
|
+
// the snapshot the lock TOOK (`lockedBlob`); ANY change ⇒ STALE ⇒ fail loud,
|
|
284
|
+
// touch NOTHING (the lock stays held; a human re-tasks or routes to
|
|
285
|
+
// needs-attention). It is the SAME content-identity check `releaseTaskingLock`
|
|
286
|
+
// runs — relocated here because this transition, not the release, owns the
|
|
287
|
+
// completing commit now.
|
|
288
|
+
const stale = await heldSpecIsStale(cwd, arbiter, slug, lockedBlob, env);
|
|
289
|
+
if (stale) {
|
|
290
|
+
const specRel = workItemRel('specs-ready', `${slug}.md`);
|
|
291
|
+
const message = `RELEASE CONFLICT for '${slug}': the prd was edited (${specRel} ` +
|
|
292
|
+
`changed on ${arbiter}/main) while the tasking lock was held. The tasking is ` +
|
|
293
|
+
`STALE — re-task from the edited prd or route it to needs-attention. ` +
|
|
294
|
+
`The arbiter was NOT modified (lock still held).`;
|
|
295
|
+
note(message);
|
|
296
|
+
return { exitCode: 4, outcome: 'stale', slug, message };
|
|
297
|
+
}
|
|
298
|
+
// Route the OUTPUT through the SHARED integrate back-half (task
|
|
299
|
+
// `slice-output-through-integration`): the produced backlog tasks + the prd
|
|
300
|
+
// lifecycle move (`work/prds/ready/ -> work/prds/tasked/`, residence = tasked-ness) integrate
|
|
301
|
+
// via `performIntegration` honoring `--propose` (push the work branch + open a
|
|
302
|
+
// PR, NO `main` touch) / `--merge` (land on `main`). Because the integrate-time
|
|
303
|
+
// args resolve ONCE in the shared core, every `do task:` arg applies here by
|
|
304
|
+
// construction. The agent did NO git; the runner (the band) owns the ONE commit.
|
|
305
|
+
const core = await performIntegration({
|
|
306
|
+
cwd,
|
|
307
|
+
arbiter,
|
|
308
|
+
slug,
|
|
309
|
+
// `source`/`recovering` are task-shaped and IGNORED when `lifecycle` is set
|
|
310
|
+
// (a tasking transition never recovers a surfaced needs-attention move).
|
|
311
|
+
source: 'in-progress',
|
|
312
|
+
recovering: false,
|
|
313
|
+
// Skip the build acceptance gate (Gate 1 / verify): a tasking transition has
|
|
314
|
+
// no `verify` floor (the tasker review loop above is its quality gate).
|
|
315
|
+
skipVerify: true,
|
|
316
|
+
// THE TASK-SET ACCEPTANCE GATE (task `slice-acceptance-gate`): the
|
|
317
|
+
// task-path mirror of the build Gate-2, riding THIS shared core's
|
|
318
|
+
// review-before-integrate block. When `review` resolves on, the wired
|
|
319
|
+
// `reviewGate` (production: `harnessTaskReviewGate` with the task-SET
|
|
320
|
+
// prompt) runs a FRESH-CONTEXT review of the produced task SET before it
|
|
321
|
+
// integrates: `approve` lands it, `block` routes the set to needs-attention
|
|
322
|
+
// via the SAME machinery the build block uses (mapped to the tasking
|
|
323
|
+
// `needs-attention` outcome below). It is ONE-SHOT: we pin
|
|
324
|
+
// `reviewMaxRounds: 1` so the gate is a SINGLE reviewer invocation → verdict
|
|
325
|
+
// (terminal pass/fail). The task path NEVER exposes/consults
|
|
326
|
+
// `--review-max-rounds` — a gate is terminal, the rounds bound is an orphan
|
|
327
|
+
// that belongs to a future revise↔review loop (see
|
|
328
|
+
// `work/notes/observations/reviewmaxrounds-on-wrong-concept.md`). This is
|
|
329
|
+
// independently controllable from the tasker improver loop (`reviewLoop` /
|
|
330
|
+
// the `--tasker-loop*` family); toggling one does not affect the other.
|
|
331
|
+
review: options.review,
|
|
332
|
+
reviewGate: options.reviewGate,
|
|
333
|
+
reviewModel: options.acceptanceReviewModel,
|
|
334
|
+
reviewMaxRounds: 1,
|
|
335
|
+
// The EXPLICITLY-chosen integrate mode proceeds AS-IS on an APPROVE — a
|
|
336
|
+
// `--merge` tasking run lands on main, `--propose` opens a PR. The tasking
|
|
337
|
+
// path's merge-vs-propose decision is the `integration` mode the user typed;
|
|
338
|
+
// `merge` IS the auto-land mode, so a resolved `merge` is never downgraded.
|
|
339
|
+
// The task gate family is `--review`/`--no-review`/`--review-model` only
|
|
340
|
+
// (prd US #6).
|
|
341
|
+
mode: options.integration ?? 'propose',
|
|
342
|
+
noPR: options.noPR,
|
|
343
|
+
providerInstance: options.providerInstance,
|
|
344
|
+
type: 'tasking',
|
|
345
|
+
lifecycle: {
|
|
346
|
+
// Read the PR title / commit summary from the held prd (before it moves).
|
|
347
|
+
titlePath: workItemPath(cwd, 'specs-ready', slug),
|
|
348
|
+
commitTag: 'tasked',
|
|
349
|
+
stage: () => stageTaskingLifecycle({
|
|
350
|
+
cwd,
|
|
351
|
+
slug,
|
|
352
|
+
emitTasks,
|
|
353
|
+
poolBefore,
|
|
354
|
+
placementDir,
|
|
355
|
+
placementReason: placementDecision.reason,
|
|
356
|
+
note,
|
|
357
|
+
env,
|
|
358
|
+
}),
|
|
359
|
+
},
|
|
360
|
+
env,
|
|
361
|
+
// The task-SET acceptance review AGENT launches AMBIENT, never the
|
|
362
|
+
// identity-scoped `env` (an agent must not act as the bot).
|
|
363
|
+
agentEnv,
|
|
364
|
+
note,
|
|
365
|
+
});
|
|
366
|
+
// THE TASK-SET ACCEPTANCE GATE BLOCKED (task `slice-acceptance-gate`): the
|
|
367
|
+
// fresh-context review of the produced SET returned `block`, so the core ran
|
|
368
|
+
// the review BEFORE the stage/integrate and did NOT integrate the tasks
|
|
369
|
+
// (correct). The CORRECT task-path destination is the SAME needs-attention
|
|
370
|
+
// route the lock release owns for the decomposition-unclear verdict: it amends
|
|
371
|
+
// the `prd:<slug>` unified lock `active -> stuck` with the block reason (the
|
|
372
|
+
// tasking needs-attention surface is the stuck lock now — NO folder write; the
|
|
373
|
+
// prd body stays in `work/prds/ready/`). So on a block we route the held prd to
|
|
374
|
+
// needs-attention THROUGH the lock release — the set never lands.
|
|
375
|
+
if (core.outcome === 'review-blocked') {
|
|
376
|
+
const reason = taskGateBlockedReason(slug, core.reviewBlockReason);
|
|
377
|
+
const routed = await lock.release({
|
|
378
|
+
slug,
|
|
379
|
+
cwd,
|
|
380
|
+
arbiter,
|
|
381
|
+
lockedBlob,
|
|
382
|
+
routeToNeedsAttention: { reason },
|
|
383
|
+
env,
|
|
384
|
+
note,
|
|
385
|
+
});
|
|
386
|
+
if (routed.outcome !== 'released') {
|
|
387
|
+
return releaseFailureToResult(routed, slug);
|
|
388
|
+
}
|
|
389
|
+
note(reason);
|
|
390
|
+
return {
|
|
391
|
+
exitCode: 1,
|
|
392
|
+
outcome: 'needs-attention',
|
|
393
|
+
slug,
|
|
394
|
+
message: `The task acceptance gate blocked the set produced for '${slug}'; ` +
|
|
395
|
+
`marked the per-item lock stuck (needs attention; no tasks landed).`,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
if (core.outcome === 'review-unparseable') {
|
|
399
|
+
// The task-set acceptance gate RAN but its verdict was UNPARSEABLE (malformed
|
|
400
|
+
// JSON). Route the held prd to needs-attention through the SAME lock-release
|
|
401
|
+
// seam the block path uses (the tasking needs-attention surface is the stuck
|
|
402
|
+
// `prd:<slug>` lock; no folder write). It is NOT a block (the gate output was
|
|
403
|
+
// unreadable) — record it as the transient-infra-class re-run signal so the
|
|
404
|
+
// stuck reason reads correctly; nothing landed.
|
|
405
|
+
const reason = `The task acceptance gate for '${slug}' produced an UNPARSEABLE verdict ` +
|
|
406
|
+
`(re-run — transient): ${core.reason ?? ''}`;
|
|
407
|
+
const routed = await lock.release({
|
|
408
|
+
slug,
|
|
409
|
+
cwd,
|
|
410
|
+
arbiter,
|
|
411
|
+
lockedBlob,
|
|
412
|
+
routeToNeedsAttention: { reason },
|
|
413
|
+
env,
|
|
414
|
+
note,
|
|
415
|
+
});
|
|
416
|
+
if (routed.outcome !== 'released') {
|
|
417
|
+
return releaseFailureToResult(routed, slug);
|
|
418
|
+
}
|
|
419
|
+
note(reason);
|
|
420
|
+
return {
|
|
421
|
+
exitCode: 1,
|
|
422
|
+
outcome: 'needs-attention',
|
|
423
|
+
slug,
|
|
424
|
+
message: `The task acceptance gate produced an unparseable verdict for '${slug}'; ` +
|
|
425
|
+
`marked the per-item lock stuck (needs attention; no tasks landed; re-run).`,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
if (core.outcome === 'completed') {
|
|
429
|
+
// The durable `prd → prd-tasked` `main` move landed through the shared integrate
|
|
430
|
+
// core (the body moved straight from `work/prds/ready/` — no transient `tasking/`
|
|
431
|
+
// marker). The completing commit is owned by the integrate band, NOT
|
|
432
|
+
// `releaseTaskingLock`, so the unified per-item lock that `acquireTaskingLock`
|
|
433
|
+
// took is released HERE (delete the ref). A
|
|
434
|
+
// `propose` (`mode: 'propose'`) is ALSO `completed` (the PR opened, the lock's
|
|
435
|
+
// hold over the in-flight tasking is done); the eventual hold-across-the-PR
|
|
436
|
+
// crash-safe ordering is the capstone task #7's concern, not this interim
|
|
437
|
+
// half. Best-effort + idempotent (`not-held` is fine).
|
|
438
|
+
// MIGRATE step: release under the `spec:<slug>` identity (keyed to the
|
|
439
|
+
// `spec-<slug>` lock entry the acquire now takes). Idempotent.
|
|
440
|
+
if (useLock) {
|
|
441
|
+
await releaseItemLock({ item: `spec:${slug}`, cwd, arbiter, env });
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return integrationToTaskResult(core, { slug, emitted, loop: loopTag });
|
|
445
|
+
}
|
|
446
|
+
// HUMAN, no-lock path: the human commits on `main` directly (the runner does
|
|
447
|
+
// not own the human's git). We report the produced tasks; moving the prd into
|
|
448
|
+
// `work/prds/tasked/` (residence = tasked-ness) and committing is the human's to
|
|
449
|
+
// do, as with the human `complete`.
|
|
450
|
+
const message = `Tasked '${slug}' -> ${emitted.length} backlog task` +
|
|
451
|
+
`${emitted.length === 1 ? '' : 's'} (human path, no lock). Inspect + commit ` +
|
|
452
|
+
`the produced files (and move the prd into work/prds/tasked/) yourself.`;
|
|
453
|
+
note(message);
|
|
454
|
+
return {
|
|
455
|
+
exitCode: 0,
|
|
456
|
+
outcome: 'tasked',
|
|
457
|
+
slug,
|
|
458
|
+
emitted,
|
|
459
|
+
loop: loopTag,
|
|
460
|
+
message,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Map a non-`released` lock-release result onto the {@link TaskResult} contract
|
|
465
|
+
* (the decomposition-unclear routing reuses the SAME release seam, so it can also
|
|
466
|
+
* be `stale`/`lost`/`contended`/usage-error). Mirrors the step-4 release mapping.
|
|
467
|
+
*/
|
|
468
|
+
function releaseFailureToResult(released, slug) {
|
|
469
|
+
if (released.outcome === 'stale') {
|
|
470
|
+
return { exitCode: 4, outcome: 'stale', slug, message: released.message };
|
|
471
|
+
}
|
|
472
|
+
if (released.outcome === 'lost' || released.outcome === 'contended') {
|
|
473
|
+
const code = released.outcome === 'lost' ? 2 : 3;
|
|
474
|
+
return {
|
|
475
|
+
exitCode: code,
|
|
476
|
+
outcome: 'lock-lost',
|
|
477
|
+
slug,
|
|
478
|
+
message: released.message,
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
return { exitCode: 1, outcome: 'usage-error', slug, message: released.message };
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Map the shared integrate band's {@link IntegrationCoreResult} onto the tasking
|
|
485
|
+
* {@link TaskResult} (task `slice-output-through-integration`). On `completed`
|
|
486
|
+
* (propose pushed the work branch + opened a PR / merge landed on `main`) the
|
|
487
|
+
* tasking is `tasked`. The band's FAILURE outcomes are reported on the tasking
|
|
488
|
+
* contract: a `rebase-conflict` against a concurrently-advanced `main` maps to
|
|
489
|
+
* `stale` (exit 4) — the tasking analogue of "the held prd moved under us"; a
|
|
490
|
+
* a `gate-failed` cannot occur (the tasking path passes `skipVerify`) but maps to
|
|
491
|
+
* a usage error defensively. A `review-blocked` (the task-SET ACCEPTANCE GATE
|
|
492
|
+
* blocked the set, task `slice-acceptance-gate`) is handled by `performTask`
|
|
493
|
+
* BEFORE this mapper — it routes the held prd to needs-attention via
|
|
494
|
+
* the lock release (the task-path needs-attention route) — so it never reaches
|
|
495
|
+
* here; it is mapped defensively to a usage error if it ever does.
|
|
496
|
+
*/
|
|
497
|
+
function integrationToTaskResult(core, ctx) {
|
|
498
|
+
const { slug, emitted, loop } = ctx;
|
|
499
|
+
if (core.outcome === 'completed') {
|
|
500
|
+
const landed = core.integration?.mode === 'merge'
|
|
501
|
+
? 'landed them on the arbiter main'
|
|
502
|
+
: 'opened a PR carrying them (main untouched)';
|
|
503
|
+
const message = `Tasked '${slug}' -> ${emitted.length} backlog task` +
|
|
504
|
+
`${emitted.length === 1 ? '' : 's'}; the runner integrated the transition ` +
|
|
505
|
+
`through the shared core (moved work/prds/ready/ -> work/prds/tasked/, the ` +
|
|
506
|
+
`tasked resting state) and ${landed}.`;
|
|
507
|
+
return { exitCode: 0, outcome: 'tasked', slug, emitted, loop, message };
|
|
508
|
+
}
|
|
509
|
+
if (core.outcome === 'rebase-conflict') {
|
|
510
|
+
return {
|
|
511
|
+
exitCode: 4,
|
|
512
|
+
outcome: 'stale',
|
|
513
|
+
slug,
|
|
514
|
+
message: core.reason ??
|
|
515
|
+
`Integrating the tasking of '${slug}' conflicted against the latest ` +
|
|
516
|
+
`${slug} main — the tasking is stale; re-task from the current prd.`,
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
exitCode: 1,
|
|
521
|
+
outcome: 'usage-error',
|
|
522
|
+
slug,
|
|
523
|
+
message: core.reason ??
|
|
524
|
+
`Integrating the tasking of '${slug}' failed unexpectedly.`,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* ONBOARD the tasking work onto a `work/<slug>` branch cut from the freshly-
|
|
529
|
+
* fetched `<arbiter>/main` (task `slice-output-through-integration`). Called
|
|
530
|
+
* AFTER the tasking lock is held, so the branch's base HOLDS the prd in
|
|
531
|
+
* `work/prds/ready/` (the lock no longer moves the body) — the lifecycle stage then moves
|
|
532
|
+
* it `prd/ -> prd-tasked/` ON THIS BRANCH and the shared integrate core lands it. A
|
|
533
|
+
* pre-existing local `work/<slug>` (a re-run) is force-recreated off fresh main.
|
|
534
|
+
* The agent runs in-place on this branch (branch ≠ worktree).
|
|
535
|
+
*/
|
|
536
|
+
async function switchToWorkBranch(cwd, arbiter, slug, env) {
|
|
537
|
+
// The tasking path is the parent-spec namespace (`do spec:<slug>`): the branch
|
|
538
|
+
// is `work/spec-<slug>`, distinct from a same-slug task-build's `work/task-<slug>`.
|
|
539
|
+
// MIGRATE step (prd `prd-to-spec-vocabulary-cutover-and-migration-command`):
|
|
540
|
+
// MINT the work-branch under the `spec` namespace token (`workBranchRef` still
|
|
541
|
+
// parses the legacy `work/prd-<slug>` form, so in-flight branches keep resolving).
|
|
542
|
+
const branch = workBranchRef('spec', slug);
|
|
543
|
+
await gitHard(['fetch', '--quiet', arbiter], cwd, env);
|
|
544
|
+
await gitHard(['switch', '--quiet', '-C', branch, `${arbiter}/main`], cwd, env);
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* The READ-STABILITY content-identity STALE CHECK (the lock's backstop, owned at
|
|
548
|
+
* the integrate seam now that the OUTPUT no longer rides the lock release): true
|
|
549
|
+
* iff the CURRENTLY held `work/prds/ready/<slug>.md` blob on `<arbiter>/main` DIFFERS
|
|
550
|
+
* from the snapshot the lock TOOK (`lockedBlob`, read from `work/prds/ready/<slug>.md` at
|
|
551
|
+
* acquire). ANY change = a concurrent edit under the lock = the tasking is STALE.
|
|
552
|
+
* Stronger than a textual rebase conflict (which a rename+edit merge can apply
|
|
553
|
+
* CLEANLY). When `lockedBlob` is absent (never, in production) it reads as
|
|
554
|
+
* not-stale (the lock acquire always returns it).
|
|
555
|
+
*/
|
|
556
|
+
async function heldSpecIsStale(cwd, arbiter, slug, lockedBlob, env) {
|
|
557
|
+
if (lockedBlob === undefined) {
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
560
|
+
await gitHard(['fetch', '--quiet', arbiter], cwd, env);
|
|
561
|
+
const held = await gitSoft(['rev-parse', `${arbiter}/main:${workFolderRel('specs-ready')}/${slug}.md`], cwd, env);
|
|
562
|
+
// The prd being absent (already tasked/moved) is NOT this check's concern
|
|
563
|
+
// (the integrate's rebase/push surfaces that); only a CHANGED held blob is stale.
|
|
564
|
+
if (held.status !== 0) {
|
|
565
|
+
return false;
|
|
566
|
+
}
|
|
567
|
+
return held.stdout.trim() !== lockedBlob;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* STAGE the tasking lifecycle into the index on the `work/<slug>` branch (the
|
|
571
|
+
* {@link performIntegration} lifecycle seam): move the held prd
|
|
572
|
+
* `git mv work/prds/ready/<slug>.md -> work/prds/tasked/<slug>.md` (the TASKED resting
|
|
573
|
+
* state — the build-machine `done/` analogue, the SOURCE OF TRUTH for tasked-ness),
|
|
574
|
+
* and write+`git add` the produced `work/tasks/backlog/*.md` files. The band's subsequent
|
|
575
|
+
* `git add -A` + atomic commit folds this AND the agent's uncommitted backlog writes
|
|
576
|
+
* into ONE runner-owned commit (the agent never does git).
|
|
577
|
+
*
|
|
578
|
+
* TASK `prd-sliced-folder-step-a` (prd `slicing-coherence` US #8): the lifecycle
|
|
579
|
+
* destination is `work/prds/tasked/` (NOT back to `work/prds/ready/`) — `prd-tasked/`
|
|
580
|
+
* residence IS tasked-ness (like `done/` for tasks, with no `done:` marker). The
|
|
581
|
+
* `tasked:` frontmatter marker was removed entirely in `remove-sliced-marker-step-b`
|
|
582
|
+
* (sequenced last): residence in `work/prds/tasked/` is now the sole signal.
|
|
583
|
+
*/
|
|
584
|
+
async function stageTaskingLifecycle(params) {
|
|
585
|
+
const { cwd, slug, emitTasks, poolBefore, placementDir, placementReason, note, env, } = params;
|
|
586
|
+
const spec = workItemRel('specs-ready', `${slug}.md`);
|
|
587
|
+
const specTasked = workItemRel('specs-tasked', `${slug}.md`);
|
|
588
|
+
// PROPAGATE the origin-trust PROVENANCE (task
|
|
589
|
+
// `untrusted-origin-forces-build-propose`): read the held prd's `origin`/
|
|
590
|
+
// `originTrust` stamp BEFORE the move, so each emitted task can carry it. A
|
|
591
|
+
// task's risk is its BUILD; the stamp must reach the task so the build
|
|
592
|
+
// transition can force `propose` for untrusted-origin work. An UNSTAMPED prd (a
|
|
593
|
+
// human/local-authored one ⇒ trusted) propagates nothing — the normal path is
|
|
594
|
+
// untouched.
|
|
595
|
+
const specAbs = join(cwd, spec);
|
|
596
|
+
const specProvenance = existsSync(specAbs)
|
|
597
|
+
? parseFrontmatter(readFileSync(specAbs, 'utf8'))
|
|
598
|
+
: { origin: undefined, originTrust: undefined };
|
|
599
|
+
if (placementReason === 'untrusted-origin') {
|
|
600
|
+
note(`Untrusted-origin prd '${slug}': forcing the emitted tasks STAGED ` +
|
|
601
|
+
`(${placementDir}/) regardless of tasksLandIn (a human promotes ` +
|
|
602
|
+
'them into work/tasks/ready/). Pass --tasks-land-in <where> to override.');
|
|
603
|
+
}
|
|
604
|
+
// Move the held prd prd/ -> prd-tasked/ (the TASKED resting state — folder =
|
|
605
|
+
// source of truth, like done/ for tasks). This is the DURABLE `prd → prd-tasked`
|
|
606
|
+
// success move, owned by THIS transition's commit (the lock no longer moved the
|
|
607
|
+
// body, so the source is `work/prds/ready/`, never `work/tasking/`).
|
|
608
|
+
mkdirSync(dirname(join(cwd, specTasked)), { recursive: true });
|
|
609
|
+
await gitHard(['mv', spec, specTasked], cwd, env);
|
|
610
|
+
await gitHard(['add', '--', specTasked], cwd, env);
|
|
611
|
+
// **POOL-PLACEMENT FENCE (prd US #4 / governing ADR
|
|
612
|
+
// `placement-is-runner-deterministic-humanonly-is-agent-judgement`).** The
|
|
613
|
+
// agent ALWAYS writes to the STAGING folder (`work/tasks/backlog/`); the POOL
|
|
614
|
+
// (`work/tasks/ready/`) is the agent-eligible pool the runner owns the promotion
|
|
615
|
+
// into. Anything the agent dropped under the pool would otherwise be swept in
|
|
616
|
+
// by `performIntegration`'s subsequent `git add -A` — a self-placement into
|
|
617
|
+
// the pool. Scrub it FIRST (before the runner writes its resolved destination
|
|
618
|
+
// files below), so when the runner-deterministic placement resolves to the
|
|
619
|
+
// pool the runner's writes are the ONLY legitimate pool entries in the commit.
|
|
620
|
+
await scrubPoolDrift(cwd, poolBefore, env);
|
|
621
|
+
// Drop the produced backlog tasks IN at the RUNNER-RESOLVED destination
|
|
622
|
+
// (write + stage; the band's `git add -A` also catches them, but staging here
|
|
623
|
+
// keeps the transition explicit + atomic). The runner STAMPS the propagated
|
|
624
|
+
// provenance onto each task as it writes it (the agent does no git; the runner
|
|
625
|
+
// owns the file write here). When the resolved destination DIFFERS from where
|
|
626
|
+
// the agent wrote (the staging folder), remove the agent's source file too —
|
|
627
|
+
// otherwise `git add -A` would commit BOTH (staging twin + pool destination).
|
|
628
|
+
for (const [agentRel, content] of Object.entries(emitTasks)) {
|
|
629
|
+
const filename = basename(agentRel);
|
|
630
|
+
const destRel = `${placementDir}/${filename}`;
|
|
631
|
+
const destAbs = join(cwd, destRel);
|
|
632
|
+
mkdirSync(dirname(destAbs), { recursive: true });
|
|
633
|
+
writeFileSync(destAbs, propagateOrigin(specProvenance, content));
|
|
634
|
+
await gitHard(['add', '--', destRel], cwd, env);
|
|
635
|
+
if (destRel !== agentRel) {
|
|
636
|
+
const srcAbs = join(cwd, agentRel);
|
|
637
|
+
rmSync(srcAbs, { force: true });
|
|
638
|
+
// Also unstage if `git add --` from a previous run picked it up;
|
|
639
|
+
// untracked-and-now-gone is fine.
|
|
640
|
+
await gitSoft(['rm', '-f', '--quiet', '--', agentRel], cwd, env);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Revert any change/addition the agent made to the POOL `work/tasks/ready/` during a
|
|
646
|
+
* tasking run. The agent's STAGING folder is `work/tasks/backlog/`; a write to the
|
|
647
|
+
* pool is an attempt to self-place into the agent-eligible pool the runner owns
|
|
648
|
+
* the promotion into (prd US #4 / governing ADR). Compared to the `poolBefore`
|
|
649
|
+
* snapshot (the branch-base state of `work/tasks/ready/`, taken BEFORE the agent
|
|
650
|
+
* ran), any new file is removed from the worktree and any changed file is
|
|
651
|
+
* checked back out to HEAD — so the subsequent `git add -A` cannot land it. The
|
|
652
|
+
* runner's commit then carries ONLY the explicit `pre-backlog/` placement.
|
|
653
|
+
*/
|
|
654
|
+
async function scrubPoolDrift(cwd, poolBefore, env) {
|
|
655
|
+
const dir = workFolderPath(cwd, 'tasks-ready');
|
|
656
|
+
let entries;
|
|
657
|
+
try {
|
|
658
|
+
entries = readdirSync(dir);
|
|
659
|
+
}
|
|
660
|
+
catch {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
for (const name of entries) {
|
|
664
|
+
if (!isWorkItemFile(name)) {
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
const abs = join(dir, name);
|
|
668
|
+
const content = readFileSync(abs, 'utf8');
|
|
669
|
+
if (poolBefore.has(name)) {
|
|
670
|
+
if (poolBefore.get(name) === content) {
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
// The agent edited a pre-existing pool task — restore it from HEAD.
|
|
674
|
+
await gitSoft(['checkout', 'HEAD', '--', workItemRel('tasks-ready', name)], cwd, env);
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
// The agent introduced a NEW file in the pool: drop it.
|
|
678
|
+
rmSync(abs, { force: true });
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
/** Run git, returning the raw result (no throw) — for soft checks. */
|
|
682
|
+
function gitSoft(args, cwd, env) {
|
|
683
|
+
return runAsync('git', args, cwd, { env });
|
|
684
|
+
}
|
|
685
|
+
/** Run git; throw on non-zero (genuinely unexpected plumbing failures). */
|
|
686
|
+
async function gitHard(args, cwd, env) {
|
|
687
|
+
const result = await runAsync('git', args, cwd, { env });
|
|
688
|
+
if (result.status !== 0) {
|
|
689
|
+
throw new Error(`git ${args.join(' ')} failed (exit ${result.status}): ${result.stderr.trim()}`);
|
|
690
|
+
}
|
|
691
|
+
return result;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Build the needs-attention REASON for a task-SET ACCEPTANCE GATE block (task
|
|
695
|
+
* `slice-acceptance-gate`): the fresh-context review of the produced set returned
|
|
696
|
+
* `block`, so the prd is routed to `work/needs-attention/` with the review's
|
|
697
|
+
* blocking findings as the body prose and NO tasks landed. Takes the core's
|
|
698
|
+
* structured `reviewBlockReason` (the gate's blocking findings); falls back to a
|
|
699
|
+
* generic line when absent. DISTINCT from the improver loop's
|
|
700
|
+
* {@link decompositionUnclearReason} (which carries the loop's open questions).
|
|
701
|
+
*/
|
|
702
|
+
function taskGateBlockedReason(slug, findingsReason) {
|
|
703
|
+
const head = `The task acceptance gate (fresh-context review of the produced SET) blocked ` +
|
|
704
|
+
`'${slug}'. The prd is routed to needs-attention with no tasks landed; a human ` +
|
|
705
|
+
`must resolve the blocking findings, then re-task.`;
|
|
706
|
+
return findingsReason ? `${head}\n\n${findingsReason}` : head;
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Build the needs-attention REASON for a decomposition-unclear loop verdict (the
|
|
710
|
+
* prd is routed to `work/needs-attention/` with these open questions, no guessed
|
|
711
|
+
* tasks). Prose only — recorded as the prd's needs-attention body block.
|
|
712
|
+
*/
|
|
713
|
+
function decompositionUnclearReason(slug, questions) {
|
|
714
|
+
const head = `The tasker review→edit loop could not converge on a sound decomposition of ` +
|
|
715
|
+
`'${slug}' (--tasker-loop-max exhausted with unresolved blockers). The prd is routed ` +
|
|
716
|
+
`to needs-attention with no guessed tasks; a human must resolve:`;
|
|
717
|
+
const body = questions.length > 0
|
|
718
|
+
? questions.map((q) => `- ${q}`).join('\n')
|
|
719
|
+
: '- (no specific questions surfaced; the decomposition is broadly unclear)';
|
|
720
|
+
return `${head}\n${body}`;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Mark a candidate task file `needsAnswers: true` and record its open questions in
|
|
724
|
+
* its body (the loop's uncertain-task routing outcome). The runner writes the
|
|
725
|
+
* file; the agent does no git/disk-escape. A path outside `work/tasks/backlog/`
|
|
726
|
+
* is skipped (defensive). A relative `work/tasks/backlog/<slug>.md` that does not
|
|
727
|
+
* exist is skipped with a note (never crash the transition).
|
|
728
|
+
*/
|
|
729
|
+
function markTaskNeedsAnswers(cwd, relPath, questions, note) {
|
|
730
|
+
const normalized = relPath.replace(/\\/g, '/');
|
|
731
|
+
if (!normalized.startsWith(`${STAGED_TASKS_DIR}/`) ||
|
|
732
|
+
normalized.includes('..')) {
|
|
733
|
+
note(`Skipped a needsAnswers mark outside ${STAGED_TASKS_DIR}/ (${relPath}).`);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
const abs = join(cwd, normalized);
|
|
737
|
+
if (!existsSync(abs)) {
|
|
738
|
+
note(`Skipped a needsAnswers mark for missing candidate task ${relPath}.`);
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
const current = readFileSync(abs, 'utf8');
|
|
742
|
+
const marked = setNeedsAnswersMarker(current, true);
|
|
743
|
+
writeFileSync(abs, appendQuestionsBlock(marked, questions));
|
|
744
|
+
}
|
|
745
|
+
/** The heading that opens the open-questions block in an uncertain task body. */
|
|
746
|
+
const OPEN_QUESTIONS_HEADING = '## Open questions';
|
|
747
|
+
/**
|
|
748
|
+
* Append an `## Open questions` block (prose, never a frontmatter field —
|
|
749
|
+
* WORK-CONTRACT rule 3) listing the loop's surfaced questions to an uncertain
|
|
750
|
+
* task's body. A human answers these before the task becomes agent-buildable.
|
|
751
|
+
*/
|
|
752
|
+
function appendQuestionsBlock(content, questions) {
|
|
753
|
+
if (questions.length === 0) {
|
|
754
|
+
return content;
|
|
755
|
+
}
|
|
756
|
+
const base = content.replace(/\s*$/, '');
|
|
757
|
+
const items = questions.map((q) => `- ${q}`).join('\n');
|
|
758
|
+
return [base, '', OPEN_QUESTIONS_HEADING, '', items, ''].join('\n');
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Resolve the AGENT tasking gate for `slug`: the pure predicate
|
|
762
|
+
* (`needsAnswers !== true && humanOnly !== true && autoTask`) plus the
|
|
763
|
+
* cross-prd `taskedAfter` ordering, resolved against `work/prds/tasked/` residence of
|
|
764
|
+
* the prds present in the checkout.
|
|
765
|
+
*/
|
|
766
|
+
function resolveAgentGate(cwd, slug, specFm, autoTask, explicit) {
|
|
767
|
+
return resolveTaskingEligibility({
|
|
768
|
+
humanOnly: specFm.humanOnly,
|
|
769
|
+
needsAnswers: specFm.needsAnswers,
|
|
770
|
+
taskedAfter: specFm.taskedAfter,
|
|
771
|
+
taskedSlugs: readTaskedSlugs(cwd),
|
|
772
|
+
autoTask: autoTask ?? false,
|
|
773
|
+
explicit,
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
/** Build an HONEST gate-refusal message naming WHY the agent skipped the prd. */
|
|
777
|
+
function gateRefusalReason(slug, specFm, eligibility, options) {
|
|
778
|
+
const reasons = [];
|
|
779
|
+
if (specFm.humanOnly === true) {
|
|
780
|
+
reasons.push('the prd is humanOnly (a human must drive its tasking)');
|
|
781
|
+
}
|
|
782
|
+
if (specFm.needsAnswers === true) {
|
|
783
|
+
reasons.push('the prd has needsAnswers (open questions block auto-tasking)');
|
|
784
|
+
}
|
|
785
|
+
// The autoTask POLICY only refuses on the NON-explicit (auto-pick pool) path:
|
|
786
|
+
// an explicitly-named `do prd:<slug>` is authorized by the naming itself (the
|
|
787
|
+
// build path's autoBuild precedent), so the policy is never the reason there.
|
|
788
|
+
if (options.explicit !== true &&
|
|
789
|
+
specFm.humanOnly !== true &&
|
|
790
|
+
specFm.needsAnswers !== true &&
|
|
791
|
+
(options.autoTask ?? false) !== true) {
|
|
792
|
+
reasons.push("the repo's autoTask policy is off");
|
|
793
|
+
}
|
|
794
|
+
if (!eligibility.taskedAfter.satisfied) {
|
|
795
|
+
reasons.push(`taskedAfter prd(s) not yet tasked: ${eligibility.taskedAfter.missing.join(', ')}`);
|
|
796
|
+
}
|
|
797
|
+
const why = reasons.length > 0 ? reasons.join('; ') : 'the tasking gate refused';
|
|
798
|
+
return `Skipped tasking '${slug}': ${why}.`;
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Read the set of slugs whose prds are already TASKED in this checkout — RESIDENCE
|
|
802
|
+
* in `work/prds/tasked/` (the tasked resting state, task `prd-sliced-folder-step-a`
|
|
803
|
+
* / prd `slicing-coherence` US #9), the build-machine `done/` analogue. The FOLDER
|
|
804
|
+
* is the source of truth; the `tasked:` frontmatter marker was removed entirely in
|
|
805
|
+
* `remove-sliced-marker-step-b` and is NOT consulted. So `taskedAfter` resolves
|
|
806
|
+
* against `prd-tasked/` residence
|
|
807
|
+
* (mirroring `blockedBy` -> `done/`). A missing folder reads as empty. The slug is
|
|
808
|
+
* read from each file's frontmatter `slug:`, falling back to the filename — the same
|
|
809
|
+
* shape the task readers use.
|
|
810
|
+
*/
|
|
811
|
+
function readTaskedSlugs(cwd) {
|
|
812
|
+
const slugs = new Set();
|
|
813
|
+
const dir = workFolderPath(cwd, 'specs-tasked');
|
|
814
|
+
for (const file of listMarkdown(dir)) {
|
|
815
|
+
const content = readFileSync(join(dir, file), 'utf8');
|
|
816
|
+
const fm = parseFrontmatter(content);
|
|
817
|
+
slugs.add(fm.slug ?? file.replace(/\.md$/i, ''));
|
|
818
|
+
}
|
|
819
|
+
return slugs;
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Build the tasking PROMPT: instruct a fresh-context agent to apply the
|
|
823
|
+
* **tasking discipline** (`work/protocol/TASKING-PROTOCOL.md`) to the held
|
|
824
|
+
* prd at `work/prds/ready/<slug>.md` and to EMIT tracer-bullet vertical
|
|
825
|
+
* tasks under `work/tasks/backlog/`. The discipline body (the tracer-bullet
|
|
826
|
+
* rules, the two-axis gate guidance, the confidence check, file-orthogonality,
|
|
827
|
+
* the emitted task shape) lives in `TASKING-PROTOCOL.md` — NOT inlined here
|
|
828
|
+
* (task `slicing-protocol-doc-and-vocabulary-fix`). The shape's source of
|
|
829
|
+
* truth is the frontmatter parser + the task template (`work/protocol/
|
|
830
|
+
* task-template.md`); the doc DESCRIBES it (D2).
|
|
831
|
+
*
|
|
832
|
+
* This builder owns ONLY the PER-BUILDER framing: who you are (a fresh-context
|
|
833
|
+
* tasker for ONE prd), where the held prd is, where the emitted task files
|
|
834
|
+
* MUST land (the staging folder, never the pool), and the runner-owns-git
|
|
835
|
+
* boundary on the agent path. The shared discipline body is NOT duplicated
|
|
836
|
+
* here.
|
|
837
|
+
*/
|
|
838
|
+
function buildTaskingSpec(slug, _spec) {
|
|
839
|
+
return [
|
|
840
|
+
`You are a FRESH-CONTEXT tasker for the prd \`work/prds/ready/${slug}.md\`.`,
|
|
841
|
+
`Apply the tasking discipline defined in \`work/protocol/TASKING-PROTOCOL.md\``,
|
|
842
|
+
`(the in-band, protocol-native tasking protocol every set-up repo carries; the`,
|
|
843
|
+
`human-facing pointer is \`skills/to-task/SKILL.md\`) to this ONE prd:`,
|
|
844
|
+
`decompose it into independently-grabbable, tracer-bullet vertical tasks.`,
|
|
845
|
+
`Read the prd fully first.`,
|
|
846
|
+
``,
|
|
847
|
+
`The discipline rules — the tracer-bullet test, the two-axis gate guidance`,
|
|
848
|
+
`(\`humanOnly\` is NARROW; \`needsAnswers\` flags genuine uncertainty), the`,
|
|
849
|
+
`confidence check that REPLACES the human-quiz step when no human is present,`,
|
|
850
|
+
`the file-orthogonality preference, the prd-vs-task gate disjointness, and`,
|
|
851
|
+
`the emitted task shape — ALL live in that doc. Read them there.`,
|
|
852
|
+
``,
|
|
853
|
+
`No human is present, so apply the CONFIDENCE CHECK (\`TASKING-PROTOCOL.md\``,
|
|
854
|
+
`step 4): only emit tasks you would have gotten a human to approve. If`,
|
|
855
|
+
`granularity, dependency order, a gate, or a seam is genuinely unresolved, set`,
|
|
856
|
+
`\`needsAnswers: true\` on the specific uncertain task (questions in its body)`,
|
|
857
|
+
`rather than guessing — or, if the whole decomposition is unclear, stop and`,
|
|
858
|
+
`route the prd to needs-attention with the questions.`,
|
|
859
|
+
``,
|
|
860
|
+
`WRITE EVERY emitted task file under \`${STAGED_TASKS_DIR}/\` (the STAGING folder)`,
|
|
861
|
+
`— NEVER \`work/tasks/ready/\`. \`work/tasks/ready/\` is the agent-eligible POOL and`,
|
|
862
|
+
`the runner owns the runner/human-only promotion into it; the tasker's staging`,
|
|
863
|
+
`folder is \`work/tasks/backlog/\`. A write outside the staging folder is dropped`,
|
|
864
|
+
`by the runner-deterministic placement resolver.`,
|
|
865
|
+
``,
|
|
866
|
+
`Set each task's \`prd:\` field to the source prd slug (\`${slug}\`) so the`,
|
|
867
|
+
`link back to the prd survives.`,
|
|
868
|
+
``,
|
|
869
|
+
`Do NOT perform any git operations — do not stage, commit, push, or move any`,
|
|
870
|
+
`files. The RUNNER owns every git-state transition (it commits the produced`,
|
|
871
|
+
`tasks, releases the tasking lock, and moves the prd into \`work/prds/tasked/\`).`,
|
|
872
|
+
].join('\n');
|
|
873
|
+
}
|
|
874
|
+
/** Run the task agent. Prefers the injected runner; else the harness seam. */
|
|
875
|
+
async function runTaskAgent(options, cwd, prompt, slug) {
|
|
876
|
+
// The tasker AGENT launches with the AMBIENT env (`agentEnv`), never the
|
|
877
|
+
// identity-scoped `env` (an agent must not act as the bot). Falls back to `env`
|
|
878
|
+
// when no identity is configured.
|
|
879
|
+
const agentEnv = options.agentEnv ?? options.env;
|
|
880
|
+
if (options.dorfl) {
|
|
881
|
+
return options.dorfl({ cwd, prompt, slug, env: agentEnv });
|
|
882
|
+
}
|
|
883
|
+
const harness = options.harness ?? new NullHarness();
|
|
884
|
+
const launched = await launchWithOptionalWatch({
|
|
885
|
+
harness,
|
|
886
|
+
dir: cwd,
|
|
887
|
+
slug,
|
|
888
|
+
command: options.agentCmd ?? '',
|
|
889
|
+
prompt,
|
|
890
|
+
model: options.model,
|
|
891
|
+
sessionId: `task-${slug}`,
|
|
892
|
+
sessionsDir: options.sessionsDir,
|
|
893
|
+
env: agentEnv,
|
|
894
|
+
});
|
|
895
|
+
return { ok: launched.ok, detail: launched.detail };
|
|
896
|
+
}
|
|
897
|
+
/** A snapshot of {@link STAGED_TASKS_DIR}: filename → file content. */
|
|
898
|
+
function snapshotStagedTasks(cwd) {
|
|
899
|
+
const dir = join(cwd, STAGED_TASKS_DIR);
|
|
900
|
+
const snap = new Map();
|
|
901
|
+
for (const file of listMarkdown(dir)) {
|
|
902
|
+
snap.set(file, readFileSync(join(dir, file), 'utf8'));
|
|
903
|
+
}
|
|
904
|
+
return snap;
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Repo-relative paths of the {@link STAGED_TASKS_DIR}`/*.md` files the agent
|
|
908
|
+
* NEWLY created or CHANGED vs the pre-run snapshot — exactly what the runner
|
|
909
|
+
* captures + commits. (An untouched pre-existing staged task is NOT
|
|
910
|
+
* re-committed.) The agent's staging folder is `work/tasks/backlog/`; writes to
|
|
911
|
+
* the pool `work/tasks/ready/` are scrubbed at stage time, never picked up here.
|
|
912
|
+
*/
|
|
913
|
+
function newOrChangedStagedTasks(cwd, before) {
|
|
914
|
+
const dir = join(cwd, STAGED_TASKS_DIR);
|
|
915
|
+
const changed = [];
|
|
916
|
+
for (const file of listMarkdown(dir)) {
|
|
917
|
+
const content = readFileSync(join(dir, file), 'utf8');
|
|
918
|
+
if (before.get(file) !== content) {
|
|
919
|
+
changed.push(stagedTaskPath(file));
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return changed.sort();
|
|
923
|
+
}
|
|
924
|
+
/** Snapshot the POOL `work/tasks/ready/` (for the agent-write fence at stage time). */
|
|
925
|
+
function snapshotPool(cwd) {
|
|
926
|
+
const dir = workFolderPath(cwd, 'tasks-ready');
|
|
927
|
+
const snap = new Map();
|
|
928
|
+
for (const file of listMarkdown(dir)) {
|
|
929
|
+
snap.set(file, readFileSync(join(dir, file), 'utf8'));
|
|
930
|
+
}
|
|
931
|
+
return snap;
|
|
932
|
+
}
|
|
933
|
+
/** Read the produced backlog tasks' content keyed by repo-relative path. */
|
|
934
|
+
function collectEmittedTasks(cwd, relPaths) {
|
|
935
|
+
const out = {};
|
|
936
|
+
for (const rel of relPaths) {
|
|
937
|
+
out[rel] = readFileSync(join(cwd, rel), 'utf8');
|
|
938
|
+
}
|
|
939
|
+
return out;
|
|
940
|
+
}
|
|
941
|
+
/** List `*.md` files in `dir`, sorted; an absent dir reads as empty. */
|
|
942
|
+
function listMarkdown(dir) {
|
|
943
|
+
let entries;
|
|
944
|
+
try {
|
|
945
|
+
entries = readdirSync(dir);
|
|
946
|
+
}
|
|
947
|
+
catch {
|
|
948
|
+
return [];
|
|
949
|
+
}
|
|
950
|
+
return entries.filter((name) => isWorkItemFile(name)).sort();
|
|
951
|
+
}
|
|
952
|
+
//# sourceMappingURL=tasking.js.map
|