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/advance.js
ADDED
|
@@ -0,0 +1,1122 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { workItemPath, workItemRel } from './work-layout.js';
|
|
4
|
+
import { resolveAdvanceArg, SlugResolutionError, } from './slug-namespace.js';
|
|
5
|
+
import { ledgerRead } from './ledger-read.js';
|
|
6
|
+
import { parseFrontmatter } from './frontmatter.js';
|
|
7
|
+
import { parseSidecar, sidecarPathFor, } from './sidecar.js';
|
|
8
|
+
import { classifyTick } from './advance-classify.js';
|
|
9
|
+
import { acquireAdvancingLock, releaseAdvancingLock, } from './advancing-lock.js';
|
|
10
|
+
import { performDo } from './do.js';
|
|
11
|
+
import { harnessSurfaceGate, toNewQuestions, } from './surface-gate.js';
|
|
12
|
+
import { harnessTriageGate } from './triage-gate.js';
|
|
13
|
+
import { autoDispositionObservation, promoteObservation, } from './triage-persist.js';
|
|
14
|
+
import { mintAdr } from './mint-adr.js';
|
|
15
|
+
import { LIFECYCLE_CAS_CONTENTION } from './advancing-lock.js';
|
|
16
|
+
import { decide, DisallowedOutcomeError, } from './decision-engine.js';
|
|
17
|
+
import { APPLY_ALLOWED_OUTCOMES, buildApplyDecisionInput, harnessApplyDecider, } from './apply-decide.js';
|
|
18
|
+
import { persistSurfacedQuestions, } from './surface-persist.js';
|
|
19
|
+
import { applyAnsweredQuestions, } from './apply-persist.js';
|
|
20
|
+
import { detectAnsweredMergeAction, performMergeAction, } from './apply-merge-action.js';
|
|
21
|
+
/**
|
|
22
|
+
* The **`advance` verb SKELETON** (prd `advance-loop`, task
|
|
23
|
+
* `advance-verb-resolver`, US #1/5/6/18). `advance` is the SIBLING top-level verb
|
|
24
|
+
* (NOT a `do` subcommand — `do` subcommands are REJECTED in the prd) that drives
|
|
25
|
+
* a `work/` item ONE lifecycle rung toward "ready/built", reusing the SAME shared
|
|
26
|
+
* `prefix:arg` resolver `do` uses (extended with the `obs:` namespace, see
|
|
27
|
+
* {@link resolveAdvanceArg}).
|
|
28
|
+
*
|
|
29
|
+
* This module delivers the **classify → lock → execute SKELETON** — the contract
|
|
30
|
+
* both drivers (the later one-shot/loop tasks) wrap:
|
|
31
|
+
*
|
|
32
|
+
* 1. **classify** — read-only, NO model, NO lock: read the item's two signals
|
|
33
|
+
* (`needsAnswers` + the sidecar's answered-state) and call the pure
|
|
34
|
+
* {@link classifyTick} to get the rung kind. A CAS loser will have spent ONLY
|
|
35
|
+
* this free classification.
|
|
36
|
+
* 2. **lock** — take the `advancing` CAS borrow ({@link acquireAdvancingLock})
|
|
37
|
+
* for the classified rung, keyed on the item's `<type>-<slug>` identity. The
|
|
38
|
+
* expensive (agent/model) phase is ALWAYS post-lock, so a loser backs off
|
|
39
|
+
* having done ~nothing (a TOCTOU between classify and CAS is harmless — only
|
|
40
|
+
* the free classification is wasted, and the loser never starts model work).
|
|
41
|
+
* 3. **execute** — WINNER ONLY: dispatch the classified rung to the
|
|
42
|
+
* {@link RungExecutor} seam, then release the borrow.
|
|
43
|
+
*
|
|
44
|
+
* The **rung BODIES** are now ALL filled (their own tasks): `surface`
|
|
45
|
+
* (`advance-rung-surface`), `apply` (`advance-rung-apply`), and
|
|
46
|
+
* `triage-observation` (`advance-rung-triage`) dispatch through the clearly-named
|
|
47
|
+
* executor SEAM ({@link RungExecutor}); the build/task rungs ORCHESTRATE
|
|
48
|
+
* `do`/`do prd:`. What this verb does NOT do (LATER tasks):
|
|
49
|
+
* - The two **DRIVERS** (one-shot sequential / loop) + `-n` + the gate-FAMILY
|
|
50
|
+
* WIRING that resolves `autoBuild`/`autoTask`/`observationTriage` and threads
|
|
51
|
+
* them into the build/task gate composition — task `advance-drivers-and-gates`.
|
|
52
|
+
* (This verb already RESPECTS `observationTriage` in the triage rung — the gate's
|
|
53
|
+
* resolution chain + the build/task gate composition is the drivers task.)
|
|
54
|
+
* - The bare `advance` (eligible-SET) form — it needs the pool scan / driver, so
|
|
55
|
+
* the verb here is a SINGLE named-item tick; the bare form errors clearly
|
|
56
|
+
* ("needs the driver task"). See the `## Decisions` block in the task.
|
|
57
|
+
*
|
|
58
|
+
* The build-task / task-spec rungs ORCHESTRATE the existing `do` / `do spec:`
|
|
59
|
+
* machinery ({@link performDo}) — `advance` is a driver layered ON TOP, NEVER a
|
|
60
|
+
* peer that duplicates the build/task path (ONE build path, ONE task path —
|
|
61
|
+
* US #6).
|
|
62
|
+
*/
|
|
63
|
+
const DEFAULT_ARBITER = 'origin';
|
|
64
|
+
/**
|
|
65
|
+
* Map the resolver's namespace onto the sidecar type. The `spec` namespace maps
|
|
66
|
+
* to the `spec` sidecar type, so `advance spec:<slug>` resolves its
|
|
67
|
+
* signals/folders (`FOLDERS_FOR_TYPE['spec']`) and orchestrates `do spec:<slug>`.
|
|
68
|
+
*/
|
|
69
|
+
function sidecarTypeFor(namespace) {
|
|
70
|
+
return namespace === 'observation'
|
|
71
|
+
? 'observation'
|
|
72
|
+
: namespace === 'spec'
|
|
73
|
+
? 'spec'
|
|
74
|
+
: 'task';
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Read an item's two CLASSIFY signals off disk (read-only, NO model, NO lock):
|
|
78
|
+
* - `needsAnswers` from the item-body frontmatter (searching the lifecycle
|
|
79
|
+
* folders the type may rest in), and
|
|
80
|
+
* - the ACTIVE sidecar (`work/questions/<type>-<slug>.md`) parsed when present.
|
|
81
|
+
*
|
|
82
|
+
* Identity-keyed: the sidecar path derives PURELY from `<type>-<slug>` (it
|
|
83
|
+
* survives the item's `git mv`s with no lock-step move). Returns
|
|
84
|
+
* `needsAnswers:undefined` when no item file is found (the classifier treats it
|
|
85
|
+
* as "not gated", an ANALYSE rung) — the verb's job here is to wire the two
|
|
86
|
+
* signals, not to assert the item exists (that is the rung's concern).
|
|
87
|
+
*/
|
|
88
|
+
export function readItemSignals(input) {
|
|
89
|
+
const { repoPath, type, slug, item } = input;
|
|
90
|
+
const needsAnswers = readNeedsAnswers(repoPath, type, slug);
|
|
91
|
+
const sidecarRel = sidecarPathFor(item);
|
|
92
|
+
const sidecarAbs = join(repoPath, sidecarRel);
|
|
93
|
+
let sidecar;
|
|
94
|
+
if (existsSync(sidecarAbs)) {
|
|
95
|
+
sidecar = parseSidecar(readFileSync(sidecarAbs, 'utf8'));
|
|
96
|
+
}
|
|
97
|
+
return { needsAnswers, sidecar };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The lifecycle folders each item type may rest in (frontmatter source). After
|
|
101
|
+
* the capstone cut-over (task
|
|
102
|
+
* `cutover-retire-slicing-advancing-markers-and-trim-folder-sets`) the transient
|
|
103
|
+
* `tasking/` folder is GONE — a prd rests in `prds/ready` (source) or
|
|
104
|
+
* `prds/tasked` (tasked); while it is being tasked the body STAYS in
|
|
105
|
+
* `prds/ready` (the lock no longer moves it), so `tasking/` is never a
|
|
106
|
+
* frontmatter source.
|
|
107
|
+
*
|
|
108
|
+
* STAGING IS INCLUDED (`tasks-backlog` / `prds-proposed`): with `surfaceStaging`
|
|
109
|
+
* on (the user-visible default) the lifecycle surface pool enumerates
|
|
110
|
+
* `needsAnswers` items resting in STAGING as `task:`/`prd:` legs
|
|
111
|
+
* (`lifecycle-gather.ts`). The rung CLASSIFIER's signal read MUST see those
|
|
112
|
+
* staged bodies, or `needsAnswers` reads back `undefined`, the classifier
|
|
113
|
+
* mis-routes the item to the BUILD rung, and claim dies with "not found on
|
|
114
|
+
* origin/main" (observation
|
|
115
|
+
* `advance-task-folder-set-omits-tasks-backlog-staged-surface-items-misroute-to-build`).
|
|
116
|
+
* This is the staging-inclusive set its sibling `apply-persist.ts`
|
|
117
|
+
* (`APPLY_LIFECYCLE_FOLDERS`) already uses — kept in step here. BUILD/claim
|
|
118
|
+
* eligibility is UNCHANGED (still pool-only; staging items stay non-claimable):
|
|
119
|
+
* only the rung-classifier's frontmatter-source folders widen.
|
|
120
|
+
*/
|
|
121
|
+
const FOLDERS_FOR_TYPE = {
|
|
122
|
+
task: ['tasks-backlog', 'tasks-ready', 'in-progress', 'done'],
|
|
123
|
+
// `spec` rests in the parent-spec regime folders (a `spec:<slug>` legs
|
|
124
|
+
// frontmatter-source read resolves against these).
|
|
125
|
+
spec: ['specs-proposed', 'specs-ready', 'specs-tasked'],
|
|
126
|
+
observation: ['observations'],
|
|
127
|
+
};
|
|
128
|
+
/** Read `needsAnswers` off the FIRST `work/<folder>/<slug>.md` that exists. */
|
|
129
|
+
function readNeedsAnswers(repoPath, type, slug) {
|
|
130
|
+
for (const folder of FOLDERS_FOR_TYPE[type]) {
|
|
131
|
+
const abs = workItemPath(repoPath, folder, slug);
|
|
132
|
+
if (existsSync(abs)) {
|
|
133
|
+
return parseFrontmatter(readFileSync(abs, 'utf8')).needsAnswers;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The PRODUCTION rung executor: build/task rungs ORCHESTRATE the existing
|
|
140
|
+
* `do`/`do prd:` machinery ({@link performDo}); the `surface`/`apply`/
|
|
141
|
+
* `triage-observation` rung bodies are filled by their own tasks
|
|
142
|
+
* ({@link surfaceRung} / {@link applyRung} / {@link triageRung}). It NEVER
|
|
143
|
+
* re-implements the build/task path — it hands the resolved arg to `performDo`,
|
|
144
|
+
* which spans both namespaces (the task path is the `do prd:` rung the prd's
|
|
145
|
+
* 2026-06-09 UPDATE confirms routes through `performIntegration`).
|
|
146
|
+
*/
|
|
147
|
+
export const defaultRungExecutor = {
|
|
148
|
+
async buildTask(input) {
|
|
149
|
+
return orchestrateDo(input);
|
|
150
|
+
},
|
|
151
|
+
async taskSpec(input) {
|
|
152
|
+
return orchestrateDo(input);
|
|
153
|
+
},
|
|
154
|
+
async triageObservation(input) {
|
|
155
|
+
return triageRung(input);
|
|
156
|
+
},
|
|
157
|
+
async surface(input) {
|
|
158
|
+
return surfaceRung(input);
|
|
159
|
+
},
|
|
160
|
+
async apply(input) {
|
|
161
|
+
return applyRung(input);
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* ORCHESTRATE `do`/`do spec:` for the build-task / task-spec rungs: hand the
|
|
166
|
+
* resolved namespaced identity to {@link performDo} (the ONE build path / ONE
|
|
167
|
+
* task path). `advance` is a driver ON TOP — it does NOT duplicate `do`. The
|
|
168
|
+
* `do` outcome is mapped back onto the tick's outcome surface.
|
|
169
|
+
*/
|
|
170
|
+
async function orchestrateDo(input) {
|
|
171
|
+
const { item, context } = input;
|
|
172
|
+
const base = context.doOptions;
|
|
173
|
+
if (base === undefined) {
|
|
174
|
+
// The skeleton can classify + lock + DISPATCH without `do` options wired
|
|
175
|
+
// (the driver task threads them). Report it honestly rather than crash —
|
|
176
|
+
// the orchestration TARGET is `performDo`, named here, not re-implemented.
|
|
177
|
+
return {
|
|
178
|
+
exitCode: 1,
|
|
179
|
+
outcome: 'usage-error',
|
|
180
|
+
message: `advance would ORCHESTRATE \`do ${item}\` for this rung, but no \`do\` ` +
|
|
181
|
+
`options were threaded into the tick (the driver task wires them).`,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// The ORCHESTRATION TARGET is `performDo` by DEFAULT (in-place, the cwd checkout
|
|
185
|
+
// IS the isolation), or the injected {@link AdvanceContext.doDriver} — the
|
|
186
|
+
// registry-set advance driver threads a PER-MIRROR JOB-WORKTREE driver so the
|
|
187
|
+
// daemon/CI build runs isolated off the mirror's arbiter. Either way `advance`
|
|
188
|
+
// ORCHESTRATES `do` (the ONE build path / ONE task path) — it does NOT duplicate it.
|
|
189
|
+
const driver = context.doDriver ?? performDo;
|
|
190
|
+
const result = await driver({ ...base, arg: item });
|
|
191
|
+
return {
|
|
192
|
+
exitCode: result.exitCode,
|
|
193
|
+
outcome: result.exitCode === 0 ? 'advanced' : mapDoOutcome(result),
|
|
194
|
+
message: result.message,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/** Map a non-zero `do` result onto the tick's outcome vocabulary. */
|
|
198
|
+
function mapDoOutcome(result) {
|
|
199
|
+
switch (result.outcome) {
|
|
200
|
+
case 'lost':
|
|
201
|
+
return 'lost';
|
|
202
|
+
case 'contended':
|
|
203
|
+
return 'contended';
|
|
204
|
+
default:
|
|
205
|
+
return 'usage-error';
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* The SURFACE rung BODY (task `advance-rung-surface`, US #32/33): the FIRST rung
|
|
210
|
+
* filling the executor seam, establishing the spawn→emit→persist pattern the
|
|
211
|
+
* other rung bodies reuse. Under the `advancing` CAS lock (held by
|
|
212
|
+
* {@link performAdvance} BEFORE this runs — so the expensive spawn is POST-lock,
|
|
213
|
+
* winner-only), it:
|
|
214
|
+
*
|
|
215
|
+
* 1. spawns a FRESH-CONTEXT agent with `surface-questions` loaded (the
|
|
216
|
+
* {@link SurfaceGate} seam, mirroring the review gate's `review` spawn) and
|
|
217
|
+
* collects the EMITTED questions — the skill JUDGES, writes nothing; and
|
|
218
|
+
* 2. has the ENGINE ITSELF write/append them to the sidecar CAS-atomically AND
|
|
219
|
+
* set `needsAnswers:true` in the SAME commit
|
|
220
|
+
* ({@link persistSurfacedQuestions}) — the engine PERSISTS.
|
|
221
|
+
*
|
|
222
|
+
* Append-never-overwrite: a re-surface ADDS `qN+1` and flips a previously-all-
|
|
223
|
+
* answered sidecar back to not-all-answered (the persist owns that). An EMPTY
|
|
224
|
+
* emit (the skill's honest "no open judgement") writes nothing and reports it.
|
|
225
|
+
*/
|
|
226
|
+
async function surfaceRung(input) {
|
|
227
|
+
const { item, context } = input;
|
|
228
|
+
const note = context.note ?? (() => { });
|
|
229
|
+
const cwd = context.cwd;
|
|
230
|
+
// Locate the item file (the only thing the persist needs beyond the questions:
|
|
231
|
+
// the file to set `needsAnswers:true` on). The sidecar path is identity-derived,
|
|
232
|
+
// not folder-derived, so only the ITEM file's folder must be found.
|
|
233
|
+
const itemPath = findItemPath(cwd, input.namespace, input.slug);
|
|
234
|
+
if (itemPath === undefined) {
|
|
235
|
+
return vanishedSkip({ rung: 'surface', item });
|
|
236
|
+
}
|
|
237
|
+
// 1. SPAWN the fresh-context `surface-questions` agent (the skill JUDGES). The
|
|
238
|
+
// expensive model work is POST-lock (the lock is held by `performAdvance`).
|
|
239
|
+
const gate = context.surfaceGate ?? harnessSurfaceGate();
|
|
240
|
+
let emit;
|
|
241
|
+
try {
|
|
242
|
+
emit = await gate({
|
|
243
|
+
item,
|
|
244
|
+
cwd,
|
|
245
|
+
surfaceModel: context.surfaceModel,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
250
|
+
return {
|
|
251
|
+
exitCode: 1,
|
|
252
|
+
outcome: 'usage-error',
|
|
253
|
+
message: `surface ${item}: the surface-questions agent produced no usable emit (${detail}).`,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
// 2. The ENGINE persists (the skill wrote nothing): append-or-create the sidecar
|
|
257
|
+
// + set `needsAnswers:true` in ONE commit (CAS-atomic under the held lock).
|
|
258
|
+
const persist = context.surfacePersist ?? persistSurfacedQuestions;
|
|
259
|
+
const result = persist({
|
|
260
|
+
cwd,
|
|
261
|
+
item,
|
|
262
|
+
itemPath,
|
|
263
|
+
questions: toNewQuestions(emit),
|
|
264
|
+
note,
|
|
265
|
+
});
|
|
266
|
+
if (result.outcome === 'nothing') {
|
|
267
|
+
// LIMBO DETECTION (task `advance-surface-limbo-observation-loudly-instead-of-
|
|
268
|
+
// silent-no-op`): an untriaged observation with NO sidecar whose surfacer had
|
|
269
|
+
// nothing to ask is a TRAP — the human's triage answer may have been recorded
|
|
270
|
+
// in an in-BODY "Applied answers" block, but the engine reads triage-vs-
|
|
271
|
+
// settled only from the `triaged:` frontmatter marker (`ledger-read.ts`) and
|
|
272
|
+
// the promote path only from an answered `disposition: promote` SIDECAR
|
|
273
|
+
// (`triage-persist.ts`). Without either, the item is untriaged (re-enumerated
|
|
274
|
+
// every tick), un-surfaceable (nothing to ask), un-promotable (no sidecar) —
|
|
275
|
+
// a silent exit-0 no-op forever. Surface it LOUDLY instead.
|
|
276
|
+
const limbo = detectObservationLimbo(input);
|
|
277
|
+
if (limbo !== undefined) {
|
|
278
|
+
note(limbo);
|
|
279
|
+
return { exitCode: 1, outcome: 'usage-error', message: limbo };
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
exitCode: 0,
|
|
283
|
+
outcome: 'no-op',
|
|
284
|
+
message: `surface ${item}: no open judgement — nothing surfaced.`,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
exitCode: 0,
|
|
289
|
+
outcome: 'advanced',
|
|
290
|
+
message: `surfaced ${result.entryCount} question(s) for ${item} → ${result.sidecarPath} ` +
|
|
291
|
+
`(needsAnswers:true, CAS-atomic).`,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Detect the OBSERVATION LIMBO shape (task `advance-surface-limbo-observation-
|
|
296
|
+
* loudly-instead-of-silent-no-op`): an observation whose triage answer was
|
|
297
|
+
* (mis-)authored in an in-body "Applied answers" block instead of the sidecar/
|
|
298
|
+
* frontmatter channels the engine reads. All four conditions must hold:
|
|
299
|
+
*
|
|
300
|
+
* 1. the item is an OBSERVATION;
|
|
301
|
+
* 2. its frontmatter has NO `triaged:` marker (untriaged per `ledger-read.ts`);
|
|
302
|
+
* 3. there is NO active question sidecar at `work/questions/observation-<slug>.md`;
|
|
303
|
+
* 4. the surfacer just returned empty (the caller — {@link surfaceRung} — only
|
|
304
|
+
* calls this on the persist's `nothing` branch).
|
|
305
|
+
*
|
|
306
|
+
* Returns the loud diagnostic to emit (naming BOTH valid channels), or `undefined`
|
|
307
|
+
* when the shape does not match (the surfacer's empty is then the normal calm no-op).
|
|
308
|
+
* The engine does NOT (and will not) honour in-body disposition prose — one channel
|
|
309
|
+
* (the sidecar + `triaged:` frontmatter) keeps the loop honest.
|
|
310
|
+
*/
|
|
311
|
+
function detectObservationLimbo(input) {
|
|
312
|
+
if (input.namespace !== 'observation')
|
|
313
|
+
return undefined;
|
|
314
|
+
const { item, slug, context } = input;
|
|
315
|
+
const cwd = context.cwd;
|
|
316
|
+
const itemRel = findItemPath(cwd, input.namespace, slug);
|
|
317
|
+
if (itemRel === undefined)
|
|
318
|
+
return undefined;
|
|
319
|
+
const fm = parseFrontmatter(readFileSync(join(cwd, itemRel), 'utf8'));
|
|
320
|
+
if (fm.triaged !== undefined && fm.triaged !== '')
|
|
321
|
+
return undefined;
|
|
322
|
+
const sidecarRel = sidecarPathFor(item);
|
|
323
|
+
if (existsSync(join(cwd, sidecarRel)))
|
|
324
|
+
return undefined;
|
|
325
|
+
return (`observation \`${slug}\` is in a limbo: no \`triaged:\` frontmatter marker ` +
|
|
326
|
+
`AND no answered question sidecar at \`${sidecarRel}\`, but the surfacer has ` +
|
|
327
|
+
`nothing to ask. If a human triage decision (promote-slice / keep / duplicate) ` +
|
|
328
|
+
`has been recorded in the observation BODY, that channel is INVISIBLE to the ` +
|
|
329
|
+
`runner — author the sidecar, or set \`triaged:\` in frontmatter. The engine ` +
|
|
330
|
+
`does not (and will not) honour in-body disposition prose.`);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* The observation TRIAGE rung BODY (task `advance-rung-triage`, US #16/17/23):
|
|
334
|
+
* the rung the classifier picks for an UNTRIAGED observation (`needsAnswers` not
|
|
335
|
+
* set, no sidecar). It is QUESTION-GATED BY DEFAULT: it surfaces a promote/keep/
|
|
336
|
+
* delete question and WAITS — so "is this worth building?" is NEVER decided
|
|
337
|
+
* autonomously. A CONSERVATIVE `observationTriage: 'auto'`-gated EXCEPTION (US #17,
|
|
338
|
+
* high bar) may auto-disposition ONLY the no-question cases:
|
|
339
|
+
*
|
|
340
|
+
* - **default (question-gated):** delegate to {@link surfaceRung} — spawn the
|
|
341
|
+
* `surface-questions` agent (it emits a PLAIN "what becomes of this signal?"
|
|
342
|
+
* question — NO disposition token any more, task
|
|
343
|
+
* `agentic-apply-retire-disposition-vocabulary`) and the ENGINE persists the
|
|
344
|
+
* sidecar + `needsAnswers`. When the human answers, the AGENTIC apply decision
|
|
345
|
+
* (not a stamped token) reads the answer + source and chooses what to DO.
|
|
346
|
+
* Surface stays ALWAYS allowed (US #23) — this path runs under `ask`/`off`
|
|
347
|
+
* (and `off` + an explicit `obs:` runs in this `ask`-mode).
|
|
348
|
+
* - **`auto` exception:** ONLY under `observationTriage: 'auto'`, ask the
|
|
349
|
+
* {@link TriageGate} whether the observation is a no-question case. If it emits
|
|
350
|
+
* `auto: true` (`duplicate` → DELETE the redundant note; `map` → unambiguous
|
|
351
|
+
* map onto an existing item), the engine auto-dispositions it WITHOUT a
|
|
352
|
+
* question ({@link autoDispositionObservation}). It NEVER auto-deletes a
|
|
353
|
+
* NON-duplicate (a `duplicate` discharges by deletion because it is a
|
|
354
|
+
* redundant copy of an already-captured signal — nothing is lost) and NEVER
|
|
355
|
+
* auto-promotes a judgement call (`auto: false` ⇒ fall back to the surface
|
|
356
|
+
* question). Promotion is ALWAYS a human answer (the apply path).
|
|
357
|
+
*
|
|
358
|
+
* Under the `advancing` CAS lock (held by {@link performAdvance} BEFORE this runs),
|
|
359
|
+
* so the expensive spawn is POST-lock, winner-only.
|
|
360
|
+
*/
|
|
361
|
+
async function triageRung(input) {
|
|
362
|
+
const { item, context } = input;
|
|
363
|
+
const note = context.note ?? (() => { });
|
|
364
|
+
const cwd = context.cwd;
|
|
365
|
+
// The CONSERVATIVE auto-disposition EXCEPTION — ONLY under `observationTriage:
|
|
366
|
+
// 'auto'`. Under `'ask'`/`'off'`/unset (including `off` + an EXPLICIT obs:<slug>
|
|
367
|
+
// that bypassed the selection gate), EVERY untriaged observation surfaces the
|
|
368
|
+
// question (the always-allowed path), so "worth building?" is never decided
|
|
369
|
+
// autonomously — `off` + explicit runs in the conservative `ask`-mode.
|
|
370
|
+
if (context.observationTriage === 'auto') {
|
|
371
|
+
const itemPath = findItemPath(cwd, input.namespace, input.slug);
|
|
372
|
+
if (itemPath === undefined) {
|
|
373
|
+
return vanishedSkip({ rung: 'triage', item });
|
|
374
|
+
}
|
|
375
|
+
const gate = context.triageGate ?? harnessTriageGate();
|
|
376
|
+
let decision;
|
|
377
|
+
try {
|
|
378
|
+
decision = await gate({ item, cwd, triageModel: context.triageModel });
|
|
379
|
+
}
|
|
380
|
+
catch (err) {
|
|
381
|
+
// A gate failure is NOT a reason to auto-dispose — fall back to the SAFE
|
|
382
|
+
// question-gated path (surface the question), never the reverse.
|
|
383
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
384
|
+
note(`triage ${item}: the auto-triage gate produced no usable emit (${detail}); ` +
|
|
385
|
+
'falling back to the question-gated surface path.');
|
|
386
|
+
decision = { auto: false };
|
|
387
|
+
}
|
|
388
|
+
if (decision.auto === true) {
|
|
389
|
+
// A no-question case (duplicate / map) — auto-disposition WITHOUT a
|
|
390
|
+
// question. BOTH discharge the redundant note BY DELETION (a duplicate is a
|
|
391
|
+
// redundant copy; a map is already covered by the item it maps onto). There
|
|
392
|
+
// is no resting `triaged:keep` state any more. NEVER auto-deletes a
|
|
393
|
+
// NON-redundant signal; NEVER auto-promotes.
|
|
394
|
+
const dispose = context.autoDisposition ?? autoDispositionObservation;
|
|
395
|
+
const result = dispose({
|
|
396
|
+
cwd,
|
|
397
|
+
item,
|
|
398
|
+
itemPath,
|
|
399
|
+
kind: decision.kind,
|
|
400
|
+
existing: decision.existing,
|
|
401
|
+
reason: decision.reason,
|
|
402
|
+
note,
|
|
403
|
+
});
|
|
404
|
+
return { exitCode: 0, outcome: 'advanced', message: result.message };
|
|
405
|
+
}
|
|
406
|
+
// `auto: false` ⇒ a judgement call. Fall through to the surface question.
|
|
407
|
+
}
|
|
408
|
+
// DEFAULT (question-gated): surface a PLAIN "what becomes of this signal?"
|
|
409
|
+
// question + WAIT. This REUSES the surface rung verbatim (the `surface-questions`
|
|
410
|
+
// skill emits the triage question — NO disposition token any more, task
|
|
411
|
+
// `agentic-apply-retire-disposition-vocabulary`); the AGENTIC apply decision
|
|
412
|
+
// reads the human's answer + source and decides what to DO when it is answered.
|
|
413
|
+
return surfaceRung(input);
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* The APPLY rung BODY (task `advance-rung-apply`; AGENTIC apply, task
|
|
417
|
+
* `agentic-apply-retire-disposition-vocabulary`): when the classifier says `apply`
|
|
418
|
+
* (ALL sidecar entries answered), apply the HUMAN's answers.
|
|
419
|
+
*
|
|
420
|
+
* For a fully-answered OBSERVATION (and no caller-supplied follow-up batch), the
|
|
421
|
+
* apply rung is now AGENT-DRIVEN: it runs the shared `decide(input, allowedOutcomes)`
|
|
422
|
+
* engine ({@link decide}) over `(the answered question(s) + the SOURCE item + its
|
|
423
|
+
* type/context)` via the injected {@link ApplyDecider}, allowing the set
|
|
424
|
+
* `{task | prd | adr | delete | ask}` (= `{mint-task | mint-prd | mint-adr |
|
|
425
|
+
* delete-source | ask-follow-up}`; `adr` is now WIRED by task
|
|
426
|
+
* `agentic-apply-mint-adr-route`, which added the {@link mintAdr} route). The
|
|
427
|
+
* verdict ROUTES:
|
|
428
|
+
* - `ask` → the EXISTING append/re-pause loop ({@link applyAnsweredQuestions}
|
|
429
|
+
* with the follow-up appended; `needsAnswers:true` stays, re-pause in one
|
|
430
|
+
* commit);
|
|
431
|
+
* - `adr` → {@link mintAdr} (mint a SELF-CONTAINED ADR into `docs/adr/` + `git
|
|
432
|
+
* rm` the source + sidecar in the SAME atomic commit; the SIBLING route for the
|
|
433
|
+
* off-board target);
|
|
434
|
+
* - `task` / `prd` → {@link promoteObservation} (mint a SELF-CONTAINED artifact +
|
|
435
|
+
* `git rm` the source + sidecar in the SAME atomic commit); the artifact type
|
|
436
|
+
* comes from the agent's VERDICT, NOT a human `promote-*` field;
|
|
437
|
+
* - `delete` → {@link applyAnsweredQuestions} discharge-by-deletion (`git rm`
|
|
438
|
+
* source + sidecar in one revertible commit, the reason in the commit message).
|
|
439
|
+
*
|
|
440
|
+
* For a TASK/PRD (answering its OWN open questions) or a caller-supplied follow-up
|
|
441
|
+
* batch, it delegates straight to {@link applyAnsweredQuestions} (resolve fully /
|
|
442
|
+
* re-pause) — the lifecycle path is untouched (a task/prd is dropped by its own
|
|
443
|
+
* lifecycle, not by a question answer).
|
|
444
|
+
*
|
|
445
|
+
* Under the `advancing` CAS lock (held by {@link performAdvance} BEFORE this runs
|
|
446
|
+
* — so the work is POST-lock, winner-only). ALWAYS allowed (no gate). NEVER
|
|
447
|
+
* invents an answer — it applies ONLY the human-authored `answer:` text; a
|
|
448
|
+
* subset-answered sidecar is not even classified `apply` (the classifier NO-OPs),
|
|
449
|
+
* asserted in the persist.
|
|
450
|
+
*/
|
|
451
|
+
async function applyRung(input) {
|
|
452
|
+
const { item, context } = input;
|
|
453
|
+
const note = context.note ?? (() => { });
|
|
454
|
+
const cwd = context.cwd;
|
|
455
|
+
const itemPath = findItemPath(cwd, input.namespace, input.slug);
|
|
456
|
+
if (itemPath === undefined) {
|
|
457
|
+
return vanishedSkip({ rung: 'apply', item });
|
|
458
|
+
}
|
|
459
|
+
// RUNNER-ACTION KIND-CHECK (task `apply-rung-merge-disposition`, prd
|
|
460
|
+
// `land-time-reverify-and-parallel-merge-ceiling`): an answered `kind: merge`
|
|
461
|
+
// sidecar entry is a DETERMINISTIC land action, NOT a content decision. It
|
|
462
|
+
// dispatches HERE (a sibling of the agentic `decide()`), keyed off the
|
|
463
|
+
// question kind + the human's plain `merge | hold | drop` answer, BEFORE the
|
|
464
|
+
// agentic decider runs. The dispatcher invokes the EXISTING land primitive
|
|
465
|
+
// (`performIntegration` with `committedRecovery: true` + `freshWorktreeGate:
|
|
466
|
+
// true`) through the EXISTING per-job worktree seam (`workspace.ts`
|
|
467
|
+
// `createJob` off the hub mirror) — it does NOT re-implement rebase / verify
|
|
468
|
+
// / advance and does NOT improvise a worktree or clone.
|
|
469
|
+
const mergeRoute = await maybeRunMergeAction(input, itemPath);
|
|
470
|
+
if (mergeRoute !== undefined) {
|
|
471
|
+
return mergeRoute;
|
|
472
|
+
}
|
|
473
|
+
// AGENTIC APPLY for an answered OBSERVATION (the subsumed triage rung): run the
|
|
474
|
+
// shared decision engine over the answer(s) + source, route the verdict. A
|
|
475
|
+
// caller-supplied follow-up batch (`applyFollowups`) bypasses the decision and
|
|
476
|
+
// re-pauses directly (a test/driver hook). TASK/PRD items resolve in place
|
|
477
|
+
// (their own lifecycle), so they skip the decision and fall through to the
|
|
478
|
+
// persist below.
|
|
479
|
+
const runAgenticDecision = input.namespace === 'observation' &&
|
|
480
|
+
!(context.applyFollowups && context.applyFollowups.length > 0);
|
|
481
|
+
if (runAgenticDecision) {
|
|
482
|
+
return applyAgenticDecision(input, itemPath);
|
|
483
|
+
}
|
|
484
|
+
const apply = context.applyPersist ?? applyAnsweredQuestions;
|
|
485
|
+
try {
|
|
486
|
+
const result = apply({
|
|
487
|
+
cwd,
|
|
488
|
+
item,
|
|
489
|
+
itemPath,
|
|
490
|
+
appendQuestions: context.applyFollowups,
|
|
491
|
+
note,
|
|
492
|
+
});
|
|
493
|
+
// Map the apply persist's outcome to the rung's outcome. `vanished` is the
|
|
494
|
+
// F3a clean-exit case (item was gone between capture and write, e.g. a
|
|
495
|
+
// concurrent promote); benign skip, exitCode 0, distinct from `no-op`.
|
|
496
|
+
const mapped = result.outcome === 'repaused'
|
|
497
|
+
? 'no-op'
|
|
498
|
+
: result.outcome === 'vanished'
|
|
499
|
+
? 'vanished'
|
|
500
|
+
: 'advanced';
|
|
501
|
+
return {
|
|
502
|
+
exitCode: 0,
|
|
503
|
+
outcome: mapped,
|
|
504
|
+
message: result.message,
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
catch (err) {
|
|
508
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
509
|
+
return {
|
|
510
|
+
exitCode: 1,
|
|
511
|
+
outcome: 'usage-error',
|
|
512
|
+
message: `apply ${item}: ${detail}`,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Dispatch an answered MERGE-QUESTION (a sidecar entry stamped `kind: merge` by
|
|
518
|
+
* the merge-question surfacer) through the EXISTING land primitive
|
|
519
|
+
* (`performIntegration` with `committedRecovery: true` + `freshWorktreeGate:
|
|
520
|
+
* true`) via the EXISTING per-job worktree seam (`workspace.ts` `createJob`).
|
|
521
|
+
* The deterministic SIBLING of the agentic `decide()` content-decision (PRD
|
|
522
|
+
* `land-time-reverify-and-parallel-merge-ceiling`, task
|
|
523
|
+
* `apply-rung-merge-disposition`; Stories #15, #16): a merge-acceptance has no
|
|
524
|
+
* judgement content (the human's plain `merge | hold | drop` answer IS the
|
|
525
|
+
* decision; the apply-time re-verify on the rebased tip is the real correctness
|
|
526
|
+
* gate), so the apply rung KIND-CHECKS the sidecar BEFORE the agentic decider.
|
|
527
|
+
*
|
|
528
|
+
* Returns `undefined` when there is no answered `kind: merge` entry to dispatch
|
|
529
|
+
* (the apply rung then proceeds to the existing path — agentic for
|
|
530
|
+
* observations, normal `applyAnsweredQuestions` for task/prd content questions).
|
|
531
|
+
* Returns a {@link RungExecResult} when the dispatcher handled the rung:
|
|
532
|
+
*
|
|
533
|
+
* - `landed` / `already-integrated` ⇒ the kept commit landed on `main` (or
|
|
534
|
+
* was already there); the dispatcher FALLS THROUGH to the normal apply path
|
|
535
|
+
* so the answer is recorded in the item body + the sidecar is resolved.
|
|
536
|
+
* - `refused` ⇒ the LAND was refused (RED re-verify on the rebased tip,
|
|
537
|
+
* rebase conflict, or pre-checkout failure); `performIntegration` routed
|
|
538
|
+
* the item to needs-attention through its own shared seam, so `main` never
|
|
539
|
+
* received a failing tree. The apply rung SHORT-CIRCUITS — the sidecar is
|
|
540
|
+
* LEFT IN PLACE so the open answer stays surfaced for a human follow-up.
|
|
541
|
+
* - `restale` ⇒ `strictMergeApproval` was ON and the merge-base moved
|
|
542
|
+
* between the surfacer's question and this apply; the apply rung appends
|
|
543
|
+
* a follow-up question + re-pauses (the human re-confirms against the new
|
|
544
|
+
* base).
|
|
545
|
+
* - `hold` / `drop` ⇒ no land; fall through to the normal apply path so the
|
|
546
|
+
* answer is recorded in body. The branch stays unmerged; a future
|
|
547
|
+
* surfacer pass may re-emit a merge-question.
|
|
548
|
+
*/
|
|
549
|
+
async function maybeRunMergeAction(input, itemPath) {
|
|
550
|
+
const { item, context } = input;
|
|
551
|
+
const cwd = context.cwd;
|
|
552
|
+
const note = context.note ?? (() => { });
|
|
553
|
+
const detected = detectAnsweredMergeAction(cwd, item);
|
|
554
|
+
if (detected === undefined)
|
|
555
|
+
return undefined;
|
|
556
|
+
const handler = context.mergeAction ?? performMergeAction;
|
|
557
|
+
// The dispatcher needs a `workspacesDir` to cut a per-job worktree (the
|
|
558
|
+
// `createJob` seam). When unset (a caller that has not threaded it) we
|
|
559
|
+
// REFUSE cleanly rather than guess: a land without isolation is not the
|
|
560
|
+
// shape this dispatcher promises.
|
|
561
|
+
const workspacesDir = context.workspacesDir;
|
|
562
|
+
if (workspacesDir === undefined && context.mergeAction === undefined) {
|
|
563
|
+
const message = `apply ${item}: answered merge-question detected (kind=merge, answer=` +
|
|
564
|
+
`${detected.verb}) but no \`workspacesDir\` is threaded into the apply ` +
|
|
565
|
+
`rung — the dispatcher needs one to cut the per-job worktree via ` +
|
|
566
|
+
`\`workspace.ts\` \`createJob\`. NOT landing; the answer stays surfaced.`;
|
|
567
|
+
note(message);
|
|
568
|
+
return { exitCode: 1, outcome: 'usage-error', message };
|
|
569
|
+
}
|
|
570
|
+
let result;
|
|
571
|
+
try {
|
|
572
|
+
result = await handler({
|
|
573
|
+
action: detected,
|
|
574
|
+
item,
|
|
575
|
+
slug: input.slug,
|
|
576
|
+
cwd,
|
|
577
|
+
arbiter: context.arbiter ?? DEFAULT_ARBITER,
|
|
578
|
+
arbiterUrl: context.arbiterUrl,
|
|
579
|
+
workspacesDir: workspacesDir ?? '',
|
|
580
|
+
prepare: context.prepare,
|
|
581
|
+
verify: context.verify,
|
|
582
|
+
strictMergeApproval: context.strictMergeApproval,
|
|
583
|
+
note,
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
catch (err) {
|
|
587
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
588
|
+
const message = `apply ${item}: answered merge-question dispatch raised (${detail}); NOT ` +
|
|
589
|
+
`landing; the answer stays surfaced.`;
|
|
590
|
+
note(message);
|
|
591
|
+
return { exitCode: 1, outcome: 'usage-error', message };
|
|
592
|
+
}
|
|
593
|
+
if (result.outcome === 'refused') {
|
|
594
|
+
// `main` never received a failing tree (performIntegration routed the
|
|
595
|
+
// bounce to needs-attention through its own shared seam). SHORT-CIRCUIT:
|
|
596
|
+
// leave the sidecar so the open answer stays surfaced — the apply rung
|
|
597
|
+
// MUST NOT also resolve it (the next surfacer / human will follow up).
|
|
598
|
+
note(result.message);
|
|
599
|
+
return { exitCode: 1, outcome: 'usage-error', message: result.message };
|
|
600
|
+
}
|
|
601
|
+
if (result.outcome === 'restale') {
|
|
602
|
+
// `strictMergeApproval` re-surface: append a follow-up question and
|
|
603
|
+
// re-pause. The previous answer stays recorded in the entry; the human
|
|
604
|
+
// re-confirms against the new merge-base in the appended question.
|
|
605
|
+
const apply = context.applyPersist ?? applyAnsweredQuestions;
|
|
606
|
+
try {
|
|
607
|
+
const applied = apply({
|
|
608
|
+
cwd,
|
|
609
|
+
item,
|
|
610
|
+
itemPath,
|
|
611
|
+
appendQuestions: [
|
|
612
|
+
{
|
|
613
|
+
question: `Merge-base for \`work/${input.slug}\` moved since your last ` +
|
|
614
|
+
`answer (strictMergeApproval is ON). Re-confirm: still land?`,
|
|
615
|
+
context: result.message,
|
|
616
|
+
default: 'merge | hold | drop',
|
|
617
|
+
kind: 'merge',
|
|
618
|
+
},
|
|
619
|
+
],
|
|
620
|
+
note,
|
|
621
|
+
});
|
|
622
|
+
return {
|
|
623
|
+
exitCode: 0,
|
|
624
|
+
outcome: 'no-op',
|
|
625
|
+
message: applied.message,
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
catch (err) {
|
|
629
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
630
|
+
return {
|
|
631
|
+
exitCode: 1,
|
|
632
|
+
outcome: 'usage-error',
|
|
633
|
+
message: `apply ${item}: re-surfacing the merge-question failed (${detail}).`,
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
// landed | already-integrated | hold | drop: the dispatcher's action is
|
|
638
|
+
// done; FALL THROUGH to the normal apply path so the answer is recorded in
|
|
639
|
+
// the item body + the sidecar is resolved.
|
|
640
|
+
note(result.message);
|
|
641
|
+
const apply = context.applyPersist ?? applyAnsweredQuestions;
|
|
642
|
+
try {
|
|
643
|
+
const applied = apply({ cwd, item, itemPath, note });
|
|
644
|
+
const mapped = applied.outcome === 'repaused'
|
|
645
|
+
? 'no-op'
|
|
646
|
+
: applied.outcome === 'vanished'
|
|
647
|
+
? 'vanished'
|
|
648
|
+
: 'advanced';
|
|
649
|
+
return {
|
|
650
|
+
exitCode: 0,
|
|
651
|
+
outcome: mapped,
|
|
652
|
+
message: `${result.message} ${applied.message}`,
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
catch (err) {
|
|
656
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
657
|
+
return {
|
|
658
|
+
exitCode: 1,
|
|
659
|
+
outcome: 'usage-error',
|
|
660
|
+
message: `apply ${item}: ${detail}`,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* The cross-tick-window BENIGN SKIP shared by all three rungs that need to
|
|
666
|
+
* resolve an item file (surface / triage / apply). The lifecycle pool enumerated
|
|
667
|
+
* the item at scan-time, but by the time this leg ran a sibling parallel leg had
|
|
668
|
+
* already triaged/settled/deleted it. At the ~33-way CI matrix scale this is a
|
|
669
|
+
* CALM, EXPECTED condition — making it an exit-1 "a human must reconcile" turned
|
|
670
|
+
* the matrix into a wall of red. It is now a `vanished` outcome (`exitCode: 0`,
|
|
671
|
+
* distinguishable from `no-op`) carrying a clear message naming the rung + item.
|
|
672
|
+
* (See task `observation-identity-is-its-filename-not-a-foreign-slug`.)
|
|
673
|
+
*/
|
|
674
|
+
function vanishedSkip(input) {
|
|
675
|
+
return {
|
|
676
|
+
exitCode: 0,
|
|
677
|
+
outcome: 'vanished',
|
|
678
|
+
message: `advance classified the '${input.rung}' rung for ${input.item} but its ` +
|
|
679
|
+
`item file was gone from work/ by the time the leg ran (a sibling leg ` +
|
|
680
|
+
`likely triaged/settled/deleted it between enumerate and run) — benign ` +
|
|
681
|
+
`skip.`,
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Find the item file `work/<folder>/<slug>.md` for a type, across the lifecycle
|
|
686
|
+
* folders it may rest in (the SAME folder set {@link readNeedsAnswers} searches).
|
|
687
|
+
* Returns the path RELATIVE to `cwd`, or `undefined` when no file exists.
|
|
688
|
+
*/
|
|
689
|
+
function findItemPath(cwd, namespace, slug) {
|
|
690
|
+
const type = sidecarTypeFor(namespace);
|
|
691
|
+
for (const folder of FOLDERS_FOR_TYPE[type]) {
|
|
692
|
+
const rel = workItemRel(folder, `${slug}.md`);
|
|
693
|
+
if (existsSync(join(cwd, rel))) {
|
|
694
|
+
return rel;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return undefined;
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* The AGENTIC apply DECISION for a fully-answered OBSERVATION (task
|
|
701
|
+
* `agentic-apply-retire-disposition-vocabulary`): run the shared
|
|
702
|
+
* `decide(input, allowedOutcomes)` engine over the answer(s) + source, then ROUTE
|
|
703
|
+
* the verdict. The artifact-type selection (task vs prd) comes from the agent's
|
|
704
|
+
* VERDICT, NOT a human `promote-*` field (which is retired). Replaces the old
|
|
705
|
+
* `answeredPromoteArtifact` + disposition picker.
|
|
706
|
+
*
|
|
707
|
+
* - `ask` → append the follow-up question(s) + re-pause (the EXISTING loop, via
|
|
708
|
+
* {@link applyAnsweredQuestions}'s `appendQuestions`);
|
|
709
|
+
* - `task` / `prd` → {@link promoteObservation} (mint self-contained + delete
|
|
710
|
+
* source in the same atomic commit);
|
|
711
|
+
* - `adr` → {@link mintAdr} (mint a self-contained ADR into `docs/adr/` + delete
|
|
712
|
+
* source in the same atomic commit; the SIBLING route for the off-board target,
|
|
713
|
+
* task `agentic-apply-mint-adr-route`);
|
|
714
|
+
* - `delete` → {@link applyAnsweredQuestions}'s discharge-by-deletion (`git rm`
|
|
715
|
+
* source + sidecar in one revertible commit, the reason in the message).
|
|
716
|
+
*
|
|
717
|
+
* The allowed set is `{task | prd | adr | delete | ask}`; a verdict outside it is
|
|
718
|
+
* rejected by the engine's allowed-outcome guard ({@link DisallowedOutcomeError})
|
|
719
|
+
* and mapped onto a usage-error — never dispatched.
|
|
720
|
+
*/
|
|
721
|
+
async function applyAgenticDecision(input, itemPath) {
|
|
722
|
+
const { item, context } = input;
|
|
723
|
+
const note = context.note ?? (() => { });
|
|
724
|
+
const cwd = context.cwd;
|
|
725
|
+
const decisionInput = buildApplyDecisionInput({
|
|
726
|
+
item,
|
|
727
|
+
type: sidecarTypeFor(input.namespace),
|
|
728
|
+
itemPath,
|
|
729
|
+
cwd,
|
|
730
|
+
model: context.applyModel,
|
|
731
|
+
});
|
|
732
|
+
if (decisionInput === undefined) {
|
|
733
|
+
// No sidecar / item to decide over (a sibling leg removed it between classify
|
|
734
|
+
// and run) — the same benign clean-exit the persist's vanished branch gives.
|
|
735
|
+
return vanishedSkip({ rung: 'apply', item });
|
|
736
|
+
}
|
|
737
|
+
const decider = context.applyDecide ?? harnessApplyDecider();
|
|
738
|
+
let verdict;
|
|
739
|
+
try {
|
|
740
|
+
verdict = await decide(decisionInput, decider, APPLY_ALLOWED_OUTCOMES);
|
|
741
|
+
}
|
|
742
|
+
catch (err) {
|
|
743
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
744
|
+
// A DisallowedOutcomeError (e.g. a stubbed `adr` verdict) and an agent-failed
|
|
745
|
+
// parse both degrade HONESTLY onto a usage-error — never a silent dispatch.
|
|
746
|
+
const label = err instanceof DisallowedOutcomeError
|
|
747
|
+
? `apply ${item}: the decision verdict is not an allowed outcome (${detail})`
|
|
748
|
+
: `apply ${item}: the decision agent produced no usable verdict (${detail})`;
|
|
749
|
+
return { exitCode: 1, outcome: 'usage-error', message: label };
|
|
750
|
+
}
|
|
751
|
+
if (verdict.outcome === 'ask') {
|
|
752
|
+
// ask-follow-up → the EXISTING append/re-pause loop. One BATCH of follow-ups.
|
|
753
|
+
const apply = context.applyPersist ?? applyAnsweredQuestions;
|
|
754
|
+
const question = (verdict.question ?? '').trim();
|
|
755
|
+
if (question === '') {
|
|
756
|
+
return {
|
|
757
|
+
exitCode: 1,
|
|
758
|
+
outcome: 'usage-error',
|
|
759
|
+
message: `apply ${item}: the decision agent chose 'ask' but emitted no follow-up question.`,
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
try {
|
|
763
|
+
const result = apply({
|
|
764
|
+
cwd,
|
|
765
|
+
item,
|
|
766
|
+
itemPath,
|
|
767
|
+
appendQuestions: [{ question }],
|
|
768
|
+
note,
|
|
769
|
+
});
|
|
770
|
+
return {
|
|
771
|
+
exitCode: 0,
|
|
772
|
+
outcome: result.outcome === 'vanished' ? 'vanished' : 'no-op',
|
|
773
|
+
message: result.message,
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
catch (err) {
|
|
777
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
778
|
+
return {
|
|
779
|
+
exitCode: 1,
|
|
780
|
+
outcome: 'usage-error',
|
|
781
|
+
message: `apply ${item}: ${detail}`,
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (verdict.outcome === 'task' || verdict.outcome === 'spec') {
|
|
786
|
+
// mint-task / mint-spec → CAS-create a SELF-CONTAINED artifact + delete the
|
|
787
|
+
// source + sidecar in the SAME commit (delete-on-promote, preserved). The
|
|
788
|
+
// verdict's drafted body (when present) seeds the new item; else the writer
|
|
789
|
+
// builds a self-contained body FROM the observation (carrying the answers +
|
|
790
|
+
// open-question scoping). The artifact type is the agent's VERDICT.
|
|
791
|
+
const promote = context.promote ?? promoteObservation;
|
|
792
|
+
const draftedBody = verdict.outcome === 'task' ? verdict.taskBody : verdict.specBody;
|
|
793
|
+
const draftedSlug = verdict.outcome === 'task' ? verdict.taskSlug : verdict.specSlug;
|
|
794
|
+
try {
|
|
795
|
+
const result = await promote({
|
|
796
|
+
cwd,
|
|
797
|
+
item,
|
|
798
|
+
itemPath,
|
|
799
|
+
artifact: verdict.outcome,
|
|
800
|
+
newSlug: context.promoteSlug ?? draftedSlug,
|
|
801
|
+
...(draftedBody !== undefined && draftedBody.trim() !== ''
|
|
802
|
+
? { stubContent: draftedBody }
|
|
803
|
+
: {}),
|
|
804
|
+
arbiter: context.arbiter,
|
|
805
|
+
// Lifecycle FAN-OUT: widen the CAS contention budget + jitter the retries
|
|
806
|
+
// so N parallel promote legs desync (task
|
|
807
|
+
// `jitter-and-widen-cas-contention-retry-for-lifecycle-fanout`).
|
|
808
|
+
contention: LIFECYCLE_CAS_CONTENTION,
|
|
809
|
+
note,
|
|
810
|
+
});
|
|
811
|
+
return {
|
|
812
|
+
exitCode: result.exitCode,
|
|
813
|
+
outcome: result.outcome === 'promoted'
|
|
814
|
+
? 'advanced'
|
|
815
|
+
: result.outcome === 'lost'
|
|
816
|
+
? 'lost'
|
|
817
|
+
: result.outcome === 'contended'
|
|
818
|
+
? 'contended'
|
|
819
|
+
: 'usage-error',
|
|
820
|
+
message: result.message,
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
catch (err) {
|
|
824
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
825
|
+
return {
|
|
826
|
+
exitCode: 1,
|
|
827
|
+
outcome: 'usage-error',
|
|
828
|
+
message: `apply ${item}: ${detail}`,
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
if (verdict.outcome === 'adr') {
|
|
833
|
+
// mint-adr → CAS-create a SELF-CONTAINED ADR into `docs/adr/` + delete the
|
|
834
|
+
// source + sidecar in the SAME commit (delete-on-promote, preserved via the
|
|
835
|
+
// shared create-CAS). An ADR lives OUTSIDE the work board, so this is the
|
|
836
|
+
// SIBLING route (NOT a `promoteObservation` artifact type). The verdict's
|
|
837
|
+
// drafted body (when present) seeds the ADR; else `mintAdr` builds a
|
|
838
|
+
// self-contained body FROM the observation + the answered question(s).
|
|
839
|
+
const mint = context.mintAdr ?? mintAdr;
|
|
840
|
+
const answers = decisionInput.sidecar.entries.map((e) => ({
|
|
841
|
+
question: e.question,
|
|
842
|
+
answer: e.answer,
|
|
843
|
+
}));
|
|
844
|
+
try {
|
|
845
|
+
const result = await mint({
|
|
846
|
+
cwd,
|
|
847
|
+
item,
|
|
848
|
+
itemPath,
|
|
849
|
+
adrSlug: context.promoteSlug ?? verdict.adrSlug,
|
|
850
|
+
...(verdict.adrTitle !== undefined ? { adrTitle: verdict.adrTitle } : {}),
|
|
851
|
+
...(verdict.adrBody !== undefined && verdict.adrBody.trim() !== ''
|
|
852
|
+
? { adrBody: verdict.adrBody }
|
|
853
|
+
: {}),
|
|
854
|
+
answers,
|
|
855
|
+
arbiter: context.arbiter,
|
|
856
|
+
// Lifecycle FAN-OUT: widen the CAS contention budget + jitter the retries
|
|
857
|
+
// so N parallel mint-adr legs desync (task
|
|
858
|
+
// `jitter-and-widen-cas-contention-retry-for-lifecycle-fanout`).
|
|
859
|
+
contention: LIFECYCLE_CAS_CONTENTION,
|
|
860
|
+
note,
|
|
861
|
+
});
|
|
862
|
+
return {
|
|
863
|
+
exitCode: result.exitCode,
|
|
864
|
+
outcome: result.outcome === 'minted'
|
|
865
|
+
? 'advanced'
|
|
866
|
+
: result.outcome === 'lost'
|
|
867
|
+
? 'lost'
|
|
868
|
+
: result.outcome === 'contended'
|
|
869
|
+
? 'contended'
|
|
870
|
+
: 'usage-error',
|
|
871
|
+
message: result.message,
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
catch (err) {
|
|
875
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
876
|
+
return {
|
|
877
|
+
exitCode: 1,
|
|
878
|
+
outcome: 'usage-error',
|
|
879
|
+
message: `apply ${item}: ${detail}`,
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
// delete-source → discharge by deletion (DIRECT, no confirm — decision 12). The
|
|
884
|
+
// human's answer is the source of truth; the deletion is a single revertible
|
|
885
|
+
// commit with the reason in the message.
|
|
886
|
+
const apply = context.applyPersist ?? applyAnsweredQuestions;
|
|
887
|
+
try {
|
|
888
|
+
const result = apply({
|
|
889
|
+
cwd,
|
|
890
|
+
item,
|
|
891
|
+
itemPath,
|
|
892
|
+
discharge: { reason: verdict.deleteReason ?? '' },
|
|
893
|
+
note,
|
|
894
|
+
});
|
|
895
|
+
return {
|
|
896
|
+
exitCode: 0,
|
|
897
|
+
outcome: result.outcome === 'vanished' ? 'vanished' : 'advanced',
|
|
898
|
+
message: result.message,
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
catch (err) {
|
|
902
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
903
|
+
return {
|
|
904
|
+
exitCode: 1,
|
|
905
|
+
outcome: 'usage-error',
|
|
906
|
+
message: `apply ${item}: ${detail}`,
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Run ONE `advance` tick over a SINGLE named item: classify → lock → dispatch →
|
|
912
|
+
* release. The pure tick the drivers (later tasks) wrap. The expensive phase is
|
|
913
|
+
* ALWAYS post-lock — a CAS loser backs off having done ONLY the free
|
|
914
|
+
* classification (it never reaches the executor).
|
|
915
|
+
*/
|
|
916
|
+
export async function performAdvance(options) {
|
|
917
|
+
const note = options.note ?? (() => { });
|
|
918
|
+
const cwd = options.cwd;
|
|
919
|
+
const repoPath = options.repoPath ?? cwd;
|
|
920
|
+
const arbiter = options.arbiter ?? DEFAULT_ARBITER;
|
|
921
|
+
// {@link performAdvance} is the SINGLE-item tick. The bare `advance`
|
|
922
|
+
// (eligible-SET) form is the DRIVER's job ({@link performAdvanceAuto} in
|
|
923
|
+
// `advance-drivers.ts`, which selects over the pool + runs THIS tick per item) —
|
|
924
|
+
// the tick itself REQUIRES a named item, so an empty arg here is a usage error
|
|
925
|
+
// (the CLI dispatches the bare form to the driver before reaching here).
|
|
926
|
+
if (options.arg === undefined || options.arg.trim() === '') {
|
|
927
|
+
const message = '`advance` with no item is the eligible-SET form (the one-shot driver). ' +
|
|
928
|
+
'The single-item tick needs a named item: ' +
|
|
929
|
+
'`advance <slug>` / `advance prd:<slug>` / `advance obs:<slug>`.';
|
|
930
|
+
note(message);
|
|
931
|
+
return { exitCode: 1, outcome: 'usage-error', message };
|
|
932
|
+
}
|
|
933
|
+
// 1. RESOLVE the arg via the SHARED resolver (extended with `obs:`). `advance`
|
|
934
|
+
// spans task / prd / observation; a collision / bad arg is a loud usage error.
|
|
935
|
+
let resolved;
|
|
936
|
+
try {
|
|
937
|
+
resolved = resolveAdvanceArg({
|
|
938
|
+
arg: options.arg,
|
|
939
|
+
repoPath,
|
|
940
|
+
read: options.read ?? ledgerRead,
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
catch (err) {
|
|
944
|
+
if (err instanceof SlugResolutionError) {
|
|
945
|
+
return { exitCode: 1, outcome: 'usage-error', message: err.message };
|
|
946
|
+
}
|
|
947
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
948
|
+
return { exitCode: 1, outcome: 'usage-error', message };
|
|
949
|
+
}
|
|
950
|
+
const type = sidecarTypeFor(resolved.namespace);
|
|
951
|
+
const item = `${resolved.namespace}:${resolved.slug}`;
|
|
952
|
+
// 2. CLASSIFY — read-only, NO model, NO lock. Read the two signals + run the
|
|
953
|
+
// pure classifier. This is the ONLY work a CAS loser will have spent.
|
|
954
|
+
const readSignals = options.readSignals ?? readItemSignals;
|
|
955
|
+
const signals = readSignals({ repoPath, type, slug: resolved.slug, item });
|
|
956
|
+
const classification = classifyTick({
|
|
957
|
+
type,
|
|
958
|
+
needsAnswers: signals.needsAnswers,
|
|
959
|
+
sidecar: signals.sidecar,
|
|
960
|
+
});
|
|
961
|
+
// A NO-OP (pending sidecar / nothing eligible) or an invariant violation never
|
|
962
|
+
// takes the lock — there is nothing to execute, so do NOT pay the CAS.
|
|
963
|
+
if (classification.kind === 'no-op') {
|
|
964
|
+
const message = `no-op for ${item} (${classification.reason ?? 'nothing to advance'}).`;
|
|
965
|
+
note(message);
|
|
966
|
+
return {
|
|
967
|
+
exitCode: 0,
|
|
968
|
+
outcome: 'no-op',
|
|
969
|
+
rung: 'no-op',
|
|
970
|
+
slug: resolved.slug,
|
|
971
|
+
message,
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
if (classification.kind === 'invariant-violation') {
|
|
975
|
+
const message = `refusing to advance ${item}: the \`needsAnswers\` flag and the sidecar ` +
|
|
976
|
+
`disagree (${classification.reason ?? 'invariant violation'}). ` +
|
|
977
|
+
`A human must reconcile them.`;
|
|
978
|
+
note(message);
|
|
979
|
+
return {
|
|
980
|
+
exitCode: 1,
|
|
981
|
+
outcome: 'invariant-violation',
|
|
982
|
+
rung: 'invariant-violation',
|
|
983
|
+
slug: resolved.slug,
|
|
984
|
+
message,
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
// 3. LOCK — take the `advancing` CAS borrow for the classified rung, keyed on
|
|
988
|
+
// the item's `<type>-<slug>` identity. The expensive phase is POST-lock.
|
|
989
|
+
//
|
|
990
|
+
// UNIFIED PER-ITEM LOCK, TREE-LESS RUNGS ONLY (prd
|
|
991
|
+
// `ledger-status-per-item-lock-refs` US #1/#3/#18; ADR
|
|
992
|
+
// `ledger-status-on-per-item-lock-refs`). The rung kind is KNOWN here
|
|
993
|
+
// (`classification.kind`, classified pre-lock), so the tree-less-only policy
|
|
994
|
+
// lives HERE — where the rung is known — and `advancing-lock.ts` stays
|
|
995
|
+
// rung-agnostic (it only learns "unified or not" via `acquireUnified`). For a
|
|
996
|
+
// TREE-LESS rung (`surface`/`apply`/`triage-observation`) the advancing acquire
|
|
997
|
+
// ALSO takes the item's unified lock (`action: advance`) — these rungs have NO
|
|
998
|
+
// inner `do`, so the unified hold is what realises advance∥claim / advance∥task
|
|
999
|
+
// exclusion. For a BUILD-TASK / TASK-PRD rung we do NOT take the unified lock
|
|
1000
|
+
// at the advance layer: `performAdvance` ORCHESTRATES an inner `performDo` that
|
|
1001
|
+
// ITSELF acquires the SAME `task-<slug>`/`prd-<slug>` ref (the create-only CAS
|
|
1002
|
+
// with NO re-entrancy/auto-steal, per the ADR), so taking it here too would
|
|
1003
|
+
// DEADLOCK the tick against itself. The inner `do`'s claim/task lock IS the
|
|
1004
|
+
// single exclusion point for those rungs. The `work/advancing/<entry>.md` marker
|
|
1005
|
+
// CAS is KEPT for ALL rungs (its removal is the capstone task #9).
|
|
1006
|
+
const unifiedForRung = isTreeLessRung(classification.kind);
|
|
1007
|
+
const acquire = options.acquireLock ??
|
|
1008
|
+
((lockItem) => acquireAdvancingLock({
|
|
1009
|
+
item: lockItem,
|
|
1010
|
+
cwd,
|
|
1011
|
+
arbiter,
|
|
1012
|
+
acquireUnified: unifiedForRung,
|
|
1013
|
+
note,
|
|
1014
|
+
}));
|
|
1015
|
+
const lock = await acquire(item);
|
|
1016
|
+
if (lock.exitCode !== 0) {
|
|
1017
|
+
// A CAS LOSER (exit 2) or contended (exit 3) backs off having spent ONLY the
|
|
1018
|
+
// free classification above — it never reaches the executor.
|
|
1019
|
+
return {
|
|
1020
|
+
exitCode: lock.exitCode,
|
|
1021
|
+
outcome: lock.outcome === 'lost' ? 'lost' : 'contended',
|
|
1022
|
+
rung: classification.kind,
|
|
1023
|
+
slug: resolved.slug,
|
|
1024
|
+
message: lock.message,
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
// 4. EXECUTE — WINNER ONLY: dispatch the classified rung to the executor seam,
|
|
1028
|
+
// then ALWAYS release the borrow (the item never moved; release is clean).
|
|
1029
|
+
const executor = options.executor ?? defaultRungExecutor;
|
|
1030
|
+
const release = options.releaseLock ??
|
|
1031
|
+
((lockItem) => releaseAdvancingLock({
|
|
1032
|
+
item: lockItem,
|
|
1033
|
+
cwd,
|
|
1034
|
+
arbiter,
|
|
1035
|
+
releaseUnified: unifiedForRung,
|
|
1036
|
+
note,
|
|
1037
|
+
}));
|
|
1038
|
+
try {
|
|
1039
|
+
const exec = await dispatchRung(executor, {
|
|
1040
|
+
item,
|
|
1041
|
+
namespace: resolved.namespace,
|
|
1042
|
+
slug: resolved.slug,
|
|
1043
|
+
classification,
|
|
1044
|
+
context: {
|
|
1045
|
+
cwd,
|
|
1046
|
+
arbiter,
|
|
1047
|
+
doOptions: options.doOptions,
|
|
1048
|
+
doDriver: options.doDriver,
|
|
1049
|
+
surfaceGate: options.surfaceGate,
|
|
1050
|
+
surfaceModel: options.surfaceModel,
|
|
1051
|
+
surfacePersist: options.surfacePersist,
|
|
1052
|
+
applyPersist: options.applyPersist,
|
|
1053
|
+
applyFollowups: options.applyFollowups,
|
|
1054
|
+
applyDecide: options.applyDecide,
|
|
1055
|
+
applyModel: options.applyModel,
|
|
1056
|
+
observationTriage: options.observationTriage,
|
|
1057
|
+
triageGate: options.triageGate,
|
|
1058
|
+
triageModel: options.triageModel,
|
|
1059
|
+
autoDisposition: options.autoDisposition,
|
|
1060
|
+
promote: options.promote,
|
|
1061
|
+
mintAdr: options.mintAdr,
|
|
1062
|
+
promoteSlug: options.promoteSlug,
|
|
1063
|
+
// The answered-merge LAND dispatch context (task
|
|
1064
|
+
// `apply-rung-merge-disposition`): threaded into the apply rung's
|
|
1065
|
+
// kind-check for a `kind: merge` runner-action.
|
|
1066
|
+
workspacesDir: options.workspacesDir,
|
|
1067
|
+
arbiterUrl: options.arbiterUrl,
|
|
1068
|
+
prepare: options.prepare,
|
|
1069
|
+
verify: options.verify,
|
|
1070
|
+
strictMergeApproval: options.strictMergeApproval,
|
|
1071
|
+
mergeAction: options.mergeAction,
|
|
1072
|
+
note,
|
|
1073
|
+
},
|
|
1074
|
+
});
|
|
1075
|
+
return {
|
|
1076
|
+
exitCode: exec.exitCode,
|
|
1077
|
+
outcome: exec.outcome,
|
|
1078
|
+
rung: classification.kind,
|
|
1079
|
+
slug: resolved.slug,
|
|
1080
|
+
message: exec.message,
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
finally {
|
|
1084
|
+
await release(item);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Is this a TREE-LESS rung (`surface`/`apply`/`triage-observation`) — the rungs
|
|
1089
|
+
* that have NO inner `performDo`, so the advancing acquire must ALSO take the
|
|
1090
|
+
* unified per-item lock (`action: advance`) to realise advance∥claim / advance∥task
|
|
1091
|
+
* exclusion? The build/task rungs (`build-task`/`task-spec`) are the inverse:
|
|
1092
|
+
* their inner `do` holds the SAME unified ref, so the advance layer must NOT take
|
|
1093
|
+
* it (it would deadlock the tick against itself). `no-op`/`invariant-violation`
|
|
1094
|
+
* never reach the lock step. This is the single place the tree-less-only policy
|
|
1095
|
+
* is expressed (the rung is known here); `advancing-lock.ts` stays rung-agnostic.
|
|
1096
|
+
*/
|
|
1097
|
+
function isTreeLessRung(kind) {
|
|
1098
|
+
return (kind === 'surface' || kind === 'apply' || kind === 'triage-observation');
|
|
1099
|
+
}
|
|
1100
|
+
/** Dispatch the classified rung to the executor seam (winner-only). */
|
|
1101
|
+
function dispatchRung(executor, input) {
|
|
1102
|
+
switch (input.classification.kind) {
|
|
1103
|
+
case 'build-task':
|
|
1104
|
+
return executor.buildTask(input);
|
|
1105
|
+
case 'task-spec':
|
|
1106
|
+
return executor.taskSpec(input);
|
|
1107
|
+
case 'triage-observation':
|
|
1108
|
+
return executor.triageObservation(input);
|
|
1109
|
+
case 'surface':
|
|
1110
|
+
return executor.surface(input);
|
|
1111
|
+
case 'apply':
|
|
1112
|
+
return executor.apply(input);
|
|
1113
|
+
default:
|
|
1114
|
+
// `no-op` / `invariant-violation` never reach here (handled pre-lock).
|
|
1115
|
+
return Promise.resolve({
|
|
1116
|
+
exitCode: 1,
|
|
1117
|
+
outcome: 'usage-error',
|
|
1118
|
+
message: `unexpected rung kind '${input.classification.kind}' at dispatch.`,
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
//# sourceMappingURL=advance.js.map
|