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/run.js
ADDED
|
@@ -0,0 +1,963 @@
|
|
|
1
|
+
import { resolvePromptGuidance } from './config.js';
|
|
2
|
+
import { resolveRepoConfig } from './repo-config.js';
|
|
3
|
+
import { scan } from './scan.js';
|
|
4
|
+
import { selectCandidates } from './select.js';
|
|
5
|
+
import { performClaim } from './claim-cas.js';
|
|
6
|
+
import { updateJobRecord, encodeWorkId } from './workspace.js';
|
|
7
|
+
import { encodeRepoKey } from './repo-mirror.js';
|
|
8
|
+
import { run as runProcess } from './git.js';
|
|
9
|
+
import { mkdirSync, readFileSync, rmSync } from 'node:fs';
|
|
10
|
+
import { ledgerWrite } from './ledger-write.js';
|
|
11
|
+
import { jobWorktreeStrategy, } from './isolation.js';
|
|
12
|
+
import { runConcurrent, createKeyedLock } from './concurrency.js';
|
|
13
|
+
import { createHarness } from './pi-harness.js';
|
|
14
|
+
import { generateSessionPath } from './session-path.js';
|
|
15
|
+
import { resolveTask, buildAgentPrompt, resolveContinueContext, resolvePromptGuidanceForItem, PromptError, } from './prompt.js';
|
|
16
|
+
import { parseStopSentinel, isWorkBranchDiffEmpty, emptyDiffStopReason, } from './agent-stop.js';
|
|
17
|
+
import { classifyFailureCause, failureCauseLabel, } from './failure-cause.js';
|
|
18
|
+
import { performIntegration } from './integration-core.js';
|
|
19
|
+
import { shouldFailProposePrIntent, PROPOSE_PR_INTENT_GH_UNAVAILABLE_MESSAGE, } from './do-config.js';
|
|
20
|
+
import { checkGatePreconditions, detectLockfileOnDisk, detectLockfileOnMirrorMain, } from './gate-readiness.js';
|
|
21
|
+
import { isGitHubArbiterUrl, GitHubProvider } from './github.js';
|
|
22
|
+
import { identityEnv, assertTransportAllowed } from './identity.js';
|
|
23
|
+
import { tmpdir } from 'node:os';
|
|
24
|
+
import { join, dirname } from 'node:path';
|
|
25
|
+
/** Is `repoPath` a BARE git repository (a registry hub mirror has no work tree)? */
|
|
26
|
+
function isBareRepo(repoPath, env) {
|
|
27
|
+
const result = runProcess('git', ['rev-parse', '--is-bare-repository'], repoPath, { env });
|
|
28
|
+
return result.status === 0 && result.stdout.trim() === 'true';
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Claim a slug against a repo that may be EITHER a working checkout (the in-place
|
|
32
|
+
* test/`do` path) OR a BARE registry hub mirror (`run`'s production discovery).
|
|
33
|
+
*
|
|
34
|
+
* The claim CAS needs a NON-bare checkout with an `origin/main` tracking ref and
|
|
35
|
+
* a work tree to commit in (a bare mirror cannot `git mv`/`git commit`). So for a
|
|
36
|
+
* bare mirror we clone it into a throwaway claim dir under `workspace` and claim
|
|
37
|
+
* THERE against `origin` (the mirror URL) — the EXACT pattern `do --remote` uses
|
|
38
|
+
* (`do.ts` §2–3). For a non-bare checkout we claim in place against the configured
|
|
39
|
+
* arbiter remote, unchanged. The returned `fromRepo` is what the job-worktree
|
|
40
|
+
* strategy then resolves the mirror off (the claim clone, or the checkout).
|
|
41
|
+
*/
|
|
42
|
+
async function claimAgainstRepo(opts) {
|
|
43
|
+
const { slug, repoPath, workspace, arbiter, env } = opts;
|
|
44
|
+
if (!isBareRepo(repoPath, env)) {
|
|
45
|
+
// In-place checkout: claim directly against the configured arbiter remote.
|
|
46
|
+
const claim = await performClaim({ slug, cwd: repoPath, arbiter, env });
|
|
47
|
+
return {
|
|
48
|
+
claim,
|
|
49
|
+
fromRepo: repoPath,
|
|
50
|
+
strategyArbiter: arbiter,
|
|
51
|
+
cleanup: () => { },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Bare hub mirror (the registry): clone it into a throwaway claim dir and claim
|
|
55
|
+
// there against `origin` (the mirror URL = the arbiter). Keyed per slug so two
|
|
56
|
+
// in-flight jobs in the same mirror get DISTINCT claim clones (worktree
|
|
57
|
+
// isolation — no shared mutable state across in-flight jobs).
|
|
58
|
+
const mirrorUrl = runProcess('git', ['remote', 'get-url', 'origin'], repoPath, {
|
|
59
|
+
env,
|
|
60
|
+
}).stdout.trim();
|
|
61
|
+
const url = mirrorUrl !== '' ? mirrorUrl : `file://${repoPath}`;
|
|
62
|
+
const claimDir = join(workspace, 'claim', `${encodeRepoKey(url).split('/').join('__')}__${slug}`);
|
|
63
|
+
rmSync(claimDir, { recursive: true, force: true });
|
|
64
|
+
mkdirSync(dirname(claimDir), { recursive: true });
|
|
65
|
+
runProcess('git', ['clone', '--quiet', url, claimDir], dirname(claimDir), {
|
|
66
|
+
env,
|
|
67
|
+
});
|
|
68
|
+
const claim = await performClaim({
|
|
69
|
+
slug,
|
|
70
|
+
cwd: claimDir,
|
|
71
|
+
arbiter: 'origin',
|
|
72
|
+
env,
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
claim,
|
|
76
|
+
fromRepo: claimDir,
|
|
77
|
+
// Inside the clone the arbiter is `origin` (the mirror URL).
|
|
78
|
+
strategyArbiter: 'origin',
|
|
79
|
+
cleanup: () => rmSync(claimDir, { recursive: true, force: true }),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Resolve the arbiter URL for `repoPath` PRE-CLAIM, the SAME way
|
|
84
|
+
* {@link claimAgainstRepo} derives the claim target. A BARE registry hub mirror's
|
|
85
|
+
* arbiter is its `origin` remote URL (falling back to `file://${repoPath}` when it
|
|
86
|
+
* has none); a working CHECKOUT's arbiter is the configured `arbiter` remote URL
|
|
87
|
+
* (falling back to `file://${repoPath}`). Used by the PR-INTENT pre-flight guard
|
|
88
|
+
* to know whether the arbiter is GitHub BEFORE the claim — it must not claim then
|
|
89
|
+
* refuse (that would strand the item in-progress on main). Read-only; mutates
|
|
90
|
+
* nothing.
|
|
91
|
+
*/
|
|
92
|
+
function arbiterUrlForRepo(opts) {
|
|
93
|
+
const { repoPath, arbiter, env } = opts;
|
|
94
|
+
const remote = isBareRepo(repoPath, env) ? 'origin' : arbiter;
|
|
95
|
+
const res = runProcess('git', ['remote', 'get-url', remote], repoPath, { env });
|
|
96
|
+
const url = res.status === 0 ? res.stdout.trim() : '';
|
|
97
|
+
return url !== '' ? url : `file://${repoPath}`;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Run one supervised tick: claim up to `maxParallel` eligible items
|
|
101
|
+
* (≤ `perRepoMax` per repo), run the agent on each in an isolated **job
|
|
102
|
+
* worktree** (the shared execution substrate — hub mirror + worktree + seams),
|
|
103
|
+
* gate on acceptance tests, rebase onto the latest arbiter main, and integrate
|
|
104
|
+
* the green ones. The runner owns EVERY git-state transition (claim, done-move,
|
|
105
|
+
* completion commit, integration); the agent only edits code. A lost race (claim
|
|
106
|
+
* exit 2) is skipped cleanly; failing work never reaches `work/done/`; a rebase
|
|
107
|
+
* conflict is aborted and routed to needs-attention (ADR §10).
|
|
108
|
+
*/
|
|
109
|
+
export async function runOnce(options) {
|
|
110
|
+
const config = options.config;
|
|
111
|
+
// Default discovery is the REGISTRY (the hub-mirror set, ADR §1) — the reframe
|
|
112
|
+
// that landed in `run-daemon-reframe` (work/done/). Callers that still operate
|
|
113
|
+
// on WORKING CHECKOUTS (the CLI / its tests) inject an explicit `report` built
|
|
114
|
+
// from working-tree paths ({@link scanRepoPaths}); without an injected report
|
|
115
|
+
// we fall back to the registry scan (async).
|
|
116
|
+
const report = options.report ?? (await scan(config, { override: options.override }));
|
|
117
|
+
const candidates = selectCandidates(report, {
|
|
118
|
+
maxParallel: config.maxParallel,
|
|
119
|
+
perRepoMax: config.perRepoMax,
|
|
120
|
+
});
|
|
121
|
+
// The launch harness: an explicit injection wins (tests); otherwise build the
|
|
122
|
+
// adapter the config selects (`harness: 'pi'` ⇒ the pi CLI; default ⇒ null,
|
|
123
|
+
// shelling out to `agentCmd`). pi specifics stay behind the seam.
|
|
124
|
+
const harness = options.harness ??
|
|
125
|
+
createHarness({ harness: config.harness, piBin: config.piBin });
|
|
126
|
+
// An explicitly-injected provider INSTANCE (tests / embedding) wins over
|
|
127
|
+
// per-item auto-detection; otherwise each item selects its own provider PURELY
|
|
128
|
+
// from its arbiter URL (see runOneItem). This is the resolved provider OBJECT,
|
|
129
|
+
// not a config override (there is none — the provider is arbiter-derived).
|
|
130
|
+
const provider = options.provider;
|
|
131
|
+
const workspace = options.workspace ?? config.workspacesDir;
|
|
132
|
+
const env = options.env;
|
|
133
|
+
// The per-repo claim serialiser: the ONLY shared-working-tree step in the
|
|
134
|
+
// otherwise-concurrent tick is the claim (it prepares its micro-commit in the
|
|
135
|
+
// repo's checkout/mirror). Two concurrent claims in ONE repo would corrupt each
|
|
136
|
+
// other's HEAD/index, so we serialise the CLAIM per repo (cheap — mv + commit +
|
|
137
|
+
// CAS push) while the agent run + the rebase-or-abort integration stay fully
|
|
138
|
+
// concurrent in each job's OWN worktree. Distinct repos claim in parallel.
|
|
139
|
+
const claimLock = createKeyedLock();
|
|
140
|
+
// The per-repo INTEGRATE serialiser — the SIBLING of `claimLock`, the same
|
|
141
|
+
// `createKeyedLock` primitive keyed on the same repo key. The land-on-`main`
|
|
142
|
+
// TAIL of merge-mode integration (fetch+rebase → `${branch}:main` push) is a
|
|
143
|
+
// shared-`main` step: two concurrent SAME-repo merge jobs both rebase onto the
|
|
144
|
+
// same pre-merge base and the loser's plain push is non-fast-forward (today it
|
|
145
|
+
// only works by benign timing). Serialising ONLY that tail per repo (the seam
|
|
146
|
+
// is applied INSIDE `performIntegration`, around step 4–5, NOT the whole call —
|
|
147
|
+
// the slow gate + Gate-2 review stay at the front, OUTSIDE the lock, so they
|
|
148
|
+
// run concurrently) makes the loser re-fetch + rebase onto the winner's
|
|
149
|
+
// now-advanced main, so its push is a clean fast-forward. Distinct repos
|
|
150
|
+
// integrate in parallel (per-repo key); a genuine same-repo code conflict
|
|
151
|
+
// routes exactly ONE job to needs-attention (it cannot both-land).
|
|
152
|
+
//
|
|
153
|
+
// This lock is the SIBLING-INTEGRATE serialiser ONLY. It does NOT cover the
|
|
154
|
+
// claim-vs-integrate race: a sibling same-repo CLAIM advances `<arbiter>/main`
|
|
155
|
+
// under the SEPARATE `claimLock` (above), which can land INSIDE this job's
|
|
156
|
+
// integrate push window. That is closed independently by the integrator's
|
|
157
|
+
// bounded re-rebase-and-retry on a non-fast-forward merge push (task
|
|
158
|
+
// `run-fleet-claim-integrate-and-sibling-rebase-concurrency-safe`, Race 1) — the
|
|
159
|
+
// two locks are NOT merged (merging would serialise the cheap claim behind the
|
|
160
|
+
// slow gated integrate, killing run's parallelism), the retry handles the
|
|
161
|
+
// cross-lock main-advance instead.
|
|
162
|
+
const integrateLock = createKeyedLock();
|
|
163
|
+
// GENUINELY CONCURRENT (ADR §3 — the whole point of `run`): run up to
|
|
164
|
+
// `maxParallel` `runOneItem`s IN FLIGHT at once, capped at `perRepoMax`
|
|
165
|
+
// concurrently per repo. The caps now bound ACTUAL in-flight execution, not just
|
|
166
|
+
// selection (this replaced the old `for (const c) { await runOneItem }` loop
|
|
167
|
+
// that ran selected candidates one-at-a-time). The substrate is parallel-ready —
|
|
168
|
+
// each slug → a distinct `work/<slug>` branch → a distinct job worktree, and the
|
|
169
|
+
// arbiter CAS serialises claims (a loser gets dropped) — so the only shared state
|
|
170
|
+
// across in-flight jobs is read-only config; each job owns its own worktree.
|
|
171
|
+
// Integration ordering is independent per job: each rebases-or-aborts onto a
|
|
172
|
+
// moving `<arbiter>/main` on its own, and a conflict routes only THAT job to
|
|
173
|
+
// needs-attention (the executor never lets one job's failure abort the others).
|
|
174
|
+
const settled = await runConcurrent({
|
|
175
|
+
items: candidates,
|
|
176
|
+
maxInFlight: config.maxParallel,
|
|
177
|
+
keyFor: (candidate) => candidate.repoPath,
|
|
178
|
+
perKeyMax: config.perRepoMax,
|
|
179
|
+
worker: (candidate) => runOneItem(candidate, {
|
|
180
|
+
config,
|
|
181
|
+
override: options.override,
|
|
182
|
+
workspace,
|
|
183
|
+
dorfl: options.dorfl,
|
|
184
|
+
harness,
|
|
185
|
+
reviewGate: options.reviewGate,
|
|
186
|
+
provider,
|
|
187
|
+
ghCanOpenPr: options.ghCanOpenPr,
|
|
188
|
+
openPr: options.openPr,
|
|
189
|
+
env,
|
|
190
|
+
onWarn: options.onWarn,
|
|
191
|
+
backoff: options.backoff,
|
|
192
|
+
sleep: options.sleep,
|
|
193
|
+
claimLock,
|
|
194
|
+
integrateLock,
|
|
195
|
+
}),
|
|
196
|
+
});
|
|
197
|
+
// `runOneItem` is total (it maps every failure to an `ItemResult` and never
|
|
198
|
+
// throws), so a captured `{error}` slot is a defensive last resort — surface it
|
|
199
|
+
// as a claim-error item rather than crashing the whole tick.
|
|
200
|
+
const items = settled.map((slot, i) => 'ok' in slot
|
|
201
|
+
? slot.ok
|
|
202
|
+
: {
|
|
203
|
+
repoPath: candidates[i].repoPath,
|
|
204
|
+
slug: candidates[i].slug,
|
|
205
|
+
status: 'claim-error',
|
|
206
|
+
detail: slot.error?.message ?? String(slot.error),
|
|
207
|
+
});
|
|
208
|
+
const claimedAndDone = items.filter((i) => i.status === 'claimed-done').length;
|
|
209
|
+
const skipped = items.filter((i) => i.status === 'lost-race' || i.status === 'claim-contended').length;
|
|
210
|
+
const needsAttention = items.filter((i) => i.status === 'tests-failed' ||
|
|
211
|
+
i.status === 'needs-attention' ||
|
|
212
|
+
i.status === 'agent-stopped').length;
|
|
213
|
+
const failed = items.filter((i) => i.status === 'tests-failed' ||
|
|
214
|
+
i.status === 'needs-attention' ||
|
|
215
|
+
i.status === 'agent-stopped' ||
|
|
216
|
+
i.status === 'agent-failed' ||
|
|
217
|
+
// The FAILURE-CAUSE refinements of `agent-failed` count the SAME way (they
|
|
218
|
+
// are the same agent/run-failure routed to needs-attention, just labelled
|
|
219
|
+
// by cause).
|
|
220
|
+
i.status === 'transient-infra' ||
|
|
221
|
+
i.status === 'config-error' ||
|
|
222
|
+
// The surface to needs-attention did NOT land (lost the CAS race); the item
|
|
223
|
+
// is still in-progress on the arbiter — a genuine FAILURE (not a clean
|
|
224
|
+
// needs-attention), so it counts as failed, NOT in `needsAttention`.
|
|
225
|
+
i.status === 'surface-unmoved' ||
|
|
226
|
+
i.status === 'claim-error').length;
|
|
227
|
+
return { claimedAndDone, skipped, failed, needsAttention, items };
|
|
228
|
+
}
|
|
229
|
+
async function runOneItem(candidate, ctx) {
|
|
230
|
+
const { slug, repoPath } = candidate;
|
|
231
|
+
const base = { repoPath, slug, status: 'lost-race' };
|
|
232
|
+
// Resolve THIS repo's effective config against its own `.dorfl.json`
|
|
233
|
+
// layered over the global config (flag > per-repo > global > default). Each
|
|
234
|
+
// repo gets its own integration mode / arbiter, so repo A can be `merge`
|
|
235
|
+
// while repo B is `propose` in one tick.
|
|
236
|
+
const resolved = resolveRepoConfig({
|
|
237
|
+
repoPath,
|
|
238
|
+
global: ctx.config,
|
|
239
|
+
override: ctx.override,
|
|
240
|
+
});
|
|
241
|
+
const config = resolved.config;
|
|
242
|
+
if (resolved.message) {
|
|
243
|
+
ctx.onWarn?.(resolved.message);
|
|
244
|
+
}
|
|
245
|
+
// The IDENTITY env for this repo's GIT + provider operations (claim, push,
|
|
246
|
+
// integration, `gh`). Derived from the host-only `config.identity` (absent ⇒
|
|
247
|
+
// `ctx.env` unchanged, byte-for-byte ambient). This is the INTEGRATION-PATH
|
|
248
|
+
// env ONLY: the agent launch keeps the plain ambient `ctx.env` (the runner,
|
|
249
|
+
// not the agent, acts as the configured identity — design point 4). The push-
|
|
250
|
+
// time transport-coherence guard (`assertTransportAllowed`) fires before the
|
|
251
|
+
// integration push (where the arbiter URL is known), turning a forbidden
|
|
252
|
+
// transport into a clear error, never a silent wrong-account push.
|
|
253
|
+
let gitEnv;
|
|
254
|
+
try {
|
|
255
|
+
gitEnv = identityEnv(config.identity, ctx.env ?? process.env);
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
// A configured identity that cannot be resolved (e.g. `tokenEnv` names an
|
|
259
|
+
// unset env var) is a CONFIG error — fail THIS item cleanly (surfaced), never
|
|
260
|
+
// crash the tick or silently fall back to an ambient credential.
|
|
261
|
+
const message = err.message;
|
|
262
|
+
ctx.onWarn?.(message);
|
|
263
|
+
return { ...base, status: 'config-error', detail: message };
|
|
264
|
+
}
|
|
265
|
+
// A per-ITEM context carrying `gitEnv` so the needs-attention save helpers
|
|
266
|
+
// (`saveAgentFailure`/`saveAgentStop`) push under the identity too. We rebind a
|
|
267
|
+
// COPY (never mutate the shared tick `ctx` — items run concurrently).
|
|
268
|
+
ctx = { ...ctx, gitEnv };
|
|
269
|
+
// 0. PR-INTENT pre-flight guard — the AUTONOMOUS mirror of in-place `performDo`
|
|
270
|
+
// step 3c. The SAME predicate, run UP FRONT here (per-repo config resolved,
|
|
271
|
+
// BEFORE the claim) so a `propose` item on a GitHub arbiter that INTENDS a PR
|
|
272
|
+
// (`noPR` unset) fails fast when `gh` genuinely cannot open one, instead of
|
|
273
|
+
// letting integration silently degrade to manual-PR instructions. CRITICAL: the
|
|
274
|
+
// probe runs BEFORE the claim — claiming then refusing would strand the item
|
|
275
|
+
// in-progress on main (the claim precedes `prepare()`). The arbiter URL is
|
|
276
|
+
// derived from `repoPath` the SAME way `claimAgainstRepo` derives the claim
|
|
277
|
+
// target (`arbiterUrlForRepo`). The PROBE is the signal, NOT a config check —
|
|
278
|
+
// an absent `providers.github` identity falls back to AMBIENT `gh` auth (the
|
|
279
|
+
// probe reports it available), so a working ambient setup PROCEEDS. A true
|
|
280
|
+
// result returns a CLEAN PRE-CLAIM item result on the `config-error` failure-
|
|
281
|
+
// cause status (a wiring/config failure, NOT a task fault), with NO claim,
|
|
282
|
+
// build, or half-built needs-attention surface — and it never crashes the tick
|
|
283
|
+
// or aborts siblings (the result is returned; `runConcurrent` continues).
|
|
284
|
+
// REUSES the predicate + message so the in-place + autonomous paths cannot
|
|
285
|
+
// drift.
|
|
286
|
+
{
|
|
287
|
+
const arbiterUrl = arbiterUrlForRepo({
|
|
288
|
+
repoPath,
|
|
289
|
+
arbiter: config.defaultArbiter,
|
|
290
|
+
env: gitEnv,
|
|
291
|
+
});
|
|
292
|
+
const probe = ctx.ghCanOpenPr ??
|
|
293
|
+
((probeCwd, probeEnv) => new GitHubProvider().available(probeCwd, probeEnv));
|
|
294
|
+
if (shouldFailProposePrIntent({
|
|
295
|
+
mode: config.integration,
|
|
296
|
+
arbiterIsGitHub: isGitHubArbiterUrl(arbiterUrl),
|
|
297
|
+
noPR: config.noPR,
|
|
298
|
+
ghCanOpenPr: () => probe(repoPath, gitEnv),
|
|
299
|
+
})) {
|
|
300
|
+
return {
|
|
301
|
+
...base,
|
|
302
|
+
status: 'config-error',
|
|
303
|
+
detail: PROPOSE_PR_INTENT_GH_UNAVAILABLE_MESSAGE,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// 0b. STATIC fresh-worktree-gate readiness guard — the fleet mirror of
|
|
308
|
+
// `performDo` step 3d / `performDoRemote` step 1c (task
|
|
309
|
+
// `do-fails-fast-when-acceptance-gate-statically-unrunnable`). When the
|
|
310
|
+
// fresh-worktree gate is ON AND `prepare` resolves to no commands AND a
|
|
311
|
+
// lockfile is present in the repo, the throwaway worktree the gate runs in
|
|
312
|
+
// will have no installed deps, so the gate cannot run. Fail this item
|
|
313
|
+
// CLEANLY before the claim (no claim, no isolate, no agent, no surface) so a
|
|
314
|
+
// fleet tick never burns a build to discover a STATIC config gap and never
|
|
315
|
+
// routes correct work to needs-attention for this reason. Surfaced on the
|
|
316
|
+
// same `config-error` axis the PR-intent guard above uses (a wiring/config
|
|
317
|
+
// fault, not a task fault). The repo discovery is either a working checkout
|
|
318
|
+
// (in-place tests) OR a BARE hub mirror (production fleet); detect the
|
|
319
|
+
// lockfile from whichever shape `repoPath` is. Deps-only — there is no
|
|
320
|
+
// verify-unset case (the gate substitutes `DEFAULT_VERIFY_COMMAND`).
|
|
321
|
+
{
|
|
322
|
+
const lockfile = detectLockfileOnDisk(repoPath) ??
|
|
323
|
+
detectLockfileOnMirrorMain(repoPath, gitEnv);
|
|
324
|
+
const guard = checkGatePreconditions({
|
|
325
|
+
freshWorktreeGate: config.freshWorktreeGate,
|
|
326
|
+
prepare: config.prepare,
|
|
327
|
+
lockfile,
|
|
328
|
+
});
|
|
329
|
+
if (guard !== undefined) {
|
|
330
|
+
return { ...base, status: 'config-error', detail: guard.message };
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// 1. Claim (the runner's first git-state transition) via the in-process CAS.
|
|
334
|
+
// `claimAgainstRepo` handles BOTH discovery shapes: a working CHECKOUT (the
|
|
335
|
+
// in-place test/`do` path — claim directly) and a BARE registry hub mirror
|
|
336
|
+
// (`run`'s production discovery — claim in a throwaway clone, since a bare
|
|
337
|
+
// repo cannot `git mv`/`git commit`; the SAME pattern `do --remote` uses). It
|
|
338
|
+
// returns the `fromRepo`/`strategyArbiter` the worktree strategy resolves its
|
|
339
|
+
// mirror off (kept alive until AFTER prepare). `performClaim` is async, so two
|
|
340
|
+
// awaited claims over one slug genuinely race — the arbiter ref-CAS picks the
|
|
341
|
+
// winner.
|
|
342
|
+
const doClaim = () => claimAgainstRepo({
|
|
343
|
+
slug,
|
|
344
|
+
repoPath,
|
|
345
|
+
workspace: ctx.workspace,
|
|
346
|
+
arbiter: config.defaultArbiter,
|
|
347
|
+
env: gitEnv,
|
|
348
|
+
});
|
|
349
|
+
// Serialise the claim PER REPO (it prepares its micro-commit in a checkout/clone
|
|
350
|
+
// of the repo) so two in-flight jobs in the same repo do not stomp on each
|
|
351
|
+
// other; the arbiter CAS then picks the winner across machines. Different repos
|
|
352
|
+
// (and everything AFTER the claim) run concurrently.
|
|
353
|
+
const repoClaim = ctx.claimLock
|
|
354
|
+
? await ctx.claimLock(repoPath, doClaim)
|
|
355
|
+
: await doClaim();
|
|
356
|
+
const claim = repoClaim.claim;
|
|
357
|
+
if (claim.outcome === 'lost') {
|
|
358
|
+
repoClaim.cleanup();
|
|
359
|
+
return { ...base, status: 'lost-race' };
|
|
360
|
+
}
|
|
361
|
+
if (claim.outcome === 'contended') {
|
|
362
|
+
repoClaim.cleanup();
|
|
363
|
+
return { ...base, status: 'claim-contended' };
|
|
364
|
+
}
|
|
365
|
+
if (claim.outcome === 'usage-error') {
|
|
366
|
+
repoClaim.cleanup();
|
|
367
|
+
return { ...base, status: 'claim-error', detail: claim.message };
|
|
368
|
+
}
|
|
369
|
+
// 2. Isolate via the isolation-strategy seam (ADR §3). `run` always selects
|
|
370
|
+
// the JOB-WORKTREE strategy: materialise the hub mirror + cut a per-job
|
|
371
|
+
// worktree off the freshly-fetched `<hub>/main` in the agents' area
|
|
372
|
+
// (ADR §1/§2). The seam yields a UNIFORM HANDLE (dir/branch/arbiterRemote/
|
|
373
|
+
// arbiterUrl + a strategy-appropriate teardown) that the post-claim pipeline
|
|
374
|
+
// below reads from — never a concrete `Job` — so the SAME steps serve the
|
|
375
|
+
// in-place strategy too (wired by `do-in-place`). The mirror is keyed off
|
|
376
|
+
// the arbiter URL resolved from this repo, so jobs reuse it (fetch, not
|
|
377
|
+
// re-clone) across the run.
|
|
378
|
+
const strategy = jobWorktreeStrategy({
|
|
379
|
+
// In-place: the checkout. Bare-mirror: the throwaway claim CLONE (still alive)
|
|
380
|
+
// — the strategy resolves the mirror URL off it at prepare time, then we drop it.
|
|
381
|
+
fromRepo: repoClaim.fromRepo,
|
|
382
|
+
arbiter: repoClaim.strategyArbiter,
|
|
383
|
+
workspacesDir: ctx.workspace,
|
|
384
|
+
});
|
|
385
|
+
let tree;
|
|
386
|
+
try {
|
|
387
|
+
tree = strategy.prepare({ slug, env: gitEnv });
|
|
388
|
+
// The claim clone (bare-mirror path) has done its job: the mirror is
|
|
389
|
+
// materialised + the worktree cut. Drop it (no-op for the in-place path).
|
|
390
|
+
repoClaim.cleanup();
|
|
391
|
+
// 2a. CONTINUE rebase conflict (ADR §14 + §10): a requeue kept a
|
|
392
|
+
// `work/<slug>` whose commits did not replay cleanly onto the current
|
|
393
|
+
// main at onboard-time (aborted, never auto-resolved). Route the item to
|
|
394
|
+
// needs-attention TREE-LESSLY via the SAME `#89` mechanism `requeue` uses
|
|
395
|
+
// for the reverse direction — the rebase was ABORTED, so this worktree's
|
|
396
|
+
// `work/<slug>` tip == the arbiter tip (the kept branch, unchanged,
|
|
397
|
+
// after-commit). The DURABLE artifact is that branch on the arbiter + the
|
|
398
|
+
// main surface (ADR §14: the job worktree is a disposable cache; recovery
|
|
399
|
+
// flows through the branch + folder-native surfaces, NOT by editing the
|
|
400
|
+
// worktree). So the surface is purely the one-file `in-progress/ →
|
|
401
|
+
// needs-attention/` ledger move + reason — no branch push, no worktree
|
|
402
|
+
// mutation. Because the branch is provably on the arbiter, the §4 reap
|
|
403
|
+
// predicate still HOLDS and this worktree is reaped — more §14-aligned.
|
|
404
|
+
if (tree.continueRebaseConflict) {
|
|
405
|
+
const reason = `continuing the kept ${tree.branch}: rebase onto the latest main ` +
|
|
406
|
+
'conflicted (aborted, never auto-resolved) — resolve against the latest ' +
|
|
407
|
+
'main, or `requeue --reset` to discard and start fresh';
|
|
408
|
+
updateJobRecord(tree.dir, { state: 'needs-attention', reason });
|
|
409
|
+
const surfaced = await ledgerWrite.applyTreelessNeedsAttentionTransition({
|
|
410
|
+
cwd: tree.dir,
|
|
411
|
+
slug,
|
|
412
|
+
reason,
|
|
413
|
+
arbiter: tree.arbiterRemote,
|
|
414
|
+
env: gitEnv,
|
|
415
|
+
});
|
|
416
|
+
if (!surfaced.moved) {
|
|
417
|
+
return surfaceUnmovedItemResult({ base, tree, slug, reason, surfaced });
|
|
418
|
+
}
|
|
419
|
+
return { ...base, status: 'needs-attention', detail: reason };
|
|
420
|
+
}
|
|
421
|
+
// 2b. CONTINUE reconcile-push TERMINAL failure (the stale-lease-strand bug):
|
|
422
|
+
// the onboard reconcile push of the kept (already-committed) work branch
|
|
423
|
+
// FAILED terminally (stale-lease cap exhausted, or a non-stale-lease
|
|
424
|
+
// rejection / unreachable arbiter). The push helper THROWS; `createJob`
|
|
425
|
+
// CATCHES it and flags `continuePushFailure` so the tick does NOT crash
|
|
426
|
+
// leaving the task silently in-progress on the arbiter. Surface to
|
|
427
|
+
// needs-attention TREE-LESSLY via the SAME `#89` mechanism `requeue` uses —
|
|
428
|
+
// the kept branch already on the arbiter from the prior requeue
|
|
429
|
+
// (after-commit, recoverable), so the surface is purely the one-file ledger
|
|
430
|
+
// move + reason (no branch push, no worktree) — instead of running the agent.
|
|
431
|
+
if (tree.continuePushFailure !== undefined) {
|
|
432
|
+
const reason = `continuing the kept ${tree.branch}: publishing the rebased work branch ` +
|
|
433
|
+
`to the arbiter failed terminally (${tree.continuePushFailure}) — the ` +
|
|
434
|
+
'kept branch is left intact on the arbiter (recoverable); `requeue` to ' +
|
|
435
|
+
'retry once the churn settles, or `requeue --reset` to discard and start ' +
|
|
436
|
+
'fresh';
|
|
437
|
+
updateJobRecord(tree.dir, { state: 'needs-attention', reason });
|
|
438
|
+
const surfaced = await ledgerWrite.applyTreelessNeedsAttentionTransition({
|
|
439
|
+
cwd: tree.dir,
|
|
440
|
+
slug,
|
|
441
|
+
reason,
|
|
442
|
+
arbiter: tree.arbiterRemote,
|
|
443
|
+
env: gitEnv,
|
|
444
|
+
});
|
|
445
|
+
if (!surfaced.moved) {
|
|
446
|
+
return surfaceUnmovedItemResult({ base, tree, slug, reason, surfaced });
|
|
447
|
+
}
|
|
448
|
+
return { ...base, status: 'needs-attention', detail: reason };
|
|
449
|
+
}
|
|
450
|
+
// 3. Build the prompt — the SAME dual-use assembly `dorfl prompt`
|
|
451
|
+
// emits: the canonical wrapper (+ source prd) + the task's ## Prompt.
|
|
452
|
+
let prompt;
|
|
453
|
+
try {
|
|
454
|
+
// CONTINUE-aware resolution: only on a continue (the job continued a kept
|
|
455
|
+
// arbiter `work/<slug>`) may the task already be in `work/done/`; admit
|
|
456
|
+
// `done/` ONLY behind the tip-vs-arbiter stranded gate (story 5). In a bare
|
|
457
|
+
// hub mirror's worktree the refs are the LOCAL heads `work/<slug>` / `main`.
|
|
458
|
+
const task = resolveTask(tree.dir, slug, tree.continued
|
|
459
|
+
? {
|
|
460
|
+
cwd: tree.dir,
|
|
461
|
+
branchRef: tree.branch,
|
|
462
|
+
mainRef: 'main',
|
|
463
|
+
env: gitEnv,
|
|
464
|
+
}
|
|
465
|
+
: undefined);
|
|
466
|
+
// CONTINUE-mode (the `agent-prompt-continue-context` task): when the job
|
|
467
|
+
// CONTINUED a kept arbiter `work/<slug>` (a requeue), inject the continue
|
|
468
|
+
// block (prior diff + reason + handoff note). REUSE the SAME continue-
|
|
469
|
+
// detection — in a bare hub mirror's worktree the refs are the LOCAL heads
|
|
470
|
+
// `work/<slug>` and `main` (after `ensureMirror`'s mirror-style fetch).
|
|
471
|
+
const continueContext = tree.continued
|
|
472
|
+
? resolveContinueContext({
|
|
473
|
+
cwd: tree.dir,
|
|
474
|
+
slug,
|
|
475
|
+
arbiter: tree.arbiterRemote,
|
|
476
|
+
branchRef: tree.branch,
|
|
477
|
+
mainRef: 'main',
|
|
478
|
+
content: readFileSync(task.path, 'utf8'),
|
|
479
|
+
env: gitEnv,
|
|
480
|
+
})
|
|
481
|
+
: undefined;
|
|
482
|
+
// Thread the resolved per-repo nudge through the per-item override layer
|
|
483
|
+
// (a task or prd may pin `promptGuidance.testFirst` in its frontmatter,
|
|
484
|
+
// superseding the repo policy for THIS item) before the wrapper is built.
|
|
485
|
+
const itemGuidance = resolvePromptGuidanceForItem({
|
|
486
|
+
cwd: tree.dir,
|
|
487
|
+
repoResolved: resolvePromptGuidance(config),
|
|
488
|
+
taskContent: readFileSync(task.path, 'utf8'),
|
|
489
|
+
});
|
|
490
|
+
prompt = buildAgentPrompt(task.slug, task.prd, task.taskPrompt, {
|
|
491
|
+
cwd: tree.dir,
|
|
492
|
+
continueContext,
|
|
493
|
+
promptGuidance: itemGuidance,
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
catch (err) {
|
|
497
|
+
if (err instanceof PromptError) {
|
|
498
|
+
return await saveAgentFailure(base, tree, slug, err.message, ctx);
|
|
499
|
+
}
|
|
500
|
+
throw err;
|
|
501
|
+
}
|
|
502
|
+
// 4. Run the agent — via the injected runner (tests) or the harness seam
|
|
503
|
+
// (null adapter by default), shelling out to the configured agentCmd. The
|
|
504
|
+
// resolved per-repo `model` (ADR §13) flows through the seam to the adapter;
|
|
505
|
+
// a `{model}`-in-agentCmd misconfiguration surfaces as agent-failed.
|
|
506
|
+
let agent;
|
|
507
|
+
try {
|
|
508
|
+
agent = runAgent(ctx, tree, prompt, slug, config.agentCmd, config.model, config.sessionsDir);
|
|
509
|
+
}
|
|
510
|
+
catch (err) {
|
|
511
|
+
return await saveAgentFailure(base, tree, slug, err.message, ctx);
|
|
512
|
+
}
|
|
513
|
+
if (!agent.ok) {
|
|
514
|
+
return await saveAgentFailure(base, tree, slug, agent.detail ?? `the agent failed to build '${slug}'.`, ctx);
|
|
515
|
+
}
|
|
516
|
+
// 4b. HONOR a deliberate STOP (task `agent-stop-signal`) — the SAME detection
|
|
517
|
+
// in-place/remote `do` run, mirrored here. The agent exited cleanly but the
|
|
518
|
+
// CLAIM-PROTOCOL wrapper tells it to STOP on a DRIFTED/ambiguous task; an
|
|
519
|
+
// in-band sentinel carries its reason VERBATIM, and the empty-diff backstop
|
|
520
|
+
// catches a stop without one. Either routes to needs-attention (surfaced on
|
|
521
|
+
// the arbiter) and SKIPS the gate + Gate-2 (the whole `performIntegration`
|
|
522
|
+
// band) — a clean STOP is NOT "a build that changed nothing".
|
|
523
|
+
const sentinel = parseStopSentinel(agent.output);
|
|
524
|
+
const stopReason = sentinel !== undefined
|
|
525
|
+
? sentinel.reason
|
|
526
|
+
: (await isWorkBranchDiffEmpty({
|
|
527
|
+
cwd: tree.dir,
|
|
528
|
+
arbiter: tree.arbiterRemote,
|
|
529
|
+
env: gitEnv,
|
|
530
|
+
}))
|
|
531
|
+
? emptyDiffStopReason(slug)
|
|
532
|
+
: undefined;
|
|
533
|
+
if (stopReason !== undefined) {
|
|
534
|
+
return await saveAgentStop(base, tree, slug, stopReason, ctx);
|
|
535
|
+
}
|
|
536
|
+
// 5–7 (CONVERGED). The whole gate → review → done-move → commit → rebase →
|
|
537
|
+
// integrate band — plus the needs-attention routing on any failure — now runs
|
|
538
|
+
// through the SHARED `performIntegration` core (`integration-core.ts`, the
|
|
539
|
+
// run/do convergence prd). `run` no longer forks its own gate / done-move /
|
|
540
|
+
// completion commit / `Integrator`+`integrateWithRebase`: that closed all
|
|
541
|
+
// three drift instances at once (the fleet now gets the review gate, the PR
|
|
542
|
+
// title/body, AND the per-repo language-agnostic `verify` gate instead of the
|
|
543
|
+
// old test-only `pnpm -r test` floor). The HEAD above (claim, isolate, agent,
|
|
544
|
+
// failure-save) and the TAIL below (job record + worktree reap) stay here;
|
|
545
|
+
// the band is what they share. `run` is ALWAYS autonomous, so it ALWAYS
|
|
546
|
+
// passes `surfaceArbiter` (every failure surfaces on the arbiter's main +
|
|
547
|
+
// pushes the branch). The injected `openPr` legacy bridge is forwarded to the
|
|
548
|
+
// core unchanged; absent it the core selects the provider PURELY from the
|
|
549
|
+
// arbiter URL (a GitHub remote ⇒ `gh pr create`). The per-repo `noPR` INTENT
|
|
550
|
+
// (suppress the PR) rides separately — it does not pick a provider.
|
|
551
|
+
//
|
|
552
|
+
// `performIntegration` THROWS a plain `Error` for a misconfigured gate
|
|
553
|
+
// (`review` on with no `reviewGate` wired) — `run`'s CLI always wires one when
|
|
554
|
+
// `config.review` is on, so that is a defensive case, but it must NOT crash
|
|
555
|
+
// the whole tick. We catch it and route the item through the same
|
|
556
|
+
// work-preserving needs-attention seam an agent failure uses (`saveAgentFailure`)
|
|
557
|
+
// so the worktree is handled and the run continues to the next item.
|
|
558
|
+
// Push-time transport-coherence guard (identity feature): refuse a forbidden
|
|
559
|
+
// transport for THIS arbiter's actual URL rather than silently pushing under
|
|
560
|
+
// an ambient credential. A no-op when no identity is configured.
|
|
561
|
+
try {
|
|
562
|
+
assertTransportAllowed(config.identity, tree.arbiterUrl);
|
|
563
|
+
}
|
|
564
|
+
catch (err) {
|
|
565
|
+
return await saveAgentFailure(base, tree, slug, err.message, ctx);
|
|
566
|
+
}
|
|
567
|
+
let core;
|
|
568
|
+
try {
|
|
569
|
+
core = await performIntegration({
|
|
570
|
+
cwd: tree.dir,
|
|
571
|
+
// The arbiter remote name valid inside the isolated tree (job-worktree:
|
|
572
|
+
// the mirror's clone remote `origin`), NOT the source repo's
|
|
573
|
+
// `defaultArbiter` name.
|
|
574
|
+
arbiter: tree.arbiterRemote,
|
|
575
|
+
slug,
|
|
576
|
+
// Claim no longer moves the body (task
|
|
577
|
+
// `cutover-claim-body-stays-and-complete-sources-from-backlog`): a
|
|
578
|
+
// freshly-built task RESTS in `tasks/ready/` on `main`, so the done-move
|
|
579
|
+
// sources from there.
|
|
580
|
+
source: 'tasks-ready',
|
|
581
|
+
recovering: false,
|
|
582
|
+
// `run` is ALWAYS autonomous → surface every failure on the arbiter's
|
|
583
|
+
// main AND push the work branch (DATA, not a caller-identity flag).
|
|
584
|
+
surfaceArbiter: tree.arbiterRemote,
|
|
585
|
+
// The per-repo, language-agnostic gate (ADR §8) — the protocol-conformance
|
|
586
|
+
// fix: `run` now honours `config.verify` instead of the deleted
|
|
587
|
+
// `defaultTestGate`'s hardcoded `pnpm -r test`.
|
|
588
|
+
verify: config.verify,
|
|
589
|
+
// The per-repo ENV-PREP step (`prepare`), sequenced ONCE before the first
|
|
590
|
+
// `verify` on the fresh job worktree so it has deps (a fresh worktree off
|
|
591
|
+
// the mirror has no `node_modules`). Unset ⇒ a no-op; never baked into verify.
|
|
592
|
+
prepare: config.prepare,
|
|
593
|
+
// FRESH-WORKTREE GATE (task `gate-on-rebased-tip-fresh-worktree`): run the
|
|
594
|
+
// acceptance gate against the REBASED tip in a clean throwaway worktree (the
|
|
595
|
+
// tree that integrates) so a green gate provably describes the merged
|
|
596
|
+
// artifact. Passed UNCONDITIONALLY (the resolved flag) at ANY `perRepoMax`:
|
|
597
|
+
// the two PRE-EXISTING run-fleet same-repo races the gate's latency used to
|
|
598
|
+
// make deterministic are now CLOSED on their own merits (task
|
|
599
|
+
// `run-fleet-claim-integrate-and-sibling-rebase-concurrency-safe`) — RACE 1
|
|
600
|
+
// (claim-vs-integrate non-fast-forward push) by the integrator's bounded
|
|
601
|
+
// re-rebase-and-retry on the merge push, RACE 2 (sibling-slug divergent-base
|
|
602
|
+
// ledger rebase) by the step-4 sibling-ledger reconciliation arm — so the
|
|
603
|
+
// `perRepoMax === 1` downgrade is REMOVED and the fresh rebased-tip gate runs
|
|
604
|
+
// on the `run` fleet at any same-repo parallelism. Single-job callers
|
|
605
|
+
// (`do`/`--isolated`/`--remote`/`complete`) already pass it unconditionally.
|
|
606
|
+
freshWorktreeGate: config.freshWorktreeGate,
|
|
607
|
+
// The cross-job merge-serialiser CAS-retry cap (config `mergeRetries`) —
|
|
608
|
+
// the git-alone FLOOR of the cross-job land queue (prd `land-time-reverify-
|
|
609
|
+
// and-parallel-merge-ceiling` Story 5 / Applied Answer q1 (a)). Threaded
|
|
610
|
+
// from the per-repo resolved config so a wide-matrix CI's raised cap actually
|
|
611
|
+
// reaches the merge loop — across separate `run` fleet jobs the CAS loop IS
|
|
612
|
+
// the queue (`integrateLock` only serialises sibling integrates in ONE process).
|
|
613
|
+
mergeRetries: config.mergeRetries,
|
|
614
|
+
// Gate 2 (PR/code review): the per-repo resolved flags ride from `config`;
|
|
615
|
+
// only the gate SEAM is threaded through `ctx` (the CLI wires the prod
|
|
616
|
+
// `harnessReviewGate()` only when `config.review` is on).
|
|
617
|
+
review: config.review,
|
|
618
|
+
reviewModel: config.reviewModel,
|
|
619
|
+
reviewMaxRounds: config.reviewMaxRounds,
|
|
620
|
+
reviewGate: ctx.reviewGate,
|
|
621
|
+
mode: config.integration,
|
|
622
|
+
// PR-INTENT: the per-repo `noPR` (suppress the PR even on an authed GitHub
|
|
623
|
+
// arbiter). NOT a provider choice — the provider is purely arbiter-derived.
|
|
624
|
+
noPR: config.noPR,
|
|
625
|
+
// Provider: an injected `openPr` wins (legacy test bridge); otherwise the
|
|
626
|
+
// core selects PURELY from the arbiter URL (a GitHub remote ⇒ `gh pr
|
|
627
|
+
// create`, else push-only `none`). `providerInstance` (the resolved object
|
|
628
|
+
// the `run` seam injects) STAYS — it is a DIFFERENT `provider`.
|
|
629
|
+
providerInstance: ctx.provider,
|
|
630
|
+
openPr: ctx.openPr,
|
|
631
|
+
// The per-repo INTEGRATE serialiser (sibling of the claim lock): the
|
|
632
|
+
// core applies it around ONLY the rebase-to-integrate TAIL, keyed on
|
|
633
|
+
// the repo path (the same key the claim lock uses), so two same-repo
|
|
634
|
+
// merge jobs land on `main` one-at-a-time. Absent on single-job paths.
|
|
635
|
+
integrateLock: ctx.integrateLock,
|
|
636
|
+
integrateLockKey: repoPath,
|
|
637
|
+
// Half A/B: the synthesised single-line title + the agent's surfaced
|
|
638
|
+
// final summary as the PR body (the core scaffolds the task-pointer
|
|
639
|
+
// header). `title` is synthesised inside the core from the task's
|
|
640
|
+
// frontmatter; `body` is the agent output (undefined ⇒ `--fill`).
|
|
641
|
+
body: agent.output,
|
|
642
|
+
env: gitEnv,
|
|
643
|
+
// The review AGENT (Gate 2) launches AMBIENT — never the identity env
|
|
644
|
+
// (an agent must not act as the bot; only the runner's git ops do).
|
|
645
|
+
agentEnv: ctx.env,
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
catch (err) {
|
|
649
|
+
// A thrown core error (misconfigured gate, or an unexpected plumbing
|
|
650
|
+
// failure) — SAVE the work + route to needs-attention, never crash the tick.
|
|
651
|
+
return await saveAgentFailure(base, tree, slug, err.message, ctx);
|
|
652
|
+
}
|
|
653
|
+
// TAIL: map the core's DATA outcome onto the job record + `ItemStatus`. The
|
|
654
|
+
// core already ran the gate/review/move/commit/rebase/integrate AND did the
|
|
655
|
+
// needs-attention routing; `run`'s tail does ONLY the job-record write + (in
|
|
656
|
+
// `finally`) the worktree reap. `run` NEVER switches/ff/deletes branches
|
|
657
|
+
// (that is `do`'s in-place tail; a job worktree is reaped, not switched).
|
|
658
|
+
if (core.outcome === 'gate-failed') {
|
|
659
|
+
updateJobRecord(tree.dir, {
|
|
660
|
+
state: 'needs-attention',
|
|
661
|
+
reason: core.reason,
|
|
662
|
+
});
|
|
663
|
+
return { ...base, status: 'tests-failed', detail: core.reason };
|
|
664
|
+
}
|
|
665
|
+
if (core.outcome === 'review-unparseable') {
|
|
666
|
+
// Gate 2 RAN but its verdict was UNPARSEABLE (malformed JSON). The core already
|
|
667
|
+
// routed it work-preservingly (branch pushed + surfaced). FAILURE-CAUSE axis,
|
|
668
|
+
// NOT a reviewer block: classify the parse-failure phrase the core recorded →
|
|
669
|
+
// `transient-infra` (re-run the SAME work — the stochastic gate + the parser's
|
|
670
|
+
// repair pass make a re-run far more likely to parse), the SAME
|
|
671
|
+
// `classifyFailureCause` `do` uses, so the cross-path label agrees. The job
|
|
672
|
+
// record carries the classified status; the worktree is retained by the §4 reap.
|
|
673
|
+
const cause = classifyFailureCause(core.reason);
|
|
674
|
+
updateJobRecord(tree.dir, {
|
|
675
|
+
state: 'needs-attention',
|
|
676
|
+
reason: core.reason,
|
|
677
|
+
});
|
|
678
|
+
return {
|
|
679
|
+
...base,
|
|
680
|
+
status: failureCauseToItemStatus(cause),
|
|
681
|
+
detail: core.reason,
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
if (core.outcome === 'prepare-failed' ||
|
|
685
|
+
core.outcome === 'review-blocked' ||
|
|
686
|
+
core.outcome === 'rebase-conflict' ||
|
|
687
|
+
core.outcome === 'invariant-violation') {
|
|
688
|
+
// `prepare-failed`: the env-prep (install) step was red, so the env could
|
|
689
|
+
// not be made ready and `verify` was NOT run — distinct from a `tests-failed`
|
|
690
|
+
// red gate. Route it to needs-attention like the others (a human fixes the
|
|
691
|
+
// prepare command, then re-runs); the core already surfaced the bounce.
|
|
692
|
+
// `invariant-violation`: the one-slug-one-folder guard FAILED LOUD (the
|
|
693
|
+
// arbiter already holds the slug in >1 status folder — a corrupt ledger).
|
|
694
|
+
// The core integrated NOTHING. On the LEAST-supervised caller this MUST
|
|
695
|
+
// NOT fall through to the SUCCESS branch (state:'done' / 'claimed-done'
|
|
696
|
+
// with no prUrl) — that would misreport a refusal as a completed job, the
|
|
697
|
+
// opposite of fail-loud. Route it to needs-attention like a rebase conflict
|
|
698
|
+
// (a human resolves the duplicate, then re-runs); `complete.ts` mirrors this
|
|
699
|
+
// refusal with exit 1.
|
|
700
|
+
updateJobRecord(tree.dir, {
|
|
701
|
+
state: 'needs-attention',
|
|
702
|
+
reason: core.reason,
|
|
703
|
+
});
|
|
704
|
+
return { ...base, status: 'needs-attention', detail: core.reason };
|
|
705
|
+
}
|
|
706
|
+
// SUCCESS: the core integrated. Record the PR/MR URL (when a provider opened
|
|
707
|
+
// one) on the job, surfaced by `status`.
|
|
708
|
+
const prUrl = core.integration?.url;
|
|
709
|
+
updateJobRecord(tree.dir, { state: 'done', prUrl });
|
|
710
|
+
return { ...base, status: 'claimed-done', integration: core.integration };
|
|
711
|
+
}
|
|
712
|
+
finally {
|
|
713
|
+
// Strategy-appropriate teardown via the uniform handle (ADR §4). Job-worktree:
|
|
714
|
+
// re-apply the provably-safe deletion predicate and remove the worktree ONLY
|
|
715
|
+
// if it holds (clean tree AND the branch tip reachable on the arbiter — merged
|
|
716
|
+
// or pushed). One rule, no done-vs-failed special-casing: a claimed-done job
|
|
717
|
+
// is on the arbiter → reaped; a tests-failed / needs-attention / un-pushed job
|
|
718
|
+
// is NOT → retained (the worktree is the needs-attention signal; `gc` catches
|
|
719
|
+
// up). In-place: a NO-OP (the checkout is never reaped). NEVER `--force` here.
|
|
720
|
+
if (tree) {
|
|
721
|
+
tree.teardown();
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Run the agent against the isolated tree. Prefers the injected `dorfl`
|
|
727
|
+
* (tests / custom embeddings); otherwise launches `agentCmd` through the harness
|
|
728
|
+
* seam (recording the PID in the job's harness block, when there is one).
|
|
729
|
+
*/
|
|
730
|
+
function runAgent(ctx, tree, prompt, slug, agentCmd, model, sessionsDir) {
|
|
731
|
+
if (ctx.dorfl) {
|
|
732
|
+
return ctx.dorfl({ cwd: tree.dir, prompt, slug, env: ctx.env });
|
|
733
|
+
}
|
|
734
|
+
// Generate the full pi session-FILE path (task `session-path-pi-default`):
|
|
735
|
+
// `<sessionsDir>/<work-id>-<unique>.jsonl`, or pi's per-cwd default folder when
|
|
736
|
+
// `sessionsDir` is unset. The work-id (arbiter URL + slug) is `run`'s natural
|
|
737
|
+
// unique-per-claim id. Threaded through the seam so the pi adapter passes
|
|
738
|
+
// `--session <path>` (never `--session-dir` into the worktree).
|
|
739
|
+
const session = generateSessionPath({
|
|
740
|
+
sessionsDir,
|
|
741
|
+
cwd: tree.dir,
|
|
742
|
+
id: encodeWorkId(tree.arbiterUrl, slug),
|
|
743
|
+
});
|
|
744
|
+
const launched = ctx.harness.launch({
|
|
745
|
+
dir: tree.dir,
|
|
746
|
+
slug,
|
|
747
|
+
command: agentCmd,
|
|
748
|
+
prompt,
|
|
749
|
+
// The model routing intent (ADR §13) — the adapter decides HOW it reaches
|
|
750
|
+
// its tool (pi: `--model`; null/shell: `{model}` placeholder).
|
|
751
|
+
model,
|
|
752
|
+
session,
|
|
753
|
+
env: ctx.env,
|
|
754
|
+
});
|
|
755
|
+
updateJobRecord(tree.dir, { harness: launched.record });
|
|
756
|
+
// Surface the agent's FINAL SUMMARY (`LaunchResult.output`) — the source channel
|
|
757
|
+
// for the propose-mode PR body — instead of dropping it (mirrors `do`'s
|
|
758
|
+
// `runDoAgent`). Absent (no parseable assistant text) ⇒ undefined ⇒ `--fill`.
|
|
759
|
+
return { ok: launched.ok, detail: launched.detail, output: launched.output };
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* SAVE the partial work of a FAILED agent instead of dropping it (closing the
|
|
763
|
+
* fifth-and-last instance of the recurring asymmetry —
|
|
764
|
+
* `work/notes/observations/run-agent-failure-does-not-save-work.md`). `run`'s
|
|
765
|
+
* agent-failure return points (prompt-assembly fail / `runAgent` throw /
|
|
766
|
+
* `agent.ok === false`) used to BARE-RETURN `agent-failed`, leaving whatever the
|
|
767
|
+
* agent edited only on the LOCAL work branch in the (disposable, possibly remote)
|
|
768
|
+
* job worktree — not on the arbiter, so a requeue-continue on a DIFFERENT machine
|
|
769
|
+
* (or after a `gc` reap) re-cut fresh off main and orphaned it.
|
|
770
|
+
*
|
|
771
|
+
* This mirrors `do.ts`'s `saveAgentFailure`: route the failure through the SAME
|
|
772
|
+
* ledger write seam's needs-attention transition the gate-fail / integrate-
|
|
773
|
+
* conflict bounces use — which (with the arbiter) saves the agent's work as a wip
|
|
774
|
+
* commit, `git mv`s the item to needs-attention/ with the failure detail recorded
|
|
775
|
+
* as the reason, surfaces the move-only commit on the arbiter's `main`
|
|
776
|
+
* (OBSERVABLE), AND pushes the `work/<slug>` branch (RECOVERABLE — the durable
|
|
777
|
+
* artifact a requeue-continue lands the next agent on; continue-detection reads
|
|
778
|
+
* <arbiter>/work/<slug> ahead of main). The push is best-effort — an unreachable
|
|
779
|
+
* arbiter leaves the retained worktree + the local commits standing (the genuinely
|
|
780
|
+
* un-pushed case). The status is the classified failure CAUSE (`transient-infra` /
|
|
781
|
+
* `config-error` where knowable, else the conservative generic `agent-failed`);
|
|
782
|
+
* only the work-preserving side-effect now matches the gate-fail path.
|
|
783
|
+
*/
|
|
784
|
+
async function saveAgentFailure(base, tree, slug, detail, ctx) {
|
|
785
|
+
// Classify the failure CAUSE (best-effort + conservative) from the surfaced
|
|
786
|
+
// detail via the SAME `classifyFailureCause` `do` uses — so a thrown CORE config/
|
|
787
|
+
// wiring error reads as `config-error` (NOT `agent-failed`) on BOTH paths, closing
|
|
788
|
+
// the cross-path divergence; a harness-surfaced model/connection outage reads as
|
|
789
|
+
// `transient-infra`; an unrecognised cause stays the generic `agent-failed`. The
|
|
790
|
+
// cause LABEL prefixes the recorded reason so the cause is legible on the
|
|
791
|
+
// needs-attention route; `agent-failed` keeps the historical "agent failed:"
|
|
792
|
+
// prefix (no reason-prose regression).
|
|
793
|
+
const cause = classifyFailureCause(detail);
|
|
794
|
+
const reason = `${failureCauseLabel(cause)}: ${detail}`;
|
|
795
|
+
updateJobRecord(tree.dir, { state: 'needs-attention', reason });
|
|
796
|
+
await ledgerWrite.applyNeedsAttentionTransition({
|
|
797
|
+
cwd: tree.dir,
|
|
798
|
+
slug,
|
|
799
|
+
reason,
|
|
800
|
+
// Autonomous (`run`): surface on the arbiter's main AND push the work branch
|
|
801
|
+
// (the seam does both now), so the fleet's failed-agent work is cross-machine
|
|
802
|
+
// recoverable via requeue-continue. The route is fault-tolerant: a git outage
|
|
803
|
+
// is retried with bounded backoff then gives up cleanly (never hangs the tick).
|
|
804
|
+
arbiter: tree.arbiterRemote,
|
|
805
|
+
env: ctx.gitEnv ?? ctx.env,
|
|
806
|
+
backoff: ctx.backoff,
|
|
807
|
+
sleep: ctx.sleep,
|
|
808
|
+
});
|
|
809
|
+
return { ...base, status: failureCauseToItemStatus(cause), detail };
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Map a {@link FailureCause} onto the `run` {@link ItemStatus}. The cause names
|
|
813
|
+
* ARE the status names (the FAILURE-CAUSE axis reuses the terminal vocabulary), so
|
|
814
|
+
* this is identity — a single helper documents the mapping + keeps the `do`/`run`
|
|
815
|
+
* sites symmetric (`do` has the twin `failureCauseToDoOutcome`).
|
|
816
|
+
*/
|
|
817
|
+
function failureCauseToItemStatus(cause) {
|
|
818
|
+
return cause;
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Build the HONEST {@link ItemResult} for a CONTINUE-site surface that did NOT
|
|
822
|
+
* land on the arbiter (`{moved: false}`). The tree-less `in-progress/ →
|
|
823
|
+
* needs-attention/` move lost the CAS race against a busy arbiter (its
|
|
824
|
+
* contention-retry cap exhausted) or had no arbiter to publish to, so the item is
|
|
825
|
+
* STILL in-progress on the arbiter — reporting a clean `needs-attention` would
|
|
826
|
+
* mislead (it claims the surface landed, when it did not). The DISTINCT
|
|
827
|
+
* `surface-unmoved` status carries `reasonNotMoved` so the caller/human can tell
|
|
828
|
+
* it apart from a successful surface and retry/resolve.
|
|
829
|
+
*
|
|
830
|
+
* The local job record was set to `state: 'needs-attention'` (its LOCAL intent,
|
|
831
|
+
* recorded regardless of the arbiter move) right before the surface; on a
|
|
832
|
+
* `moved: false` we OVERWRITE its reason so that local record does not
|
|
833
|
+
* confusingly claim a landed surface either.
|
|
834
|
+
*/
|
|
835
|
+
function surfaceUnmovedItemResult(params) {
|
|
836
|
+
const { base, tree, slug, reason, surfaced } = params;
|
|
837
|
+
const detail = `'${slug}' could NOT be surfaced to needs-attention — the surface did not ` +
|
|
838
|
+
`reach the arbiter's main; the item is still IN-PROGRESS on the arbiter ` +
|
|
839
|
+
`(retry/resolve). ${surfaced.reasonNotMoved ?? reason}`;
|
|
840
|
+
updateJobRecord(tree.dir, { state: 'needs-attention', reason: detail });
|
|
841
|
+
return { ...base, status: 'surface-unmoved', detail };
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Route a DELIBERATE agent STOP (task `agent-stop-signal`) to needs-attention
|
|
845
|
+
* through the SAME work-preserving seam `saveAgentFailure` uses, but as the
|
|
846
|
+
* DISTINCT `agent-stopped` status (NOT `agent-failed` — the agent did not error;
|
|
847
|
+
* NOT `tests-failed`/`needs-attention` — there was no red gate / rebase conflict).
|
|
848
|
+
* The agent's STOP reason is recorded VERBATIM; the job record names it honestly.
|
|
849
|
+
* The `performIntegration` band (gate + Gate-2) is NEVER reached.
|
|
850
|
+
*/
|
|
851
|
+
async function saveAgentStop(base, tree, slug, reason, ctx) {
|
|
852
|
+
updateJobRecord(tree.dir, { state: 'needs-attention', reason });
|
|
853
|
+
await ledgerWrite.applyNeedsAttentionTransition({
|
|
854
|
+
cwd: tree.dir,
|
|
855
|
+
slug,
|
|
856
|
+
reason,
|
|
857
|
+
// Autonomous (`run`): surface on the arbiter's main AND push the work branch
|
|
858
|
+
// (the seam does both), so the stopped item is cross-machine visible/recoverable.
|
|
859
|
+
arbiter: tree.arbiterRemote,
|
|
860
|
+
env: ctx.gitEnv ?? ctx.env,
|
|
861
|
+
backoff: ctx.backoff,
|
|
862
|
+
sleep: ctx.sleep,
|
|
863
|
+
});
|
|
864
|
+
return { ...base, status: 'agent-stopped', detail: reason };
|
|
865
|
+
}
|
|
866
|
+
/** A throwaway default workspace under the OS temp dir (CLI convenience). */
|
|
867
|
+
export function defaultRunWorkspace() {
|
|
868
|
+
return join(tmpdir(), 'dorfl-workspace');
|
|
869
|
+
}
|
|
870
|
+
const realSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
871
|
+
/**
|
|
872
|
+
* `run` (no flag) — the cross-repo, parallel, **forever-looping daemon** (ADR §3):
|
|
873
|
+
* loop the supervised concurrent tick over the registry until a stop bound
|
|
874
|
+
* (max-iterations / max-duration / a stop signal) or forever (the future system
|
|
875
|
+
* service). Each tick claims + runs a CONCURRENT batch ({@link runOnce}’s
|
|
876
|
+
* `maxParallel`/`perRepoMax` in-flight execution), integrates, then the loop
|
|
877
|
+
* continues. This absorbs the retired `watch` verb’s bounded-session + surface-
|
|
878
|
+
* failures behaviour: a stuck item (timeout / red gate / rebase conflict) is
|
|
879
|
+
* routed through the EXISTING ledger needs-attention seam INSIDE the tick
|
|
880
|
+
* (`runOneItem` → `applyNeedsAttentionTransition`, surfaced on `main`) — the loop
|
|
881
|
+
* does NOT infinite-retry it and adds NO bespoke failure reporting; it just keeps
|
|
882
|
+
* ticking. `run --once` is exactly ONE tick (call {@link runOnce} directly, the
|
|
883
|
+
* debug/test affordance — NOT the CI path; CI is `do`).
|
|
884
|
+
*
|
|
885
|
+
* The loop owns ONLY scheduling (when/whether to tick); the TICK owns one batch’s
|
|
886
|
+
* work — kept separable so advance-loop can swap the tick later (see {@link
|
|
887
|
+
* RunTick}).
|
|
888
|
+
*/
|
|
889
|
+
export async function runLoop(options) {
|
|
890
|
+
const tick = options.tick ?? runOnce;
|
|
891
|
+
const now = options.now ?? Date.now;
|
|
892
|
+
const sleep = options.sleep ?? realSleep;
|
|
893
|
+
const intervalMs = options.intervalMs ?? 0;
|
|
894
|
+
const start = now();
|
|
895
|
+
const deadline = options.maxDurationMs !== undefined
|
|
896
|
+
? start + options.maxDurationMs
|
|
897
|
+
: undefined;
|
|
898
|
+
// The per-tick options are the RunOnceOptions subset of RunLoopOptions; the
|
|
899
|
+
// loop-only knobs (bounds / seams / callbacks) are not forwarded to the tick.
|
|
900
|
+
const tickOptions = {
|
|
901
|
+
config: options.config,
|
|
902
|
+
report: options.report,
|
|
903
|
+
workspace: options.workspace,
|
|
904
|
+
dorfl: options.dorfl,
|
|
905
|
+
harness: options.harness,
|
|
906
|
+
reviewGate: options.reviewGate,
|
|
907
|
+
provider: options.provider,
|
|
908
|
+
openPr: options.openPr,
|
|
909
|
+
env: options.env,
|
|
910
|
+
agentId: options.agentId,
|
|
911
|
+
onWarn: options.onWarn,
|
|
912
|
+
};
|
|
913
|
+
const ticks = [];
|
|
914
|
+
let stoppedBy = 'max-iterations';
|
|
915
|
+
// eslint-disable-next-line no-constant-condition
|
|
916
|
+
while (true) {
|
|
917
|
+
// Stop-condition checks BEFORE running a tick (so a zero-iteration / already-
|
|
918
|
+
// expired / pre-signalled session does no work).
|
|
919
|
+
if (options.stop?.()) {
|
|
920
|
+
stoppedBy = 'signal';
|
|
921
|
+
break;
|
|
922
|
+
}
|
|
923
|
+
if (options.maxIterations !== undefined &&
|
|
924
|
+
ticks.length >= options.maxIterations) {
|
|
925
|
+
stoppedBy = 'max-iterations';
|
|
926
|
+
break;
|
|
927
|
+
}
|
|
928
|
+
if (deadline !== undefined && now() >= deadline) {
|
|
929
|
+
stoppedBy = 'max-duration';
|
|
930
|
+
break;
|
|
931
|
+
}
|
|
932
|
+
const result = await tick(tickOptions);
|
|
933
|
+
ticks.push(result);
|
|
934
|
+
options.onTick?.(result, ticks.length);
|
|
935
|
+
// Re-check the bounds AFTER the tick so we never sleep past a stop condition.
|
|
936
|
+
if (options.maxIterations !== undefined &&
|
|
937
|
+
ticks.length >= options.maxIterations) {
|
|
938
|
+
stoppedBy = 'max-iterations';
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
if (deadline !== undefined && now() >= deadline) {
|
|
942
|
+
stoppedBy = 'max-duration';
|
|
943
|
+
break;
|
|
944
|
+
}
|
|
945
|
+
if (options.stop?.()) {
|
|
946
|
+
stoppedBy = 'signal';
|
|
947
|
+
break;
|
|
948
|
+
}
|
|
949
|
+
if (intervalMs > 0) {
|
|
950
|
+
await sleep(intervalMs);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
return {
|
|
954
|
+
iterations: ticks.length,
|
|
955
|
+
stoppedBy,
|
|
956
|
+
claimedAndDone: ticks.reduce((n, t) => n + t.claimedAndDone, 0),
|
|
957
|
+
skipped: ticks.reduce((n, t) => n + t.skipped, 0),
|
|
958
|
+
failed: ticks.reduce((n, t) => n + t.failed, 0),
|
|
959
|
+
needsAttention: ticks.reduce((n, t) => n + t.needsAttention, 0),
|
|
960
|
+
ticks,
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
//# sourceMappingURL=run.js.map
|