@blxzer/pactile 0.6.0-beta.2 → 0.6.0-beta.4
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/README.md +93 -84
- package/README.zh-CN.md +90 -81
- package/dist/bin/compat-warning.d.ts +2 -0
- package/dist/bin/compat-warning.d.ts.map +1 -0
- package/dist/bin/compat-warning.js +8 -0
- package/dist/bin/compat-warning.js.map +1 -0
- package/dist/bin/cstl.d.ts +3 -0
- package/dist/bin/cstl.d.ts.map +1 -0
- package/{bin → dist/bin}/cstl.js +2 -3
- package/dist/bin/cstl.js.map +1 -0
- package/dist/bin/pactile.d.ts +3 -0
- package/dist/bin/pactile.d.ts.map +1 -0
- package/dist/bin/pactile.js +4 -0
- package/dist/bin/pactile.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +77 -17
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/capability.d.ts +3 -0
- package/dist/commands/capability.d.ts.map +1 -0
- package/dist/commands/capability.js +129 -0
- package/dist/commands/capability.js.map +1 -0
- package/dist/commands/codex.d.ts.map +1 -1
- package/dist/commands/codex.js +54 -6
- package/dist/commands/codex.js.map +1 -1
- package/dist/commands/context.d.ts +2 -0
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +367 -41
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/detach.d.ts.map +1 -1
- package/dist/commands/detach.js +5 -4
- package/dist/commands/detach.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +615 -259
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/legacy-task.d.ts +3 -0
- package/dist/commands/legacy-task.d.ts.map +1 -0
- package/dist/commands/legacy-task.js +295 -0
- package/dist/commands/legacy-task.js.map +1 -0
- package/dist/commands/pi.d.ts +3 -2
- package/dist/commands/pi.d.ts.map +1 -1
- package/dist/commands/pi.js +333 -10
- package/dist/commands/pi.js.map +1 -1
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +148 -24
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/task-schedule.d.ts +17 -0
- package/dist/commands/task-schedule.d.ts.map +1 -0
- package/dist/commands/task-schedule.js +395 -0
- package/dist/commands/task-schedule.js.map +1 -0
- package/dist/commands/task-verify-plan.d.ts +3 -0
- package/dist/commands/task-verify-plan.d.ts.map +1 -0
- package/dist/commands/task-verify-plan.js +503 -0
- package/dist/commands/task-verify-plan.js.map +1 -0
- package/dist/commands/task-worktree-close.d.ts +4 -0
- package/dist/commands/task-worktree-close.d.ts.map +1 -0
- package/dist/commands/task-worktree-close.js +81 -0
- package/dist/commands/task-worktree-close.js.map +1 -0
- package/dist/commands/task.d.ts +3 -0
- package/dist/commands/task.d.ts.map +1 -1
- package/dist/commands/task.js +1373 -184
- package/dist/commands/task.js.map +1 -1
- package/dist/commands/tile-selection.d.ts +3 -0
- package/dist/commands/tile-selection.d.ts.map +1 -0
- package/dist/commands/tile-selection.js +174 -0
- package/dist/commands/tile-selection.js.map +1 -0
- package/dist/commands/update.d.ts +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +80 -55
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/worktree.d.ts +4 -0
- package/dist/commands/worktree.d.ts.map +1 -0
- package/dist/commands/worktree.js +198 -0
- package/dist/commands/worktree.js.map +1 -0
- package/dist/configurators/shared.js +1 -1
- package/dist/configurators/shared.js.map +1 -1
- package/dist/core/compat/environment.d.ts +0 -1
- package/dist/core/compat/environment.d.ts.map +1 -1
- package/dist/core/compat/environment.js +1 -9
- package/dist/core/compat/environment.js.map +1 -1
- package/dist/core/pactile/bounded-capabilities.d.ts +127 -0
- package/dist/core/pactile/bounded-capabilities.d.ts.map +1 -0
- package/dist/core/pactile/bounded-capabilities.js +188 -0
- package/dist/core/pactile/bounded-capabilities.js.map +1 -0
- package/dist/core/pactile/index.d.ts +1 -0
- package/dist/core/pactile/index.d.ts.map +1 -1
- package/dist/core/pactile/index.js +1 -0
- package/dist/core/pactile/index.js.map +1 -1
- package/dist/core/task/index.d.ts +4 -2
- package/dist/core/task/index.d.ts.map +1 -1
- package/dist/core/task/index.js +2 -1
- package/dist/core/task/index.js.map +1 -1
- package/dist/core/task/kernel-cli.d.ts +14 -1
- package/dist/core/task/kernel-cli.d.ts.map +1 -1
- package/dist/core/task/kernel-cli.js +128 -0
- package/dist/core/task/kernel-cli.js.map +1 -1
- package/dist/core/task/kernel-contract.d.ts +6 -3
- package/dist/core/task/kernel-contract.d.ts.map +1 -1
- package/dist/core/task/kernel-contract.js +71 -23
- package/dist/core/task/kernel-contract.js.map +1 -1
- package/dist/core/task/kernel-store.d.ts +6 -0
- package/dist/core/task/kernel-store.d.ts.map +1 -1
- package/dist/core/task/kernel-store.js +54 -2
- package/dist/core/task/kernel-store.js.map +1 -1
- package/dist/core/task/legacy-task-migration-reader.d.ts +137 -0
- package/dist/core/task/legacy-task-migration-reader.d.ts.map +1 -0
- package/dist/core/task/legacy-task-migration-reader.js +512 -0
- package/dist/core/task/legacy-task-migration-reader.js.map +1 -0
- package/dist/core/task/legacy-task-migration.d.ts +105 -0
- package/dist/core/task/legacy-task-migration.d.ts.map +1 -0
- package/dist/core/task/legacy-task-migration.js +562 -0
- package/dist/core/task/legacy-task-migration.js.map +1 -0
- package/dist/core/task/legacy-task-overlay-durable-io.d.ts +18 -0
- package/dist/core/task/legacy-task-overlay-durable-io.d.ts.map +1 -0
- package/dist/core/task/legacy-task-overlay-durable-io.js +66 -0
- package/dist/core/task/legacy-task-overlay-durable-io.js.map +1 -0
- package/dist/core/task/legacy-task-reconciliation-reader.d.ts +26 -0
- package/dist/core/task/legacy-task-reconciliation-reader.d.ts.map +1 -0
- package/dist/core/task/legacy-task-reconciliation-reader.js +324 -0
- package/dist/core/task/legacy-task-reconciliation-reader.js.map +1 -0
- package/dist/core/task/legacy-task-v2-import.d.ts +67 -0
- package/dist/core/task/legacy-task-v2-import.d.ts.map +1 -0
- package/dist/core/task/legacy-task-v2-import.js +1046 -0
- package/dist/core/task/legacy-task-v2-import.js.map +1 -0
- package/dist/core/task/project-file-observer.d.ts +46 -0
- package/dist/core/task/project-file-observer.d.ts.map +1 -0
- package/dist/core/task/project-file-observer.js +618 -0
- package/dist/core/task/project-file-observer.js.map +1 -0
- package/dist/core/task/start-authority.d.ts +14 -0
- package/dist/core/task/start-authority.d.ts.map +1 -0
- package/dist/core/task/start-authority.js +146 -0
- package/dist/core/task/start-authority.js.map +1 -0
- package/dist/core/task/strategy.d.ts +24 -0
- package/dist/core/task/strategy.d.ts.map +1 -0
- package/dist/core/task/strategy.js +136 -0
- package/dist/core/task/strategy.js.map +1 -0
- package/dist/core/task/task-candidate-observer.d.ts +111 -0
- package/dist/core/task/task-candidate-observer.d.ts.map +1 -0
- package/dist/core/task/task-candidate-observer.js +762 -0
- package/dist/core/task/task-candidate-observer.js.map +1 -0
- package/dist/core/task/task-kernel-close.d.ts +5 -0
- package/dist/core/task/task-kernel-close.d.ts.map +1 -0
- package/dist/core/task/task-kernel-close.js +367 -0
- package/dist/core/task/task-kernel-close.js.map +1 -0
- package/dist/core/task/task-kernel-dependencies.d.ts +3 -0
- package/dist/core/task/task-kernel-dependencies.d.ts.map +1 -0
- package/dist/core/task/task-kernel-dependencies.js +24 -0
- package/dist/core/task/task-kernel-dependencies.js.map +1 -0
- package/dist/core/task/task-kernel-host.d.ts +5 -0
- package/dist/core/task/task-kernel-host.d.ts.map +1 -0
- package/dist/core/task/task-kernel-host.js +136 -0
- package/dist/core/task/task-kernel-host.js.map +1 -0
- package/dist/core/task/task-kernel-paths.d.ts +19 -0
- package/dist/core/task/task-kernel-paths.d.ts.map +1 -0
- package/dist/core/task/task-kernel-paths.js +237 -0
- package/dist/core/task/task-kernel-paths.js.map +1 -0
- package/dist/core/task/task-kernel-reviews.d.ts +3 -0
- package/dist/core/task/task-kernel-reviews.d.ts.map +1 -0
- package/dist/core/task/task-kernel-reviews.js +100 -0
- package/dist/core/task/task-kernel-reviews.js.map +1 -0
- package/dist/core/task/task-kernel-runs.d.ts +5 -0
- package/dist/core/task/task-kernel-runs.d.ts.map +1 -0
- package/dist/core/task/task-kernel-runs.js +196 -0
- package/dist/core/task/task-kernel-runs.js.map +1 -0
- package/dist/core/task/task-kernel-schema.d.ts +29 -0
- package/dist/core/task/task-kernel-schema.d.ts.map +1 -0
- package/dist/core/task/task-kernel-schema.js +1169 -0
- package/dist/core/task/task-kernel-schema.js.map +1 -0
- package/dist/core/task/task-kernel-store-v2.d.ts +33 -0
- package/dist/core/task/task-kernel-store-v2.d.ts.map +1 -0
- package/dist/core/task/task-kernel-store-v2.js +574 -0
- package/dist/core/task/task-kernel-store-v2.js.map +1 -0
- package/dist/core/task/task-kernel-types.d.ts +585 -0
- package/dist/core/task/task-kernel-types.d.ts.map +1 -0
- package/dist/core/task/task-kernel-types.js +31 -0
- package/dist/core/task/task-kernel-types.js.map +1 -0
- package/dist/core/task/task-kernel-workspaces.d.ts +10 -0
- package/dist/core/task/task-kernel-workspaces.d.ts.map +1 -0
- package/dist/core/task/task-kernel-workspaces.js +250 -0
- package/dist/core/task/task-kernel-workspaces.js.map +1 -0
- package/dist/core/task/task-kernel-write-set.d.ts +3 -0
- package/dist/core/task/task-kernel-write-set.d.ts.map +1 -0
- package/dist/core/task/task-kernel-write-set.js +12 -0
- package/dist/core/task/task-kernel-write-set.js.map +1 -0
- package/dist/core/task/task-kernel.d.ts +19 -0
- package/dist/core/task/task-kernel.d.ts.map +1 -0
- package/dist/core/task/task-kernel.js +17 -0
- package/dist/core/task/task-kernel.js.map +1 -0
- package/dist/core/task/task-pull-request-observer.d.ts +25 -0
- package/dist/core/task/task-pull-request-observer.d.ts.map +1 -0
- package/dist/core/task/task-pull-request-observer.js +187 -0
- package/dist/core/task/task-pull-request-observer.js.map +1 -0
- package/dist/core/task/task-review-evidence.d.ts +27 -0
- package/dist/core/task/task-review-evidence.d.ts.map +1 -0
- package/dist/core/task/task-review-evidence.js +383 -0
- package/dist/core/task/task-review-evidence.js.map +1 -0
- package/dist/migrations/index.d.ts +1 -1
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +5 -3
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/manifests/0.6.0-beta.3.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.4.json +9 -0
- package/dist/pactile/artifacts/documents.d.ts +72 -0
- package/dist/pactile/artifacts/documents.d.ts.map +1 -0
- package/dist/pactile/artifacts/documents.js +406 -0
- package/dist/pactile/artifacts/documents.js.map +1 -0
- package/dist/pactile/artifacts/index.d.ts +6 -0
- package/dist/pactile/artifacts/index.d.ts.map +1 -0
- package/dist/pactile/artifacts/index.js +6 -0
- package/dist/pactile/artifacts/index.js.map +1 -0
- package/dist/pactile/artifacts/projection.d.ts +50 -0
- package/dist/pactile/artifacts/projection.d.ts.map +1 -0
- package/dist/pactile/artifacts/projection.js +139 -0
- package/dist/pactile/artifacts/projection.js.map +1 -0
- package/dist/pactile/artifacts/reader.d.ts +20 -0
- package/dist/pactile/artifacts/reader.d.ts.map +1 -0
- package/dist/pactile/artifacts/reader.js +559 -0
- package/dist/pactile/artifacts/reader.js.map +1 -0
- package/dist/pactile/artifacts/schema.d.ts +4 -0
- package/dist/pactile/artifacts/schema.d.ts.map +1 -0
- package/dist/pactile/artifacts/schema.js +217 -0
- package/dist/pactile/artifacts/schema.js.map +1 -0
- package/dist/pactile/artifacts/types.d.ts +66 -0
- package/dist/pactile/artifacts/types.d.ts.map +1 -0
- package/dist/pactile/artifacts/types.js +45 -0
- package/dist/pactile/artifacts/types.js.map +1 -0
- package/dist/pactile/capabilities/claim.d.ts +3 -0
- package/dist/pactile/capabilities/claim.d.ts.map +1 -0
- package/dist/pactile/capabilities/claim.js +97 -0
- package/dist/pactile/capabilities/claim.js.map +1 -0
- package/dist/pactile/capabilities/command.d.ts +6 -0
- package/dist/pactile/capabilities/command.d.ts.map +1 -0
- package/dist/pactile/capabilities/command.js +253 -0
- package/dist/pactile/capabilities/command.js.map +1 -0
- package/dist/pactile/capabilities/discover.d.ts +6 -0
- package/dist/pactile/capabilities/discover.d.ts.map +1 -0
- package/dist/pactile/capabilities/discover.js +48 -0
- package/dist/pactile/capabilities/discover.js.map +1 -0
- package/dist/pactile/capabilities/node.d.ts +15 -0
- package/dist/pactile/capabilities/node.d.ts.map +1 -0
- package/dist/pactile/capabilities/node.js +292 -0
- package/dist/pactile/capabilities/node.js.map +1 -0
- package/dist/pactile/capabilities/read.d.ts +6 -0
- package/dist/pactile/capabilities/read.d.ts.map +1 -0
- package/dist/pactile/capabilities/read.js +100 -0
- package/dist/pactile/capabilities/read.js.map +1 -0
- package/dist/pactile/capabilities/receipts.d.ts +17 -0
- package/dist/pactile/capabilities/receipts.d.ts.map +1 -0
- package/dist/pactile/capabilities/receipts.js +90 -0
- package/dist/pactile/capabilities/receipts.js.map +1 -0
- package/dist/pactile/capabilities/search.d.ts +6 -0
- package/dist/pactile/capabilities/search.d.ts.map +1 -0
- package/dist/pactile/capabilities/search.js +181 -0
- package/dist/pactile/capabilities/search.js.map +1 -0
- package/dist/pactile/capabilities/shared.d.ts +29 -0
- package/dist/pactile/capabilities/shared.d.ts.map +1 -0
- package/dist/pactile/capabilities/shared.js +134 -0
- package/dist/pactile/capabilities/shared.js.map +1 -0
- package/dist/pactile/capabilities/workspace.d.ts +19 -0
- package/dist/pactile/capabilities/workspace.d.ts.map +1 -0
- package/dist/pactile/capabilities/workspace.js +273 -0
- package/dist/pactile/capabilities/workspace.js.map +1 -0
- package/dist/pactile/codex/bridge.d.ts +105 -2
- package/dist/pactile/codex/bridge.d.ts.map +1 -1
- package/dist/pactile/codex/bridge.js +1266 -92
- package/dist/pactile/codex/bridge.js.map +1 -1
- package/dist/pactile/codex/dispatch-stop-proof.d.ts +43 -0
- package/dist/pactile/codex/dispatch-stop-proof.d.ts.map +1 -0
- package/dist/pactile/codex/dispatch-stop-proof.js +177 -0
- package/dist/pactile/codex/dispatch-stop-proof.js.map +1 -0
- package/dist/pactile/codex/host-stop.d.ts +47 -0
- package/dist/pactile/codex/host-stop.d.ts.map +1 -0
- package/dist/pactile/codex/host-stop.js +344 -0
- package/dist/pactile/codex/host-stop.js.map +1 -0
- package/dist/pactile/coordination/index.d.ts +4 -0
- package/dist/pactile/coordination/index.d.ts.map +1 -0
- package/dist/pactile/coordination/index.js +4 -0
- package/dist/pactile/coordination/index.js.map +1 -0
- package/dist/pactile/coordination/journal.d.ts +10 -0
- package/dist/pactile/coordination/journal.d.ts.map +1 -0
- package/dist/pactile/coordination/journal.js +255 -0
- package/dist/pactile/coordination/journal.js.map +1 -0
- package/dist/pactile/coordination/kernel-resume.d.ts +8 -0
- package/dist/pactile/coordination/kernel-resume.d.ts.map +1 -0
- package/dist/pactile/coordination/kernel-resume.js +87 -0
- package/dist/pactile/coordination/kernel-resume.js.map +1 -0
- package/dist/pactile/coordination/model.d.ts +553 -0
- package/dist/pactile/coordination/model.d.ts.map +1 -0
- package/dist/pactile/coordination/model.js +239 -0
- package/dist/pactile/coordination/model.js.map +1 -0
- package/dist/pactile/coordination/state.d.ts +46 -0
- package/dist/pactile/coordination/state.d.ts.map +1 -0
- package/dist/pactile/coordination/state.js +301 -0
- package/dist/pactile/coordination/state.js.map +1 -0
- package/dist/pactile/coordination/store.d.ts +94 -0
- package/dist/pactile/coordination/store.d.ts.map +1 -0
- package/dist/pactile/coordination/store.js +396 -0
- package/dist/pactile/coordination/store.js.map +1 -0
- package/dist/pactile/index.d.ts +7 -1
- package/dist/pactile/index.d.ts.map +1 -1
- package/dist/pactile/index.js +7 -1
- package/dist/pactile/index.js.map +1 -1
- package/dist/pactile/jev/contracts.d.ts +104 -0
- package/dist/pactile/jev/contracts.d.ts.map +1 -0
- package/dist/pactile/jev/contracts.js +53 -0
- package/dist/pactile/jev/contracts.js.map +1 -0
- package/dist/pactile/jev/decision.d.ts +58 -0
- package/dist/pactile/jev/decision.d.ts.map +1 -0
- package/dist/pactile/jev/decision.js +149 -0
- package/dist/pactile/jev/decision.js.map +1 -0
- package/dist/pactile/jev/http.d.ts +25 -0
- package/dist/pactile/jev/http.d.ts.map +1 -0
- package/dist/pactile/jev/http.js +234 -0
- package/dist/pactile/jev/http.js.map +1 -0
- package/dist/pactile/jev/index.d.ts +8 -0
- package/dist/pactile/jev/index.d.ts.map +1 -0
- package/dist/pactile/jev/index.js +4 -0
- package/dist/pactile/jev/index.js.map +1 -0
- package/dist/pactile/jev/outbound.d.ts +15 -0
- package/dist/pactile/jev/outbound.d.ts.map +1 -0
- package/dist/pactile/jev/outbound.js +264 -0
- package/dist/pactile/jev/outbound.js.map +1 -0
- package/dist/pactile/jev/project-policy.d.ts +10 -0
- package/dist/pactile/jev/project-policy.d.ts.map +1 -0
- package/dist/pactile/jev/project-policy.js +53 -0
- package/dist/pactile/jev/project-policy.js.map +1 -0
- package/dist/pactile/jev/response.d.ts +17 -0
- package/dist/pactile/jev/response.d.ts.map +1 -0
- package/dist/pactile/jev/response.js +280 -0
- package/dist/pactile/jev/response.js.map +1 -0
- package/dist/pactile/jev/scheduler-advice.d.ts +92 -0
- package/dist/pactile/jev/scheduler-advice.d.ts.map +1 -0
- package/dist/pactile/jev/scheduler-advice.js +323 -0
- package/dist/pactile/jev/scheduler-advice.js.map +1 -0
- package/dist/pactile/jev/transport.d.ts +6 -0
- package/dist/pactile/jev/transport.d.ts.map +1 -0
- package/dist/pactile/jev/transport.js +87 -0
- package/dist/pactile/jev/transport.js.map +1 -0
- package/dist/pactile/lifecycle/deferred-live.d.ts +17 -0
- package/dist/pactile/lifecycle/deferred-live.d.ts.map +1 -0
- package/dist/pactile/lifecycle/deferred-live.js +173 -0
- package/dist/pactile/lifecycle/deferred-live.js.map +1 -0
- package/dist/pactile/lifecycle/legacy-import.d.ts +1 -1
- package/dist/pactile/lifecycle/legacy-import.js +3 -3
- package/dist/pactile/lifecycle/legacy-import.js.map +1 -1
- package/dist/pactile/lifecycle/project-files.d.ts.map +1 -1
- package/dist/pactile/lifecycle/project-files.js +1 -0
- package/dist/pactile/lifecycle/project-files.js.map +1 -1
- package/dist/pactile/migration/legacy-task-batch-generation.d.ts +23 -0
- package/dist/pactile/migration/legacy-task-batch-generation.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-generation.js +102 -0
- package/dist/pactile/migration/legacy-task-batch-generation.js.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-io.d.ts +9 -0
- package/dist/pactile/migration/legacy-task-batch-io.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-io.js +208 -0
- package/dist/pactile/migration/legacy-task-batch-io.js.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-journal.d.ts +6 -0
- package/dist/pactile/migration/legacy-task-batch-journal.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-journal.js +121 -0
- package/dist/pactile/migration/legacy-task-batch-journal.js.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-orchestrator.d.ts +18 -0
- package/dist/pactile/migration/legacy-task-batch-orchestrator.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-orchestrator.js +634 -0
- package/dist/pactile/migration/legacy-task-batch-orchestrator.js.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-source.d.ts +8 -0
- package/dist/pactile/migration/legacy-task-batch-source.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-source.js +158 -0
- package/dist/pactile/migration/legacy-task-batch-source.js.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-types.d.ts +127 -0
- package/dist/pactile/migration/legacy-task-batch-types.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch-types.js +127 -0
- package/dist/pactile/migration/legacy-task-batch-types.js.map +1 -0
- package/dist/pactile/migration/legacy-task-batch.d.ts +3 -0
- package/dist/pactile/migration/legacy-task-batch.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-batch.js +2 -0
- package/dist/pactile/migration/legacy-task-batch.js.map +1 -0
- package/dist/pactile/migration/legacy-task-held-source-reader.d.ts +27 -0
- package/dist/pactile/migration/legacy-task-held-source-reader.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-held-source-reader.js +261 -0
- package/dist/pactile/migration/legacy-task-held-source-reader.js.map +1 -0
- package/dist/pactile/migration/legacy-task-reconciliation.d.ts +46 -0
- package/dist/pactile/migration/legacy-task-reconciliation.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-reconciliation.js +589 -0
- package/dist/pactile/migration/legacy-task-reconciliation.js.map +1 -0
- package/dist/pactile/migration/legacy-task-update.d.ts +19 -0
- package/dist/pactile/migration/legacy-task-update.d.ts.map +1 -0
- package/dist/pactile/migration/legacy-task-update.js +164 -0
- package/dist/pactile/migration/legacy-task-update.js.map +1 -0
- package/dist/pactile/parallel/batch.d.ts +21 -2
- package/dist/pactile/parallel/batch.d.ts.map +1 -1
- package/dist/pactile/parallel/batch.js +271 -50
- package/dist/pactile/parallel/batch.js.map +1 -1
- package/dist/pactile/parallel/policy.d.ts +1 -1
- package/dist/pactile/parallel/policy.d.ts.map +1 -1
- package/dist/pactile/parallel/policy.js +130 -102
- package/dist/pactile/parallel/policy.js.map +1 -1
- package/dist/pactile/pi/bridge.d.ts +139 -2
- package/dist/pactile/pi/bridge.d.ts.map +1 -1
- package/dist/pactile/pi/bridge.js +1317 -106
- package/dist/pactile/pi/bridge.js.map +1 -1
- package/dist/pactile/pi/rpc.d.ts +23 -4
- package/dist/pactile/pi/rpc.d.ts.map +1 -1
- package/dist/pactile/pi/rpc.js +243 -42
- package/dist/pactile/pi/rpc.js.map +1 -1
- package/dist/pactile/pi/v2-dispatch.d.ts +113 -0
- package/dist/pactile/pi/v2-dispatch.d.ts.map +1 -0
- package/dist/pactile/pi/v2-dispatch.js +919 -0
- package/dist/pactile/pi/v2-dispatch.js.map +1 -0
- package/dist/pactile/projection/planner.js +1 -1
- package/dist/pactile/projection/planner.js.map +1 -1
- package/dist/pactile/registry.d.ts +63 -1
- package/dist/pactile/registry.d.ts.map +1 -1
- package/dist/pactile/registry.js +633 -4
- package/dist/pactile/registry.js.map +1 -1
- package/dist/pactile/retrieval/index.d.ts +1 -0
- package/dist/pactile/retrieval/index.d.ts.map +1 -1
- package/dist/pactile/retrieval/index.js +1 -0
- package/dist/pactile/retrieval/index.js.map +1 -1
- package/dist/pactile/retrieval/jev-planning.d.ts +28 -0
- package/dist/pactile/retrieval/jev-planning.d.ts.map +1 -0
- package/dist/pactile/retrieval/jev-planning.js +96 -0
- package/dist/pactile/retrieval/jev-planning.js.map +1 -0
- package/dist/pactile/review/contract.d.ts +232 -0
- package/dist/pactile/review/contract.d.ts.map +1 -0
- package/dist/pactile/review/contract.js +695 -0
- package/dist/pactile/review/contract.js.map +1 -0
- package/dist/pactile/review/escalation-artifacts.d.ts +41 -0
- package/dist/pactile/review/escalation-artifacts.d.ts.map +1 -0
- package/dist/pactile/review/escalation-artifacts.js +415 -0
- package/dist/pactile/review/escalation-artifacts.js.map +1 -0
- package/dist/pactile/review/escalation-contract.d.ts +115 -0
- package/dist/pactile/review/escalation-contract.d.ts.map +1 -0
- package/dist/pactile/review/escalation-contract.js +151 -0
- package/dist/pactile/review/escalation-contract.js.map +1 -0
- package/dist/pactile/review/escalation-finalize.d.ts +14 -0
- package/dist/pactile/review/escalation-finalize.d.ts.map +1 -0
- package/dist/pactile/review/escalation-finalize.js +319 -0
- package/dist/pactile/review/escalation-finalize.js.map +1 -0
- package/dist/pactile/review/escalation-reply.d.ts +10 -0
- package/dist/pactile/review/escalation-reply.d.ts.map +1 -0
- package/dist/pactile/review/escalation-reply.js +61 -0
- package/dist/pactile/review/escalation-reply.js.map +1 -0
- package/dist/pactile/review/escalation-transport.d.ts +32 -0
- package/dist/pactile/review/escalation-transport.d.ts.map +1 -0
- package/dist/pactile/review/escalation-transport.js +216 -0
- package/dist/pactile/review/escalation-transport.js.map +1 -0
- package/dist/pactile/review/escalation.d.ts +7 -0
- package/dist/pactile/review/escalation.d.ts.map +1 -0
- package/dist/pactile/review/escalation.js +6 -0
- package/dist/pactile/review/escalation.js.map +1 -0
- package/dist/pactile/review/evidence.d.ts +25 -0
- package/dist/pactile/review/evidence.d.ts.map +1 -0
- package/dist/pactile/review/evidence.js +58 -0
- package/dist/pactile/review/evidence.js.map +1 -0
- package/dist/pactile/review/jev-escalation-advice.d.ts +77 -0
- package/dist/pactile/review/jev-escalation-advice.d.ts.map +1 -0
- package/dist/pactile/review/jev-escalation-advice.js +358 -0
- package/dist/pactile/review/jev-escalation-advice.js.map +1 -0
- package/dist/pactile/scheduler/index.d.ts +6 -0
- package/dist/pactile/scheduler/index.d.ts.map +1 -0
- package/dist/pactile/scheduler/index.js +5 -0
- package/dist/pactile/scheduler/index.js.map +1 -0
- package/dist/pactile/scheduler/project-lease-store.d.ts +35 -0
- package/dist/pactile/scheduler/project-lease-store.d.ts.map +1 -0
- package/dist/pactile/scheduler/project-lease-store.js +189 -0
- package/dist/pactile/scheduler/project-lease-store.js.map +1 -0
- package/dist/pactile/scheduler/scheduler.d.ts +91 -0
- package/dist/pactile/scheduler/scheduler.d.ts.map +1 -0
- package/dist/pactile/scheduler/scheduler.js +589 -0
- package/dist/pactile/scheduler/scheduler.js.map +1 -0
- package/dist/pactile/scheduler/task-kernel-admission.d.ts +173 -0
- package/dist/pactile/scheduler/task-kernel-admission.d.ts.map +1 -0
- package/dist/pactile/scheduler/task-kernel-admission.js +1560 -0
- package/dist/pactile/scheduler/task-kernel-admission.js.map +1 -0
- package/dist/pactile/scheduler/task-kernel-wave-dispatch.d.ts +184 -0
- package/dist/pactile/scheduler/task-kernel-wave-dispatch.d.ts.map +1 -0
- package/dist/pactile/scheduler/task-kernel-wave-dispatch.js +952 -0
- package/dist/pactile/scheduler/task-kernel-wave-dispatch.js.map +1 -0
- package/dist/pactile/scheduler/task-map-scheduler.d.ts +116 -0
- package/dist/pactile/scheduler/task-map-scheduler.d.ts.map +1 -0
- package/dist/pactile/scheduler/task-map-scheduler.js +1448 -0
- package/dist/pactile/scheduler/task-map-scheduler.js.map +1 -0
- package/dist/pactile/scheduler/task-run-dispatch-state.d.ts +7 -0
- package/dist/pactile/scheduler/task-run-dispatch-state.d.ts.map +1 -0
- package/dist/pactile/scheduler/task-run-dispatch-state.js +69 -0
- package/dist/pactile/scheduler/task-run-dispatch-state.js.map +1 -0
- package/dist/pactile/task/authorization.d.ts +3 -0
- package/dist/pactile/task/authorization.d.ts.map +1 -0
- package/dist/pactile/task/authorization.js +43 -0
- package/dist/pactile/task/authorization.js.map +1 -0
- package/dist/pactile/task/creation.d.ts +22 -0
- package/dist/pactile/task/creation.d.ts.map +1 -0
- package/dist/pactile/task/creation.js +95 -0
- package/dist/pactile/task/creation.js.map +1 -0
- package/dist/pactile/task/guards.d.ts +1 -4
- package/dist/pactile/task/guards.d.ts.map +1 -1
- package/dist/pactile/task/guards.js +5 -57
- package/dist/pactile/task/guards.js.map +1 -1
- package/dist/pactile/task/session-jev-receipt.d.ts +58 -0
- package/dist/pactile/task/session-jev-receipt.d.ts.map +1 -0
- package/dist/pactile/task/session-jev-receipt.js +375 -0
- package/dist/pactile/task/session-jev-receipt.js.map +1 -0
- package/dist/pactile/task/session-jev.d.ts +7 -0
- package/dist/pactile/task/session-jev.d.ts.map +1 -0
- package/dist/pactile/task/session-jev.js +217 -0
- package/dist/pactile/task/session-jev.js.map +1 -0
- package/dist/pactile/task/session-pack.d.ts.map +1 -1
- package/dist/pactile/task/session-pack.js +155 -16
- package/dist/pactile/task/session-pack.js.map +1 -1
- package/dist/pactile/task/session-retrieval-jev-receipt.d.ts +49 -0
- package/dist/pactile/task/session-retrieval-jev-receipt.d.ts.map +1 -0
- package/dist/pactile/task/session-retrieval-jev-receipt.js +97 -0
- package/dist/pactile/task/session-retrieval-jev-receipt.js.map +1 -0
- package/dist/pactile/task/session-retrieval-jev.d.ts +8 -0
- package/dist/pactile/task/session-retrieval-jev.d.ts.map +1 -0
- package/dist/pactile/task/session-retrieval-jev.js +352 -0
- package/dist/pactile/task/session-retrieval-jev.js.map +1 -0
- package/dist/pactile/task/strategy.d.ts +1 -23
- package/dist/pactile/task/strategy.d.ts.map +1 -1
- package/dist/pactile/task/strategy.js +2 -135
- package/dist/pactile/task/strategy.js.map +1 -1
- package/dist/pactile/tiles/jev-selection.d.ts +44 -0
- package/dist/pactile/tiles/jev-selection.d.ts.map +1 -0
- package/dist/pactile/tiles/jev-selection.js +210 -0
- package/dist/pactile/tiles/jev-selection.js.map +1 -0
- package/dist/pactile/tiles/safe-yaml.d.ts +5 -1
- package/dist/pactile/tiles/safe-yaml.d.ts.map +1 -1
- package/dist/pactile/tiles/safe-yaml.js +8 -8
- package/dist/pactile/tiles/safe-yaml.js.map +1 -1
- package/dist/pactile/tiles/selection-receipts.d.ts +71 -0
- package/dist/pactile/tiles/selection-receipts.d.ts.map +1 -0
- package/dist/pactile/tiles/selection-receipts.js +319 -0
- package/dist/pactile/tiles/selection-receipts.js.map +1 -0
- package/dist/pactile/tiles/selection.d.ts +185 -0
- package/dist/pactile/tiles/selection.d.ts.map +1 -0
- package/dist/pactile/tiles/selection.js +985 -0
- package/dist/pactile/tiles/selection.js.map +1 -0
- package/dist/pactile/verification/git-observer.d.ts +3 -0
- package/dist/pactile/verification/git-observer.d.ts.map +1 -0
- package/dist/pactile/verification/git-observer.js +3 -0
- package/dist/pactile/verification/git-observer.js.map +1 -0
- package/dist/pactile/verification/index.d.ts +11 -0
- package/dist/pactile/verification/index.d.ts.map +1 -0
- package/dist/pactile/verification/index.js +6 -0
- package/dist/pactile/verification/index.js.map +1 -0
- package/dist/pactile/verification/jev-advice.d.ts +78 -0
- package/dist/pactile/verification/jev-advice.d.ts.map +1 -0
- package/dist/pactile/verification/jev-advice.js +397 -0
- package/dist/pactile/verification/jev-advice.js.map +1 -0
- package/dist/pactile/verification/planner.d.ts +15 -0
- package/dist/pactile/verification/planner.d.ts.map +1 -0
- package/dist/pactile/verification/planner.js +412 -0
- package/dist/pactile/verification/planner.js.map +1 -0
- package/dist/pactile/verification/receipt.d.ts +101 -0
- package/dist/pactile/verification/receipt.d.ts.map +1 -0
- package/dist/pactile/verification/receipt.js +400 -0
- package/dist/pactile/verification/receipt.js.map +1 -0
- package/dist/pactile/verification/types.d.ts +72 -0
- package/dist/pactile/verification/types.d.ts.map +1 -0
- package/dist/pactile/verification/types.js +21 -0
- package/dist/pactile/verification/types.js.map +1 -0
- package/dist/pactile/worktree/git-probe.d.ts +32 -0
- package/dist/pactile/worktree/git-probe.d.ts.map +1 -0
- package/dist/pactile/worktree/git-probe.js +336 -0
- package/dist/pactile/worktree/git-probe.js.map +1 -0
- package/dist/pactile/worktree/git-removal.d.ts +3 -0
- package/dist/pactile/worktree/git-removal.d.ts.map +1 -0
- package/dist/pactile/worktree/git-removal.js +11 -0
- package/dist/pactile/worktree/git-removal.js.map +1 -0
- package/dist/pactile/worktree/index.d.ts +2 -0
- package/dist/pactile/worktree/index.d.ts.map +1 -0
- package/dist/pactile/worktree/index.js +2 -0
- package/dist/pactile/worktree/index.js.map +1 -0
- package/dist/pactile/worktree/integration-evidence.d.ts +80 -0
- package/dist/pactile/worktree/integration-evidence.d.ts.map +1 -0
- package/dist/pactile/worktree/integration-evidence.js +166 -0
- package/dist/pactile/worktree/integration-evidence.js.map +1 -0
- package/dist/pactile/worktree/manager-core.d.ts +50 -0
- package/dist/pactile/worktree/manager-core.d.ts.map +1 -0
- package/dist/pactile/worktree/manager-core.js +359 -0
- package/dist/pactile/worktree/manager-core.js.map +1 -0
- package/dist/pactile/worktree/manager-provenance.d.ts +32 -0
- package/dist/pactile/worktree/manager-provenance.d.ts.map +1 -0
- package/dist/pactile/worktree/manager-provenance.js +409 -0
- package/dist/pactile/worktree/manager-provenance.js.map +1 -0
- package/dist/pactile/worktree/manager-types.d.ts +84 -0
- package/dist/pactile/worktree/manager-types.d.ts.map +1 -0
- package/dist/pactile/worktree/manager-types.js +11 -0
- package/dist/pactile/worktree/manager-types.js.map +1 -0
- package/dist/pactile/worktree/manager.d.ts +10 -0
- package/dist/pactile/worktree/manager.d.ts.map +1 -0
- package/dist/pactile/worktree/manager.js +6 -0
- package/dist/pactile/worktree/manager.js.map +1 -0
- package/dist/pactile/worktree/run-workspace-cleanup.d.ts +10 -0
- package/dist/pactile/worktree/run-workspace-cleanup.d.ts.map +1 -0
- package/dist/pactile/worktree/run-workspace-cleanup.js +695 -0
- package/dist/pactile/worktree/run-workspace-cleanup.js.map +1 -0
- package/dist/pactile/worktree/run-workspace-lifecycle.d.ts +65 -0
- package/dist/pactile/worktree/run-workspace-lifecycle.d.ts.map +1 -0
- package/dist/pactile/worktree/run-workspace-lifecycle.js +276 -0
- package/dist/pactile/worktree/run-workspace-lifecycle.js.map +1 -0
- package/dist/templates/common/bundled-skills/pactile-meta/references/customize-local/change-hooks.md +1 -1
- package/dist/templates/common/bundled-skills/pactile-meta/references/local-architecture/generated-files.md +1 -1
- package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/hooks-and-settings.md +1 -1
- package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/overview.md +1 -1
- package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/platform-map.md +1 -1
- package/dist/templates/common/bundled-skills/smart-search-cli/SKILL.md +2 -0
- package/dist/templates/common/skills/brainstorm.md +19 -13
- package/dist/templates/markdown/agents.md +2 -2
- package/dist/templates/markdown/framework/execution-strategy.md.txt +1 -1
- package/dist/templates/markdown/framework/index.md.txt +1 -1
- package/dist/templates/markdown/framework/parallel-first-execution.md.txt +17 -105
- package/dist/templates/markdown/framework/prd-grill-frontier.md.txt +3 -3
- package/dist/templates/markdown/framework/release-boundary.md.txt +9 -10
- package/dist/templates/markdown/framework/retrieval-daily-guide.md.txt +1 -1
- package/dist/templates/pactile/CONTEXT.md +48 -36
- package/dist/templates/pactile/modules/approval-personal/contract.md +12 -24
- package/dist/templates/pactile/modules/close-basic/contract.md +15 -18
- package/dist/templates/pactile/modules/debug-recovery/contract.md +4 -4
- package/dist/templates/pactile/modules/define-basic/contract.md +15 -19
- package/dist/templates/pactile/modules/define-extended/contract.md +3 -3
- package/dist/templates/pactile/modules/execute-agent/contract.md +11 -19
- package/dist/templates/pactile/modules/intake-basic/contract.md +16 -13
- package/dist/templates/pactile/modules/verify-basic/contract.md +11 -20
- package/dist/templates/pactile/tasks/templates/release-execution/design.md +5 -5
- package/dist/templates/pactile/tasks/templates/release-readiness/design.md +6 -6
- package/dist/templates/pactile/tasks/templates/release-readiness/handoff-template.md +8 -2
- package/dist/templates/pactile/workflow.md +37 -41
- package/dist/templates/template-utils.d.ts +1 -2
- package/dist/templates/template-utils.d.ts.map +1 -1
- package/dist/templates/template-utils.js +2 -13
- package/dist/templates/template-utils.js.map +1 -1
- package/dist/types/ai-tools.d.ts +1 -1
- package/dist/types/ai-tools.js +1 -1
- package/dist/utils/manifest-prune.js +1 -1
- package/dist/utils/manifest-prune.js.map +1 -1
- package/dist/utils/p36-upgrade.d.ts +2 -2
- package/dist/utils/p36-upgrade.d.ts.map +1 -1
- package/dist/utils/p36-upgrade.js +2 -10
- package/dist/utils/p36-upgrade.js.map +1 -1
- package/dist/utils/post-update-smoke.js +1 -1
- package/dist/utils/post-update-smoke.js.map +1 -1
- package/dist/utils/readiness.d.ts +2 -2
- package/dist/utils/readiness.js +2 -2
- package/dist/utils/uninstall-scrubbers.js +1 -1
- package/dist/utils/workflow-dir.d.ts +1 -1
- package/dist/utils/workflow-dir.d.ts.map +1 -1
- package/dist/utils/workflow-dir.js +1 -1
- package/dist/utils/workflow-dir.js.map +1 -1
- package/package.json +42 -37
- package/CHANGELOG.md +0 -651
- package/bin/compat-warning.js +0 -11
- package/bin/pactile.js +0 -3
- package/bin/smart-search.js +0 -38
- package/dist/migrations/manifests/0.1.0.json +0 -9
- package/dist/migrations/manifests/0.1.1.json +0 -9
- package/dist/migrations/manifests/0.1.2.json +0 -9
- package/dist/migrations/manifests/0.1.3.json +0 -9
- package/dist/migrations/manifests/0.1.4.json +0 -9
- package/dist/migrations/manifests/0.2.1.json +0 -9
- package/dist/migrations/manifests/0.2.10.json +0 -374
- package/dist/migrations/manifests/0.2.2.json +0 -9
- package/dist/migrations/manifests/0.2.3.json +0 -9
- package/dist/migrations/manifests/0.2.4.json +0 -9
- package/dist/migrations/manifests/0.2.5.json +0 -9
- package/dist/migrations/manifests/0.2.6.json +0 -9
- package/dist/migrations/manifests/0.2.7.json +0 -9
- package/dist/migrations/manifests/0.2.8.json +0 -9
- package/dist/migrations/manifests/0.2.9.json +0 -9
- package/dist/migrations/manifests/0.3.0.json +0 -89
- package/dist/migrations/manifests/0.3.1.json +0 -32
- package/dist/migrations/manifests/0.3.2.json +0 -16
- package/dist/migrations/manifests/0.3.3.json +0 -9
- package/dist/migrations/manifests/0.3.4.json +0 -9
- package/dist/migrations/manifests/0.3.5.json +0 -9
- package/dist/migrations/manifests/0.3.6.json +0 -9
- package/dist/migrations/manifests/0.4.0.json +0 -9
- package/dist/migrations/manifests/0.4.1.json +0 -9
- package/dist/migrations/manifests/0.4.2.json +0 -9
- package/dist/migrations/manifests/0.4.3.json +0 -9
- package/dist/migrations/manifests/0.5.0-beta.0.json +0 -9
- package/dist/migrations/manifests/0.5.0-beta.1.json +0 -9
- package/dist/migrations/manifests/0.5.0-beta.2.json +0 -9
- package/dist/migrations/manifests/0.5.0-beta.3.json +0 -9
- package/dist/migrations/manifests/0.5.0-beta.4.json +0 -9
- package/dist/migrations/manifests/0.5.0-beta.5.json +0 -9
- package/dist/migrations/manifests/0.5.0.json +0 -9
- package/dist/pactile/compat/retired-alternate-client.d.ts +0 -42
- package/dist/pactile/compat/retired-alternate-client.d.ts.map +0 -1
- package/dist/pactile/compat/retired-alternate-client.js +0 -153
- package/dist/pactile/compat/retired-alternate-client.js.map +0 -1
- package/dist/pactile/compat/update-residue.d.ts +0 -4
- package/dist/pactile/compat/update-residue.d.ts.map +0 -1
- package/dist/pactile/compat/update-residue.js +0 -55
- package/dist/pactile/compat/update-residue.js.map +0 -1
- package/dist/utils/cursor-retrieval-env.d.ts +0 -17
- package/dist/utils/cursor-retrieval-env.d.ts.map +0 -1
- package/dist/utils/cursor-retrieval-env.js +0 -19
- package/dist/utils/cursor-retrieval-env.js.map +0 -1
- package/dist/utils/proxy.d.ts +0 -25
- package/dist/utils/proxy.d.ts.map +0 -1
- package/dist/utils/proxy.js +0 -60
- package/dist/utils/proxy.js.map +0 -1
- package/scripts/postinstall.js +0 -6
|
@@ -0,0 +1,1448 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { KernelError, fingerprintTaskValue, listTaskKernelSnapshots, projectTaskKernelLifecycle, readTaskKernel, } from "../../core/task/index.js";
|
|
4
|
+
import { listLegacyTaskMigrationDirectories, readLegacyTaskImportRecord, } from "../../core/task/legacy-task-migration-reader.js";
|
|
5
|
+
import { approvedTask, piWorkdir } from "../pi/bridge.js";
|
|
6
|
+
import { resolveTaskDir } from "../task/session.js";
|
|
7
|
+
import { readTaskMap } from "../task/task-map.js";
|
|
8
|
+
import { inspectRunWorktree } from "../worktree/manager.js";
|
|
9
|
+
import { resolveJevProjectEgressPolicyV1, } from "../jev/project-policy.js";
|
|
10
|
+
import { applyJevTaskScheduleEgressFallbackV1, finalizeJevTaskScheduleAdviceV1, finalizeJevTaskScheduleEligibilityV1, requestJevTaskScheduleAdviceV1, supersedeJevTaskScheduleAdviceV1, } from "../jev/scheduler-advice.js";
|
|
11
|
+
import { listProjectWriteLeases, projectWriteSetsConflict, withProjectSchedulerMutex, } from "./project-lease-store.js";
|
|
12
|
+
import { hasActiveOrUncertainTaskRunDispatch } from "./task-run-dispatch-state.js";
|
|
13
|
+
import { planTaskScheduleV1, } from "./scheduler.js";
|
|
14
|
+
const TASK_RECORD = "task.json";
|
|
15
|
+
const KERNEL_RECORD = "kernel.json";
|
|
16
|
+
const RECEIPT_SCHEMA_VERSION = 1;
|
|
17
|
+
const COST_FIELDS = [
|
|
18
|
+
"latencyMs",
|
|
19
|
+
"waitingMs",
|
|
20
|
+
"executionMs",
|
|
21
|
+
"integrationMs",
|
|
22
|
+
"reworkMs",
|
|
23
|
+
"reviewMs",
|
|
24
|
+
];
|
|
25
|
+
function compareText(left, right) {
|
|
26
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
27
|
+
}
|
|
28
|
+
function relativeTaskDir(root, dir) {
|
|
29
|
+
return path.relative(root, dir).replaceAll("\\", "/");
|
|
30
|
+
}
|
|
31
|
+
function canonicalFilePath(file) {
|
|
32
|
+
return path.resolve(file);
|
|
33
|
+
}
|
|
34
|
+
function activeTaskDirectories(root) {
|
|
35
|
+
const tasksRoot = path.resolve(root, ".pactile", "tasks");
|
|
36
|
+
if (!fs.existsSync(tasksRoot))
|
|
37
|
+
return [];
|
|
38
|
+
const directories = new Set(fs
|
|
39
|
+
.readdirSync(tasksRoot, { withFileTypes: true })
|
|
40
|
+
.filter((entry) => entry.isDirectory() &&
|
|
41
|
+
!["archive", "locale", "templates"].includes(entry.name))
|
|
42
|
+
.map((entry) => path.join(tasksRoot, entry.name))
|
|
43
|
+
.filter((dir) => fs.existsSync(path.join(dir, TASK_RECORD)) ||
|
|
44
|
+
fs.existsSync(path.join(dir, KERNEL_RECORD))));
|
|
45
|
+
for (const dir of listLegacyTaskMigrationDirectories(root)) {
|
|
46
|
+
const relative = path.relative(tasksRoot, dir);
|
|
47
|
+
if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) ||
|
|
48
|
+
relative.split(path.sep).some((part) => part.toLowerCase() === "archive"))
|
|
49
|
+
continue;
|
|
50
|
+
if (readLegacyTaskImportRecord(root, dir)?.status === "imported")
|
|
51
|
+
directories.add(dir);
|
|
52
|
+
}
|
|
53
|
+
return [...directories].sort(compareText);
|
|
54
|
+
}
|
|
55
|
+
function indexTasks(root) {
|
|
56
|
+
const byId = new Map();
|
|
57
|
+
const byDir = new Map();
|
|
58
|
+
const byArchivedId = new Map();
|
|
59
|
+
for (const dir of activeTaskDirectories(root)) {
|
|
60
|
+
const read = readTaskKernel({ root, taskDir: dir });
|
|
61
|
+
const taskId = read.kind === "task-kernel-v2"
|
|
62
|
+
? read.kernel.identity.taskId
|
|
63
|
+
: read.kernel.kernel.identity.taskId;
|
|
64
|
+
if (byId.has(taskId))
|
|
65
|
+
throw new Error(`Duplicate active Task identity: ${taskId}`);
|
|
66
|
+
const indexed = {
|
|
67
|
+
dir,
|
|
68
|
+
taskId,
|
|
69
|
+
read,
|
|
70
|
+
kernel: read.kind === "task-kernel-v2" ? read.kernel : null,
|
|
71
|
+
};
|
|
72
|
+
byId.set(taskId, indexed);
|
|
73
|
+
byDir.set(canonicalFilePath(dir), indexed);
|
|
74
|
+
}
|
|
75
|
+
const archivePrefix = `${path.resolve(root, ".pactile", "tasks", "archive")}${path.sep}`.toLowerCase();
|
|
76
|
+
for (const { taskDir, kernel } of listTaskKernelSnapshots(root)) {
|
|
77
|
+
const dir = path.resolve(taskDir);
|
|
78
|
+
if (!dir.toLowerCase().startsWith(archivePrefix))
|
|
79
|
+
continue;
|
|
80
|
+
const taskId = kernel.identity.taskId;
|
|
81
|
+
if (byId.has(taskId) || byArchivedId.has(taskId))
|
|
82
|
+
throw new Error(`Duplicate active or archived Task identity: ${taskId}`);
|
|
83
|
+
byArchivedId.set(taskId, {
|
|
84
|
+
dir,
|
|
85
|
+
taskId,
|
|
86
|
+
read: { kind: "task-kernel-v2", kernel },
|
|
87
|
+
kernel,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return { byId, byDir, byArchivedId };
|
|
91
|
+
}
|
|
92
|
+
function reconciliationRecordForError(error) {
|
|
93
|
+
if (!(error instanceof KernelError))
|
|
94
|
+
return null;
|
|
95
|
+
if (error.code === "LEGACY_TASK_REQUIRES_DEFINITION")
|
|
96
|
+
return "needs-definition";
|
|
97
|
+
if (error.code === "LEGACY_TASK_REQUIRES_COORDINATION")
|
|
98
|
+
return "needs-coordination";
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
function reconciliationDetail(record, status) {
|
|
102
|
+
return status === "needs-definition"
|
|
103
|
+
? `missing definition fields: ${record.missingDefinitionFields.join(", ")}`
|
|
104
|
+
: `unresolved blocking legacy dependencies: ${record.coordinationReasons.join(", ")}`;
|
|
105
|
+
}
|
|
106
|
+
function isReconciliationIndexedTask(task) {
|
|
107
|
+
return "migrationStatus" in task;
|
|
108
|
+
}
|
|
109
|
+
function indexTaskKernelGraphTasks(root) {
|
|
110
|
+
const byId = new Map();
|
|
111
|
+
const byArchivedId = new Map();
|
|
112
|
+
for (const dir of activeTaskDirectories(root)) {
|
|
113
|
+
let read;
|
|
114
|
+
try {
|
|
115
|
+
read = readTaskKernel({ root, taskDir: dir });
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
const expectedStatus = reconciliationRecordForError(error);
|
|
119
|
+
if (!expectedStatus)
|
|
120
|
+
throw error;
|
|
121
|
+
const record = readLegacyTaskImportRecord(root, dir);
|
|
122
|
+
if (record?.status !== expectedStatus)
|
|
123
|
+
throw error;
|
|
124
|
+
if (record.taskPath !== relativeTaskDir(root, dir))
|
|
125
|
+
throw error;
|
|
126
|
+
if (byId.has(record.legacyTaskId))
|
|
127
|
+
throw new Error(`Duplicate active Task identity: ${record.legacyTaskId}`);
|
|
128
|
+
byId.set(record.legacyTaskId, {
|
|
129
|
+
dir,
|
|
130
|
+
taskId: record.legacyTaskId,
|
|
131
|
+
migrationStatus: expectedStatus,
|
|
132
|
+
reconciliationDetail: reconciliationDetail(record, expectedStatus),
|
|
133
|
+
});
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const taskId = read.kind === "task-kernel-v2"
|
|
137
|
+
? read.kernel.identity.taskId
|
|
138
|
+
: read.kernel.kernel.identity.taskId;
|
|
139
|
+
if (byId.has(taskId))
|
|
140
|
+
throw new Error(`Duplicate active Task identity: ${taskId}`);
|
|
141
|
+
byId.set(taskId, {
|
|
142
|
+
dir,
|
|
143
|
+
taskId,
|
|
144
|
+
read,
|
|
145
|
+
kernel: read.kind === "task-kernel-v2" ? read.kernel : null,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const archivePrefix = `${path.resolve(root, ".pactile", "tasks", "archive")}${path.sep}`.toLowerCase();
|
|
149
|
+
for (const { taskDir, kernel } of listTaskKernelSnapshots(root)) {
|
|
150
|
+
const dir = path.resolve(taskDir);
|
|
151
|
+
if (!dir.toLowerCase().startsWith(archivePrefix))
|
|
152
|
+
continue;
|
|
153
|
+
const taskId = kernel.identity.taskId;
|
|
154
|
+
if (byId.has(taskId) || byArchivedId.has(taskId))
|
|
155
|
+
throw new Error(`Duplicate active or archived Task identity: ${taskId}`);
|
|
156
|
+
byArchivedId.set(taskId, {
|
|
157
|
+
dir,
|
|
158
|
+
taskId,
|
|
159
|
+
read: { kind: "task-kernel-v2", kernel },
|
|
160
|
+
kernel,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return { byId, byArchivedId };
|
|
164
|
+
}
|
|
165
|
+
function reconciliationFailure(task, relation) {
|
|
166
|
+
const subject = relation === "candidate" ? "Task candidate" : "Hard Task dependency";
|
|
167
|
+
const reason = task.migrationStatus === "needs-definition"
|
|
168
|
+
? "must be completed before it can run"
|
|
169
|
+
: "must be coordinated before it can run";
|
|
170
|
+
return new Error(`${subject} ${task.taskId} requires legacy reconciliation (${task.migrationStatus}): ${reason}; ${task.reconciliationDetail}`);
|
|
171
|
+
}
|
|
172
|
+
function resolveChildDependency(dependency, children, selectedByChildId, byId) {
|
|
173
|
+
const child = children.find((candidate) => candidate.id === dependency || candidate.id.endsWith(`-${dependency}`));
|
|
174
|
+
if (child) {
|
|
175
|
+
const selected = selectedByChildId.get(child.id);
|
|
176
|
+
if (!selected)
|
|
177
|
+
throw new Error(`Parent Child dependency is not in the schedule snapshot: ${dependency}`);
|
|
178
|
+
return selected;
|
|
179
|
+
}
|
|
180
|
+
const indexed = byId.get(dependency);
|
|
181
|
+
if (!indexed)
|
|
182
|
+
throw new Error(`Missing hard Task dependency: ${dependency}`);
|
|
183
|
+
return indexed;
|
|
184
|
+
}
|
|
185
|
+
function stateForChild(childState) {
|
|
186
|
+
switch (childState) {
|
|
187
|
+
case "integrated":
|
|
188
|
+
return "completed";
|
|
189
|
+
case "blocked":
|
|
190
|
+
case "cancelled":
|
|
191
|
+
return "blocked";
|
|
192
|
+
case "working":
|
|
193
|
+
case "review":
|
|
194
|
+
case "accepted":
|
|
195
|
+
case "integrating":
|
|
196
|
+
return "running";
|
|
197
|
+
case "open":
|
|
198
|
+
case "changes":
|
|
199
|
+
return "waiting";
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function stateForV2(kernel, child) {
|
|
203
|
+
const lifecycle = projectTaskKernelLifecycle(kernel);
|
|
204
|
+
if (lifecycle.condition === "blocked" ||
|
|
205
|
+
child?.state === "blocked" ||
|
|
206
|
+
child?.state === "cancelled")
|
|
207
|
+
return "blocked";
|
|
208
|
+
if (lifecycle.closed && lifecycle.outcome === "completed")
|
|
209
|
+
return "completed";
|
|
210
|
+
if (child?.state === "integrated")
|
|
211
|
+
return "blocked";
|
|
212
|
+
const latest = kernel.runs.at(-1) ?? null;
|
|
213
|
+
if (latest?.state === "failed")
|
|
214
|
+
return "failed";
|
|
215
|
+
if (latest?.state === "blocked")
|
|
216
|
+
return "blocked";
|
|
217
|
+
if (latest?.state === "running")
|
|
218
|
+
return "running";
|
|
219
|
+
if (latest?.state === "waiting")
|
|
220
|
+
return "waiting";
|
|
221
|
+
if (latest?.state === "completed")
|
|
222
|
+
return "running";
|
|
223
|
+
if (child)
|
|
224
|
+
return stateForChild(child.state);
|
|
225
|
+
return "blocked";
|
|
226
|
+
}
|
|
227
|
+
function stateForTaskKernelCandidate(root, taskId, kernel) {
|
|
228
|
+
const projectedState = stateForV2(kernel, null);
|
|
229
|
+
const lifecycle = projectTaskKernelLifecycle(kernel);
|
|
230
|
+
const latestRun = kernel.runs.at(-1) ?? null;
|
|
231
|
+
if (projectedState === "running" &&
|
|
232
|
+
latestRun?.state === "running" &&
|
|
233
|
+
latestRun.host === null &&
|
|
234
|
+
latestRun.candidateSnapshot === null &&
|
|
235
|
+
!hasActiveOrUncertainTaskRunDispatch(root, taskId, latestRun.id) &&
|
|
236
|
+
kernel.events.some((event) => event.entityId === latestRun.id && event.type === "run.resumed")) {
|
|
237
|
+
// A public resume starts the Kernel Run; until a Host is bound, keep it
|
|
238
|
+
// eligible for the next schedule receipt to dispatch its provider work.
|
|
239
|
+
return "waiting";
|
|
240
|
+
}
|
|
241
|
+
if (projectedState === "blocked" &&
|
|
242
|
+
kernel.condition === "ready" &&
|
|
243
|
+
lifecycle.gateSnapshot.runStart.phaseAllowsRun) {
|
|
244
|
+
return "waiting";
|
|
245
|
+
}
|
|
246
|
+
return projectedState;
|
|
247
|
+
}
|
|
248
|
+
function stateForLegacy(indexed, child) {
|
|
249
|
+
if (child)
|
|
250
|
+
return stateForChild(child.state);
|
|
251
|
+
if (indexed.read.kind !== "legacy-task-kernel-v1")
|
|
252
|
+
return "blocked";
|
|
253
|
+
const legacyStatus = indexed.read.kernel.legacy.status.toLowerCase();
|
|
254
|
+
if (["completed", "integrated"].includes(legacyStatus))
|
|
255
|
+
return "completed";
|
|
256
|
+
if (legacyStatus === "failed")
|
|
257
|
+
return "failed";
|
|
258
|
+
return "blocked";
|
|
259
|
+
}
|
|
260
|
+
function sourceDependencies(indexed, child, children, selectedByChildId, byId) {
|
|
261
|
+
if (indexed.kernel) {
|
|
262
|
+
return indexed.kernel.definition.dependencies.map((dependency) => {
|
|
263
|
+
const resolved = resolveChildDependency(dependency, children, selectedByChildId, byId);
|
|
264
|
+
return resolved.taskId;
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
if (child) {
|
|
268
|
+
return child.depends_on.map((dependency) => {
|
|
269
|
+
const resolved = resolveChildDependency(dependency, children, selectedByChildId, byId);
|
|
270
|
+
return resolved.taskId;
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return [];
|
|
274
|
+
}
|
|
275
|
+
function parseInstant(value) {
|
|
276
|
+
if (!value)
|
|
277
|
+
return null;
|
|
278
|
+
const parsed = Date.parse(value);
|
|
279
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
280
|
+
}
|
|
281
|
+
function nonNegativeDelta(start, end) {
|
|
282
|
+
if (start === null || end === null || end < start)
|
|
283
|
+
return null;
|
|
284
|
+
return end - start;
|
|
285
|
+
}
|
|
286
|
+
function runObservedCosts(kernel, run, child, taskMapBody) {
|
|
287
|
+
const observed = Object.fromEntries(COST_FIELDS.map((key) => [key, null]));
|
|
288
|
+
if (kernel && run) {
|
|
289
|
+
const runEvents = kernel.events.filter((event) => event.entityId === run.id);
|
|
290
|
+
const queuedAt = parseInstant(runEvents.find((event) => event.type === "run.queued")?.at);
|
|
291
|
+
const startedEvent = runEvents.find((event) => event.type === "run.started" || event.type === "run.resumed");
|
|
292
|
+
const startedAt = parseInstant(startedEvent?.at);
|
|
293
|
+
observed.waitingMs = nonNegativeDelta(queuedAt, startedAt);
|
|
294
|
+
observed.executionMs = nonNegativeDelta(startedAt, parseInstant(run.completedAt));
|
|
295
|
+
const review = kernel.reviews
|
|
296
|
+
.filter((candidate) => candidate.runId === run.id)
|
|
297
|
+
.at(-1);
|
|
298
|
+
observed.reviewMs = nonNegativeDelta(parseInstant(run.completedAt), parseInstant(review?.reviewedAt));
|
|
299
|
+
}
|
|
300
|
+
if (child) {
|
|
301
|
+
const escaped = child.id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
302
|
+
const pattern = new RegExp(`^- (\\d{4}-\\d\\d-\\d\\dT[^ ]+) - (?:Parent integrated|Child reported) ${escaped} as (changes|accepted|integrating|integrated)\\.$`, "gm");
|
|
303
|
+
const events = [...taskMapBody.matchAll(pattern)]
|
|
304
|
+
.map((match) => ({ at: parseInstant(match[1]), state: match[2] }))
|
|
305
|
+
.filter((event) => event.at !== null)
|
|
306
|
+
.sort((left, right) => left.at - right.at);
|
|
307
|
+
let openedRework = null;
|
|
308
|
+
let integrationStart = null;
|
|
309
|
+
let reworkTotal = 0;
|
|
310
|
+
let integrationTotal = 0;
|
|
311
|
+
let completedReworkCycles = 0;
|
|
312
|
+
let completedIntegrationCycles = 0;
|
|
313
|
+
for (const event of events) {
|
|
314
|
+
if (event.state === "changes")
|
|
315
|
+
openedRework = event.at;
|
|
316
|
+
else if (openedRework !== null &&
|
|
317
|
+
["accepted", "integrating", "integrated"].includes(event.state)) {
|
|
318
|
+
reworkTotal += Math.max(0, event.at - openedRework);
|
|
319
|
+
completedReworkCycles += 1;
|
|
320
|
+
openedRework = null;
|
|
321
|
+
}
|
|
322
|
+
if (event.state === "integrating")
|
|
323
|
+
integrationStart = event.at;
|
|
324
|
+
if (event.state === "integrated" && integrationStart !== null) {
|
|
325
|
+
integrationTotal += Math.max(0, event.at - integrationStart);
|
|
326
|
+
completedIntegrationCycles += 1;
|
|
327
|
+
integrationStart = null;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (completedReworkCycles)
|
|
331
|
+
observed.reworkMs = reworkTotal;
|
|
332
|
+
if (completedIntegrationCycles)
|
|
333
|
+
observed.integrationMs = integrationTotal;
|
|
334
|
+
}
|
|
335
|
+
return observed;
|
|
336
|
+
}
|
|
337
|
+
function emptyCosts() {
|
|
338
|
+
return {
|
|
339
|
+
latencyMs: 0,
|
|
340
|
+
waitingMs: 0,
|
|
341
|
+
executionMs: 0,
|
|
342
|
+
integrationMs: 0,
|
|
343
|
+
reworkMs: 0,
|
|
344
|
+
reviewMs: 0,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function definedObservedCosts(costs) {
|
|
348
|
+
return Object.fromEntries(COST_FIELDS.flatMap((field) => costs[field] === null ? [] : [[field, costs[field]]]));
|
|
349
|
+
}
|
|
350
|
+
function mergedWriteSet(indexed, child) {
|
|
351
|
+
const run = indexed.kernel?.runs.at(-1) ?? null;
|
|
352
|
+
const values = new Set();
|
|
353
|
+
const bases = [];
|
|
354
|
+
if (run?.writeSetSnapshot.length) {
|
|
355
|
+
run.writeSetSnapshot.forEach((value) => values.add(value));
|
|
356
|
+
bases.push("task-run-writeSetSnapshot");
|
|
357
|
+
}
|
|
358
|
+
if (run?.workspace?.writeSet.length) {
|
|
359
|
+
run.workspace.writeSet.forEach((value) => values.add(value));
|
|
360
|
+
bases.push("run-workspace-writeSet");
|
|
361
|
+
}
|
|
362
|
+
if (child?.touches.length) {
|
|
363
|
+
child.touches.forEach((value) => values.add(value));
|
|
364
|
+
bases.push("task-map-touches");
|
|
365
|
+
}
|
|
366
|
+
if (!values.size)
|
|
367
|
+
return { value: null, basis: "unknown-write-scope" };
|
|
368
|
+
return { value: [...values].sort(compareText), basis: bases.join("+") };
|
|
369
|
+
}
|
|
370
|
+
function estimatedCosts(indexed, overrides) {
|
|
371
|
+
const run = indexed.kernel?.runs.at(-1) ?? null;
|
|
372
|
+
const runEstimate = run?.estimatedDurations;
|
|
373
|
+
const basis = {};
|
|
374
|
+
const costs = {};
|
|
375
|
+
for (const field of COST_FIELDS) {
|
|
376
|
+
const supplied = overrides?.[field];
|
|
377
|
+
const runValue = field === "executionMs"
|
|
378
|
+
? runEstimate?.executionMs
|
|
379
|
+
: field === "waitingMs"
|
|
380
|
+
? runEstimate?.waitingMs
|
|
381
|
+
: field === "reviewMs"
|
|
382
|
+
? runEstimate?.reviewMs
|
|
383
|
+
: null;
|
|
384
|
+
if (supplied !== undefined) {
|
|
385
|
+
costs[field] = supplied;
|
|
386
|
+
basis[field] = "caller-estimate";
|
|
387
|
+
}
|
|
388
|
+
else if (typeof runValue === "number") {
|
|
389
|
+
costs[field] = runValue;
|
|
390
|
+
basis[field] = "task-run-estimate";
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
costs[field] = 0;
|
|
394
|
+
basis[field] = "unmeasured-zero";
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return { costs, basis };
|
|
398
|
+
}
|
|
399
|
+
function toSnapshot(root, indexed, child, taskMapBody, overrides, dependencyTaskIds, notes) {
|
|
400
|
+
const kernel = indexed.kernel;
|
|
401
|
+
const run = kernel?.runs.at(-1) ?? null;
|
|
402
|
+
const write = mergedWriteSet(indexed, child);
|
|
403
|
+
const estimate = estimatedCosts(indexed, overrides);
|
|
404
|
+
if (kernel &&
|
|
405
|
+
child?.state === "integrated" &&
|
|
406
|
+
!projectTaskKernelLifecycle(kernel).closed) {
|
|
407
|
+
notes.push("task-map-integrated-without-v2-close");
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
taskId: indexed.taskId,
|
|
411
|
+
taskDir: relativeTaskDir(root, indexed.dir),
|
|
412
|
+
sourceKind: indexed.read.kind,
|
|
413
|
+
taskMapChildId: child?.id ?? null,
|
|
414
|
+
childState: child?.state ?? null,
|
|
415
|
+
kernelRevision: kernel?.revision ?? null,
|
|
416
|
+
lifecyclePhase: kernel?.phase ?? null,
|
|
417
|
+
lifecycleCondition: kernel?.condition ?? null,
|
|
418
|
+
runId: run?.id ?? null,
|
|
419
|
+
runState: run?.state ?? null,
|
|
420
|
+
runMeasurementRefs: run ? { ...run.measurementRefs } : null,
|
|
421
|
+
observedCostEvidenceRefs: [
|
|
422
|
+
...(kernel && run
|
|
423
|
+
? kernel.events
|
|
424
|
+
.filter((event) => event.entityId === run.id)
|
|
425
|
+
.map((event) => `kernel-event:${event.id}`)
|
|
426
|
+
: []),
|
|
427
|
+
...(kernel && run
|
|
428
|
+
? kernel.reviews
|
|
429
|
+
.filter((review) => review.runId === run.id)
|
|
430
|
+
.map((review) => `kernel-review:${review.id}`)
|
|
431
|
+
: []),
|
|
432
|
+
...(child ? [`task-map-event-log:${child.id}`] : []),
|
|
433
|
+
],
|
|
434
|
+
schedulerState: kernel
|
|
435
|
+
? stateForV2(kernel, child)
|
|
436
|
+
: child
|
|
437
|
+
? stateForChild(child.state)
|
|
438
|
+
: "blocked",
|
|
439
|
+
dependencyTaskIds: [...dependencyTaskIds].sort(compareText),
|
|
440
|
+
writeSet: write.value,
|
|
441
|
+
writeSetBasis: write.basis,
|
|
442
|
+
estimatedCosts: estimate.costs,
|
|
443
|
+
estimateBasis: estimate.basis,
|
|
444
|
+
observedCosts: runObservedCosts(kernel, run, child, taskMapBody),
|
|
445
|
+
projectionNotes: notes,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
function activeTaskState(indexed, child) {
|
|
449
|
+
return indexed.kernel
|
|
450
|
+
? stateForV2(indexed.kernel, child)
|
|
451
|
+
: stateForLegacy(indexed, child);
|
|
452
|
+
}
|
|
453
|
+
function allDependencies(indexed, child, children, selectedByChildId, byId) {
|
|
454
|
+
return sourceDependencies(indexed, child, children, selectedByChildId, byId);
|
|
455
|
+
}
|
|
456
|
+
function buildPlanningSnapshot(rootValue, parentRef, options) {
|
|
457
|
+
const root = path.resolve(rootValue);
|
|
458
|
+
const parentDir = resolveTaskDir(root, parentRef);
|
|
459
|
+
const parentMap = readTaskMap(parentDir);
|
|
460
|
+
if (!parentMap.data)
|
|
461
|
+
throw new Error("Parent task-map.md is required for Task scheduling");
|
|
462
|
+
const map = parentMap.data;
|
|
463
|
+
if (map.execution_topology !== "parallel")
|
|
464
|
+
throw new Error("Parent execution_topology must be parallel for Task scheduling");
|
|
465
|
+
if (new Set(map.children.map((child) => child.id)).size !== map.children.length)
|
|
466
|
+
throw new Error("Parent task-map.md contains duplicate Child IDs");
|
|
467
|
+
const { byId, byDir } = indexTasks(root);
|
|
468
|
+
const selectedByChildId = new Map();
|
|
469
|
+
const selectedByTaskId = new Map();
|
|
470
|
+
for (const child of map.children) {
|
|
471
|
+
const dir = resolveTaskDir(root, child.id);
|
|
472
|
+
const indexed = byDir.get(canonicalFilePath(dir));
|
|
473
|
+
if (!indexed)
|
|
474
|
+
throw new Error(`Parent Child task record is missing: ${child.id}`);
|
|
475
|
+
if (selectedByTaskId.has(indexed.taskId))
|
|
476
|
+
throw new Error(`Parent task-map maps multiple Children to Task ${indexed.taskId}`);
|
|
477
|
+
const selected = {
|
|
478
|
+
...indexed,
|
|
479
|
+
child,
|
|
480
|
+
dependencies: [],
|
|
481
|
+
notes: [],
|
|
482
|
+
};
|
|
483
|
+
selectedByChildId.set(child.id, selected);
|
|
484
|
+
selectedByTaskId.set(indexed.taskId, selected);
|
|
485
|
+
}
|
|
486
|
+
for (const selected of selectedByTaskId.values()) {
|
|
487
|
+
selected.dependencies = allDependencies(selected, selected.child, map.children, selectedByChildId, byId);
|
|
488
|
+
}
|
|
489
|
+
const candidateTaskIds = options.candidateTaskIds
|
|
490
|
+
? new Set(options.candidateTaskIds)
|
|
491
|
+
: new Set(selectedByTaskId.keys());
|
|
492
|
+
if (options.candidateTaskIds &&
|
|
493
|
+
candidateTaskIds.size !== options.candidateTaskIds.length)
|
|
494
|
+
throw new Error("candidateTaskIds contains duplicates");
|
|
495
|
+
const unknownCandidates = [...candidateTaskIds].filter((id) => !selectedByTaskId.has(id));
|
|
496
|
+
if (unknownCandidates.length)
|
|
497
|
+
throw new Error(`Schedule candidates are not Parent Children: ${unknownCandidates.sort(compareText).join(", ")}`);
|
|
498
|
+
const graphNodes = new Map();
|
|
499
|
+
const visit = (taskId, selected, visiting) => {
|
|
500
|
+
const existing = graphNodes.get(taskId);
|
|
501
|
+
if (existing)
|
|
502
|
+
return;
|
|
503
|
+
if (visiting.has(taskId))
|
|
504
|
+
return;
|
|
505
|
+
const indexed = selected ?? byId.get(taskId);
|
|
506
|
+
if (!indexed)
|
|
507
|
+
throw new Error(`Missing hard Task dependency: ${taskId}`);
|
|
508
|
+
const child = selected?.child ?? null;
|
|
509
|
+
const isCandidate = candidateTaskIds.has(taskId);
|
|
510
|
+
const currentState = activeTaskState(indexed, child);
|
|
511
|
+
const schedulerState = !isCandidate && currentState !== "completed"
|
|
512
|
+
? currentState === "failed"
|
|
513
|
+
? "failed"
|
|
514
|
+
: currentState === "running"
|
|
515
|
+
? "running"
|
|
516
|
+
: "blocked"
|
|
517
|
+
: currentState;
|
|
518
|
+
const isCompleted = schedulerState === "completed";
|
|
519
|
+
const noteList = selected?.notes ?? [];
|
|
520
|
+
const dependencies = isCompleted
|
|
521
|
+
? []
|
|
522
|
+
: (selected?.dependencies ??
|
|
523
|
+
allDependencies(indexed, null, map.children, selectedByChildId, byId));
|
|
524
|
+
graphNodes.set(taskId, {
|
|
525
|
+
indexed,
|
|
526
|
+
child,
|
|
527
|
+
dependencies,
|
|
528
|
+
notes: noteList,
|
|
529
|
+
external: !isCandidate,
|
|
530
|
+
});
|
|
531
|
+
const nextVisiting = new Set(visiting);
|
|
532
|
+
nextVisiting.add(taskId);
|
|
533
|
+
for (const dependencyId of dependencies) {
|
|
534
|
+
const dependency = selectedByTaskId.get(dependencyId);
|
|
535
|
+
visit(dependencyId, dependency, nextVisiting);
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
for (const taskId of [...candidateTaskIds].sort(compareText)) {
|
|
539
|
+
const selected = selectedByTaskId.get(taskId);
|
|
540
|
+
if (selected)
|
|
541
|
+
visit(taskId, selected, new Set());
|
|
542
|
+
}
|
|
543
|
+
const costOverrides = options.estimatedCosts ?? {};
|
|
544
|
+
const unknownOverrides = Object.keys(costOverrides).filter((id) => !candidateTaskIds.has(id));
|
|
545
|
+
if (unknownOverrides.length)
|
|
546
|
+
throw new Error(`Cost estimates reference non-candidate Task IDs: ${unknownOverrides.sort(compareText).join(", ")}`);
|
|
547
|
+
const lifecycle = [];
|
|
548
|
+
const tasks = [];
|
|
549
|
+
for (const [taskId, graphNode] of [...graphNodes.entries()].sort(([left], [right]) => compareText(left, right))) {
|
|
550
|
+
const { indexed, child, dependencies, notes, external } = graphNode;
|
|
551
|
+
const schedulerState = external && activeTaskState(indexed, child) !== "completed"
|
|
552
|
+
? activeTaskState(indexed, child) === "failed"
|
|
553
|
+
? "failed"
|
|
554
|
+
: "blocked"
|
|
555
|
+
: activeTaskState(indexed, child);
|
|
556
|
+
const projected = toSnapshot(root, indexed, child, parentMap.body, external ? undefined : costOverrides[taskId], dependencies, notes);
|
|
557
|
+
if (external && schedulerState !== "completed")
|
|
558
|
+
projected.projectionNotes.push("external-hard-dependency-not-closed");
|
|
559
|
+
projected.schedulerState = schedulerState;
|
|
560
|
+
lifecycle.push(projected);
|
|
561
|
+
const schedulerWriteSet = external ? null : projected.writeSet;
|
|
562
|
+
const schedulerCosts = external ? emptyCosts() : projected.estimatedCosts;
|
|
563
|
+
tasks.push({
|
|
564
|
+
taskId,
|
|
565
|
+
dependsOn: dependencies,
|
|
566
|
+
state: schedulerState,
|
|
567
|
+
writeSet: schedulerWriteSet,
|
|
568
|
+
estimatedCosts: schedulerCosts,
|
|
569
|
+
observedCosts: external
|
|
570
|
+
? {}
|
|
571
|
+
: definedObservedCosts(projected.observedCosts),
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
const request = {
|
|
575
|
+
schemaVersion: 1,
|
|
576
|
+
tasks,
|
|
577
|
+
...(options.conflictParallelizations
|
|
578
|
+
? { conflictParallelizations: options.conflictParallelizations }
|
|
579
|
+
: {}),
|
|
580
|
+
...(options.jevAdvice ? { jevAdvice: options.jevAdvice } : {}),
|
|
581
|
+
};
|
|
582
|
+
return {
|
|
583
|
+
parentDir,
|
|
584
|
+
taskMapFingerprint: fingerprintTaskValue({
|
|
585
|
+
data: map,
|
|
586
|
+
body: parentMap.body,
|
|
587
|
+
}),
|
|
588
|
+
request,
|
|
589
|
+
lifecycle,
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
/** Projects a Parent task-map and its Task Kernel records into a deterministic DAG plan. */
|
|
593
|
+
export function planParentTaskScheduleV1(root, parentRef, options = {}) {
|
|
594
|
+
const snapshot = buildPlanningSnapshot(root, parentRef, options);
|
|
595
|
+
const plan = planTaskScheduleV1(snapshot.request);
|
|
596
|
+
return {
|
|
597
|
+
request: snapshot.request,
|
|
598
|
+
plan,
|
|
599
|
+
lifecycle: snapshot.lifecycle,
|
|
600
|
+
taskMapFingerprint: snapshot.taskMapFingerprint,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function receiptFingerprint(receipt) {
|
|
604
|
+
return fingerprintTaskValue(receipt);
|
|
605
|
+
}
|
|
606
|
+
function readExistingReceipt(file, fingerprint) {
|
|
607
|
+
const value = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
608
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
609
|
+
throw new Error(`Invalid Task schedule receipt: ${file}`);
|
|
610
|
+
const receipt = value;
|
|
611
|
+
const { schemaVersion, receiptFingerprint: storedFingerprint, createdAt: _createdAt, ...base } = receipt;
|
|
612
|
+
if (schemaVersion !== RECEIPT_SCHEMA_VERSION ||
|
|
613
|
+
storedFingerprint !== fingerprint ||
|
|
614
|
+
fingerprintTaskValue(base) !== fingerprint) {
|
|
615
|
+
throw new Error(`Task schedule receipt fingerprint does not match its contents: ${file}`);
|
|
616
|
+
}
|
|
617
|
+
return receipt;
|
|
618
|
+
}
|
|
619
|
+
function persistReceipt(parentDir, receiptBase) {
|
|
620
|
+
const fingerprint = receiptFingerprint(receiptBase);
|
|
621
|
+
const folder = path.join(parentDir, "scheduler", "receipts");
|
|
622
|
+
const file = path.join(folder, `${fingerprint}.json`);
|
|
623
|
+
fs.mkdirSync(folder, { recursive: true });
|
|
624
|
+
if (fs.existsSync(file)) {
|
|
625
|
+
return {
|
|
626
|
+
receipt: readExistingReceipt(file, fingerprint),
|
|
627
|
+
receiptFile: file,
|
|
628
|
+
created: false,
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
const receipt = {
|
|
632
|
+
...receiptBase,
|
|
633
|
+
schemaVersion: RECEIPT_SCHEMA_VERSION,
|
|
634
|
+
receiptFingerprint: fingerprint,
|
|
635
|
+
createdAt: new Date().toISOString(),
|
|
636
|
+
};
|
|
637
|
+
const temporary = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
638
|
+
try {
|
|
639
|
+
fs.writeFileSync(temporary, `${JSON.stringify(receipt, null, 2)}\n`, {
|
|
640
|
+
flag: "wx",
|
|
641
|
+
mode: 0o600,
|
|
642
|
+
});
|
|
643
|
+
try {
|
|
644
|
+
fs.copyFileSync(temporary, file, fs.constants.COPYFILE_EXCL);
|
|
645
|
+
return { receipt, receiptFile: file, created: true };
|
|
646
|
+
}
|
|
647
|
+
catch (error) {
|
|
648
|
+
if (error.code !== "EEXIST")
|
|
649
|
+
throw error;
|
|
650
|
+
return {
|
|
651
|
+
receipt: readExistingReceipt(file, fingerprint),
|
|
652
|
+
receiptFile: file,
|
|
653
|
+
created: false,
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
finally {
|
|
658
|
+
fs.rmSync(temporary, { force: true });
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
/** Plans and stores a content-addressed, append-only decision receipt under the Parent Task. */
|
|
662
|
+
export function scheduleParentTaskGraph(rootValue, parentRef, options = {}) {
|
|
663
|
+
const root = path.resolve(rootValue);
|
|
664
|
+
const snapshot = buildPlanningSnapshot(root, parentRef, options);
|
|
665
|
+
const plan = planTaskScheduleV1(snapshot.request);
|
|
666
|
+
const receiptBase = {
|
|
667
|
+
parentTaskDir: relativeTaskDir(root, snapshot.parentDir),
|
|
668
|
+
taskMapFingerprint: snapshot.taskMapFingerprint,
|
|
669
|
+
request: snapshot.request,
|
|
670
|
+
plan,
|
|
671
|
+
lifecycle: snapshot.lifecycle,
|
|
672
|
+
};
|
|
673
|
+
const stored = persistReceipt(snapshot.parentDir, receiptBase);
|
|
674
|
+
return { ...stored, receiptFile: relativeTaskDir(root, stored.receiptFile) };
|
|
675
|
+
}
|
|
676
|
+
function firstCriticalPathCandidates(plan) {
|
|
677
|
+
const firstWave = plan.waves[0];
|
|
678
|
+
if (!firstWave)
|
|
679
|
+
return [];
|
|
680
|
+
const decisions = new Map(plan.decisions.map((item) => [item.taskId, item]));
|
|
681
|
+
const candidates = firstWave.candidateTaskIds
|
|
682
|
+
.map((taskId) => decisions.get(taskId))
|
|
683
|
+
.filter((decision) => !!decision && decision.action === "scheduled");
|
|
684
|
+
if (candidates.length < 2)
|
|
685
|
+
return [];
|
|
686
|
+
const highestCriticalPath = Math.max(...candidates.map((candidate) => candidate.criticalPathMs));
|
|
687
|
+
return candidates
|
|
688
|
+
.filter((candidate) => candidate.criticalPathMs === highestCriticalPath)
|
|
689
|
+
.map((candidate) => ({
|
|
690
|
+
taskId: candidate.taskId,
|
|
691
|
+
criticalPathMs: candidate.criticalPathMs,
|
|
692
|
+
estimatedCostMs: candidate.estimatedCostMs,
|
|
693
|
+
}))
|
|
694
|
+
.sort((left, right) => compareText(left.taskId, right.taskId));
|
|
695
|
+
}
|
|
696
|
+
function sameTaskIds(left, right) {
|
|
697
|
+
return (left.length === right.length &&
|
|
698
|
+
left.every((taskId, index) => taskId === right[index]));
|
|
699
|
+
}
|
|
700
|
+
function sameTaskIdSet(left, right) {
|
|
701
|
+
return (left.length === right.length &&
|
|
702
|
+
new Set(left).size === left.length &&
|
|
703
|
+
new Set(right).size === right.length &&
|
|
704
|
+
left.every((taskId) => right.includes(taskId)));
|
|
705
|
+
}
|
|
706
|
+
function eligibilityFingerprint(filteredCandidates, candidates) {
|
|
707
|
+
return fingerprintTaskValue({
|
|
708
|
+
candidateTaskIds: candidates.map(({ taskId }) => taskId),
|
|
709
|
+
filteredCandidates: filteredCandidates.map(({ taskId, reasonCode }) => ({
|
|
710
|
+
taskId,
|
|
711
|
+
reasonCode,
|
|
712
|
+
})),
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
function verifyJevScheduleCandidates(root, snapshot, candidates) {
|
|
716
|
+
let activeLeases = null;
|
|
717
|
+
let activeLeaseCheckAt = null;
|
|
718
|
+
try {
|
|
719
|
+
activeLeases = withProjectSchedulerMutex(root, () => listProjectWriteLeases(root));
|
|
720
|
+
activeLeaseCheckAt = new Date().toISOString();
|
|
721
|
+
}
|
|
722
|
+
catch {
|
|
723
|
+
// An unreadable lease set is not safe input for an external advice request.
|
|
724
|
+
}
|
|
725
|
+
const byTaskId = new Map(snapshot.lifecycle.map((item) => [item.taskId, item]));
|
|
726
|
+
const eligible = [];
|
|
727
|
+
const filteredCandidates = [];
|
|
728
|
+
const approvalPassedTaskIds = [];
|
|
729
|
+
const worktreePassedTaskIds = [];
|
|
730
|
+
for (const candidate of candidates) {
|
|
731
|
+
const lifecycle = byTaskId.get(candidate.taskId);
|
|
732
|
+
const taskRef = lifecycle?.taskMapChildId;
|
|
733
|
+
if (!lifecycle || !taskRef) {
|
|
734
|
+
filteredCandidates.push({
|
|
735
|
+
taskId: candidate.taskId,
|
|
736
|
+
reasonCode: "candidate-identity-unavailable",
|
|
737
|
+
});
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
let taskDir;
|
|
741
|
+
try {
|
|
742
|
+
taskDir = approvedTask(root, taskRef, "implement");
|
|
743
|
+
approvalPassedTaskIds.push(candidate.taskId);
|
|
744
|
+
}
|
|
745
|
+
catch {
|
|
746
|
+
filteredCandidates.push({
|
|
747
|
+
taskId: candidate.taskId,
|
|
748
|
+
reasonCode: "approval-rejected",
|
|
749
|
+
});
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
try {
|
|
753
|
+
piWorkdir(root, taskDir, "implement");
|
|
754
|
+
worktreePassedTaskIds.push(candidate.taskId);
|
|
755
|
+
}
|
|
756
|
+
catch {
|
|
757
|
+
filteredCandidates.push({
|
|
758
|
+
taskId: candidate.taskId,
|
|
759
|
+
reasonCode: "worktree-rejected",
|
|
760
|
+
});
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
if (activeLeases === null) {
|
|
764
|
+
filteredCandidates.push({
|
|
765
|
+
taskId: candidate.taskId,
|
|
766
|
+
reasonCode: "lease-state-unavailable",
|
|
767
|
+
});
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
const conflictsWithActiveLease = activeLeases.some(({ lease }) => {
|
|
771
|
+
if (lifecycle.writeSet?.length === 0)
|
|
772
|
+
return false;
|
|
773
|
+
return projectWriteSetsConflict(lifecycle.writeSet ?? [], lease.touches);
|
|
774
|
+
});
|
|
775
|
+
if (conflictsWithActiveLease) {
|
|
776
|
+
filteredCandidates.push({
|
|
777
|
+
taskId: candidate.taskId,
|
|
778
|
+
reasonCode: "active-write-lease-conflict",
|
|
779
|
+
});
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
eligible.push(candidate);
|
|
783
|
+
}
|
|
784
|
+
return {
|
|
785
|
+
candidates: eligible,
|
|
786
|
+
filteredCandidates,
|
|
787
|
+
approvalPassedTaskIds,
|
|
788
|
+
worktreePassedTaskIds,
|
|
789
|
+
activeLeaseCheckAt,
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
function scheduleSnapshotFingerprint(snapshot) {
|
|
793
|
+
return fingerprintTaskValue({
|
|
794
|
+
taskMapFingerprint: snapshot.taskMapFingerprint,
|
|
795
|
+
request: snapshot.request,
|
|
796
|
+
lifecycle: snapshot.lifecycle,
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Plans a Parent graph with an optional Jev tie-break after deterministic,
|
|
801
|
+
* execution-approval, worktree, and active-lease checks. The API persists only
|
|
802
|
+
* one final immutable schedule receipt; P37 admission must still recheck it.
|
|
803
|
+
*/
|
|
804
|
+
export async function scheduleParentTaskGraphWithJevV1(rootValue, parentRef, options = {}, jev) {
|
|
805
|
+
const root = path.resolve(rootValue);
|
|
806
|
+
const policyAtScheduleStart = resolveJevProjectEgressPolicyV1(root);
|
|
807
|
+
const planningOptions = { ...options };
|
|
808
|
+
delete planningOptions.jevAdvice;
|
|
809
|
+
const previewSnapshot = buildPlanningSnapshot(root, parentRef, planningOptions);
|
|
810
|
+
const previewPlan = planTaskScheduleV1(previewSnapshot.request);
|
|
811
|
+
const structuralCandidates = firstCriticalPathCandidates(previewPlan);
|
|
812
|
+
let initialEligibility = {
|
|
813
|
+
candidates: [],
|
|
814
|
+
filteredCandidates: [],
|
|
815
|
+
approvalPassedTaskIds: [],
|
|
816
|
+
worktreePassedTaskIds: [],
|
|
817
|
+
activeLeaseCheckAt: null,
|
|
818
|
+
};
|
|
819
|
+
if (structuralCandidates.length > 0) {
|
|
820
|
+
initialEligibility = verifyJevScheduleCandidates(root, previewSnapshot, structuralCandidates);
|
|
821
|
+
}
|
|
822
|
+
const policyBeforeAdviceRequest = resolveJevProjectEgressPolicyV1(root);
|
|
823
|
+
const projectPolicyFallbackReasonCode = !policyAtScheduleStart.allowed
|
|
824
|
+
? policyAtScheduleStart.reasonCode
|
|
825
|
+
: !policyBeforeAdviceRequest.allowed
|
|
826
|
+
? policyBeforeAdviceRequest.reasonCode
|
|
827
|
+
: undefined;
|
|
828
|
+
const requested = await requestJevTaskScheduleAdviceV1({
|
|
829
|
+
candidates: initialEligibility.candidates,
|
|
830
|
+
filteredCandidates: initialEligibility.filteredCandidates,
|
|
831
|
+
eligibility: {
|
|
832
|
+
approvalPassedTaskIds: initialEligibility.approvalPassedTaskIds,
|
|
833
|
+
worktreePassedTaskIds: initialEligibility.worktreePassedTaskIds,
|
|
834
|
+
activeLeaseCheckAt: initialEligibility.activeLeaseCheckAt,
|
|
835
|
+
},
|
|
836
|
+
options: jev,
|
|
837
|
+
fallbackReasonCode: projectPolicyFallbackReasonCode,
|
|
838
|
+
});
|
|
839
|
+
const finalSnapshot = buildPlanningSnapshot(root, parentRef, planningOptions);
|
|
840
|
+
const finalStructuralCandidates = firstCriticalPathCandidates(planTaskScheduleV1(finalSnapshot.request));
|
|
841
|
+
const finalEligibility = verifyJevScheduleCandidates(root, finalSnapshot, finalStructuralCandidates);
|
|
842
|
+
const policyAfterAdviceResponse = resolveJevProjectEgressPolicyV1(root);
|
|
843
|
+
let finalRequest = finalSnapshot.request;
|
|
844
|
+
const advice = requested.advice;
|
|
845
|
+
const requestStateStable = scheduleSnapshotFingerprint(previewSnapshot) ===
|
|
846
|
+
scheduleSnapshotFingerprint(finalSnapshot) &&
|
|
847
|
+
sameTaskIds(structuralCandidates.map(({ taskId }) => taskId), finalStructuralCandidates.map(({ taskId }) => taskId)) &&
|
|
848
|
+
eligibilityFingerprint(initialEligibility.filteredCandidates, initialEligibility.candidates) ===
|
|
849
|
+
eligibilityFingerprint(finalEligibility.filteredCandidates, finalEligibility.candidates);
|
|
850
|
+
const adviceMatchesFinalCandidates = advice === null ||
|
|
851
|
+
sameTaskIdSet(advice.taskOrder, finalEligibility.candidates.map(({ taskId }) => taskId));
|
|
852
|
+
const eligibilityChanged = !requestStateStable || !adviceMatchesFinalCandidates;
|
|
853
|
+
let audit = finalizeJevTaskScheduleEligibilityV1(requested.audit, finalEligibility.candidates, finalEligibility.filteredCandidates, {
|
|
854
|
+
approvalPassedTaskIds: finalEligibility.approvalPassedTaskIds,
|
|
855
|
+
worktreePassedTaskIds: finalEligibility.worktreePassedTaskIds,
|
|
856
|
+
activeLeaseCheckAt: finalEligibility.activeLeaseCheckAt,
|
|
857
|
+
}, eligibilityChanged);
|
|
858
|
+
audit = {
|
|
859
|
+
...audit,
|
|
860
|
+
projectEgressPolicy: projectEgressAudit(policyAtScheduleStart, policyBeforeAdviceRequest, policyAfterAdviceResponse),
|
|
861
|
+
};
|
|
862
|
+
if (advice) {
|
|
863
|
+
if (!eligibilityChanged && policyAfterAdviceResponse.allowed) {
|
|
864
|
+
finalRequest = { ...finalSnapshot.request, jevAdvice: advice };
|
|
865
|
+
audit = finalizeJevTaskScheduleAdviceV1(audit, planTaskScheduleV1(finalRequest));
|
|
866
|
+
}
|
|
867
|
+
else {
|
|
868
|
+
audit = supersedeJevTaskScheduleAdviceV1(audit);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
if (!policyAfterAdviceResponse.allowed) {
|
|
872
|
+
audit = applyJevTaskScheduleEgressFallbackV1(audit, policyAfterAdviceResponse.reasonCode);
|
|
873
|
+
}
|
|
874
|
+
const plan = planTaskScheduleV1(finalRequest);
|
|
875
|
+
const receiptBase = {
|
|
876
|
+
parentTaskDir: relativeTaskDir(root, finalSnapshot.parentDir),
|
|
877
|
+
taskMapFingerprint: finalSnapshot.taskMapFingerprint,
|
|
878
|
+
request: finalRequest,
|
|
879
|
+
plan,
|
|
880
|
+
lifecycle: finalSnapshot.lifecycle,
|
|
881
|
+
jevAdviceAudit: audit,
|
|
882
|
+
};
|
|
883
|
+
// Jev confidence maps have a null prototype. Hash the parsed JSON value
|
|
884
|
+
// that dispatch will read, so its receipt fingerprint verifies after storage.
|
|
885
|
+
const stored = persistReceipt(finalSnapshot.parentDir, JSON.parse(JSON.stringify(receiptBase)));
|
|
886
|
+
return { ...stored, receiptFile: relativeTaskDir(root, stored.receiptFile) };
|
|
887
|
+
}
|
|
888
|
+
function buildTaskKernelGraphSnapshot(rootValue, candidateTaskIds, options) {
|
|
889
|
+
const root = path.resolve(rootValue);
|
|
890
|
+
if (!candidateTaskIds.length)
|
|
891
|
+
throw new Error("At least one V2 Task candidate is required");
|
|
892
|
+
const candidateSet = new Set(candidateTaskIds);
|
|
893
|
+
if (candidateSet.size !== candidateTaskIds.length)
|
|
894
|
+
throw new Error("candidateTaskIds contains duplicates");
|
|
895
|
+
const { byId, byArchivedId } = indexTaskKernelGraphTasks(root);
|
|
896
|
+
const unknownCandidates = [...candidateSet].filter((taskId) => !byId.has(taskId));
|
|
897
|
+
if (unknownCandidates.length)
|
|
898
|
+
throw new Error(`Unknown Task candidates: ${unknownCandidates.sort(compareText).join(", ")}`);
|
|
899
|
+
for (const taskId of candidateSet) {
|
|
900
|
+
const indexed = byId.get(taskId);
|
|
901
|
+
if (indexed && isReconciliationIndexedTask(indexed))
|
|
902
|
+
throw reconciliationFailure(indexed, "candidate");
|
|
903
|
+
if (!indexed?.kernel)
|
|
904
|
+
throw new Error(`Task candidate must use Task Kernel v2: ${taskId}`);
|
|
905
|
+
}
|
|
906
|
+
const graphNodes = new Map();
|
|
907
|
+
const visit = (taskId, external, visiting) => {
|
|
908
|
+
if (graphNodes.has(taskId))
|
|
909
|
+
return;
|
|
910
|
+
if (visiting.has(taskId))
|
|
911
|
+
return;
|
|
912
|
+
const activeIndexed = byId.get(taskId);
|
|
913
|
+
const archivedIndexed = byArchivedId.get(taskId);
|
|
914
|
+
const indexed = activeIndexed ?? archivedIndexed;
|
|
915
|
+
if (!indexed)
|
|
916
|
+
throw new Error(`Missing hard Task dependency: ${taskId}`);
|
|
917
|
+
if (isReconciliationIndexedTask(indexed))
|
|
918
|
+
throw reconciliationFailure(indexed, "dependency");
|
|
919
|
+
if (!indexed.kernel)
|
|
920
|
+
throw new Error(`Hard Task dependency must use Task Kernel v2: ${taskId}`);
|
|
921
|
+
const kernel = indexed.kernel;
|
|
922
|
+
const completed = projectTaskKernelLifecycle(kernel).closed &&
|
|
923
|
+
projectTaskKernelLifecycle(kernel).outcome === "completed";
|
|
924
|
+
if (archivedIndexed && !completed)
|
|
925
|
+
throw new Error(`Archived hard Task dependency is not closed with completed outcome: ${taskId}`);
|
|
926
|
+
const dependencies = completed ? [] : [...kernel.definition.dependencies];
|
|
927
|
+
graphNodes.set(taskId, { indexed, dependencies, external });
|
|
928
|
+
const nextVisiting = new Set(visiting);
|
|
929
|
+
nextVisiting.add(taskId);
|
|
930
|
+
for (const dependencyId of dependencies) {
|
|
931
|
+
visit(dependencyId, !candidateSet.has(dependencyId), nextVisiting);
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
for (const taskId of [...candidateSet].sort(compareText))
|
|
935
|
+
visit(taskId, false, new Set());
|
|
936
|
+
const overrides = options.estimatedCosts ?? {};
|
|
937
|
+
const unknownOverrides = Object.keys(overrides).filter((taskId) => !candidateSet.has(taskId));
|
|
938
|
+
if (unknownOverrides.length)
|
|
939
|
+
throw new Error(`Cost estimates reference non-candidate Task IDs: ${unknownOverrides.sort(compareText).join(", ")}`);
|
|
940
|
+
const lifecycle = [];
|
|
941
|
+
const tasks = [];
|
|
942
|
+
const taskKernelRevisions = {};
|
|
943
|
+
for (const [taskId, graphNode] of [...graphNodes.entries()].sort(([left], [right]) => compareText(left, right))) {
|
|
944
|
+
const kernel = graphNode.indexed.kernel;
|
|
945
|
+
if (!kernel)
|
|
946
|
+
throw new Error(`Hard Task dependency must use Task Kernel v2: ${taskId}`);
|
|
947
|
+
const isClosed = projectTaskKernelLifecycle(kernel).closed &&
|
|
948
|
+
projectTaskKernelLifecycle(kernel).outcome === "completed";
|
|
949
|
+
const currentState = stateForTaskKernelCandidate(root, taskId, kernel);
|
|
950
|
+
const schedulerState = graphNode.external && !isClosed
|
|
951
|
+
? currentState === "failed"
|
|
952
|
+
? "failed"
|
|
953
|
+
: "blocked"
|
|
954
|
+
: currentState;
|
|
955
|
+
const notes = graphNode.external && !isClosed
|
|
956
|
+
? ["external-hard-dependency-not-closed"]
|
|
957
|
+
: [];
|
|
958
|
+
const projected = toSnapshot(root, graphNode.indexed, null, "", graphNode.external ? undefined : overrides[taskId], graphNode.dependencies, notes);
|
|
959
|
+
projected.schedulerState = schedulerState;
|
|
960
|
+
if (!graphNode.external &&
|
|
961
|
+
!kernel.runs.length &&
|
|
962
|
+
schedulerState === "waiting")
|
|
963
|
+
projected.projectionNotes.push("task-ready-no-run-yet");
|
|
964
|
+
lifecycle.push(projected);
|
|
965
|
+
taskKernelRevisions[taskId] = kernel.revision;
|
|
966
|
+
tasks.push({
|
|
967
|
+
taskId,
|
|
968
|
+
dependsOn: graphNode.dependencies,
|
|
969
|
+
state: schedulerState,
|
|
970
|
+
writeSet: graphNode.external ? null : projected.writeSet,
|
|
971
|
+
estimatedCosts: graphNode.external
|
|
972
|
+
? emptyCosts()
|
|
973
|
+
: projected.estimatedCosts,
|
|
974
|
+
observedCosts: graphNode.external
|
|
975
|
+
? {}
|
|
976
|
+
: definedObservedCosts(projected.observedCosts),
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
return {
|
|
980
|
+
candidateTaskIds: [...candidateSet].sort(compareText),
|
|
981
|
+
request: {
|
|
982
|
+
schemaVersion: 1,
|
|
983
|
+
tasks,
|
|
984
|
+
...(options.conflictParallelizations
|
|
985
|
+
? { conflictParallelizations: options.conflictParallelizations }
|
|
986
|
+
: {}),
|
|
987
|
+
...(options.jevAdvice ? { jevAdvice: options.jevAdvice } : {}),
|
|
988
|
+
},
|
|
989
|
+
lifecycle,
|
|
990
|
+
taskKernelRevisions,
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
function verifyJevTaskKernelScheduleCandidates(root, snapshot, candidates) {
|
|
994
|
+
let activeLeases = null;
|
|
995
|
+
let activeLeaseCheckAt = null;
|
|
996
|
+
try {
|
|
997
|
+
activeLeases = withProjectSchedulerMutex(root, () => listProjectWriteLeases(root));
|
|
998
|
+
activeLeaseCheckAt = new Date().toISOString();
|
|
999
|
+
}
|
|
1000
|
+
catch {
|
|
1001
|
+
// An unreadable lease set is not safe input for an external advice request.
|
|
1002
|
+
}
|
|
1003
|
+
const byTaskId = new Map(snapshot.lifecycle.map((item) => [item.taskId, item]));
|
|
1004
|
+
const eligible = [];
|
|
1005
|
+
const filteredCandidates = [];
|
|
1006
|
+
const approvalPassedTaskIds = [];
|
|
1007
|
+
const worktreePassedTaskIds = [];
|
|
1008
|
+
for (const candidate of candidates) {
|
|
1009
|
+
const lifecycle = byTaskId.get(candidate.taskId);
|
|
1010
|
+
if (lifecycle?.sourceKind !== "task-kernel-v2") {
|
|
1011
|
+
filteredCandidates.push({
|
|
1012
|
+
taskId: candidate.taskId,
|
|
1013
|
+
reasonCode: "candidate-identity-unavailable",
|
|
1014
|
+
});
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
let kernel;
|
|
1018
|
+
try {
|
|
1019
|
+
const read = readTaskKernel({
|
|
1020
|
+
root,
|
|
1021
|
+
taskDir: resolveTaskDir(root, lifecycle.taskDir),
|
|
1022
|
+
});
|
|
1023
|
+
if (read.kind !== "task-kernel-v2" ||
|
|
1024
|
+
read.kernel.identity.taskId !== candidate.taskId) {
|
|
1025
|
+
filteredCandidates.push({
|
|
1026
|
+
taskId: candidate.taskId,
|
|
1027
|
+
reasonCode: "candidate-identity-unavailable",
|
|
1028
|
+
});
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
kernel = read.kernel;
|
|
1032
|
+
}
|
|
1033
|
+
catch {
|
|
1034
|
+
filteredCandidates.push({
|
|
1035
|
+
taskId: candidate.taskId,
|
|
1036
|
+
reasonCode: "candidate-identity-unavailable",
|
|
1037
|
+
});
|
|
1038
|
+
continue;
|
|
1039
|
+
}
|
|
1040
|
+
if (kernel.revision !== snapshot.taskKernelRevisions[candidate.taskId] ||
|
|
1041
|
+
kernel.revision !== lifecycle.kernelRevision) {
|
|
1042
|
+
filteredCandidates.push({
|
|
1043
|
+
taskId: candidate.taskId,
|
|
1044
|
+
reasonCode: "task-kernel-revision-changed",
|
|
1045
|
+
});
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
1048
|
+
const run = kernel.runs.at(-1) ?? null;
|
|
1049
|
+
const projected = projectTaskKernelLifecycle(kernel);
|
|
1050
|
+
if (run === null) {
|
|
1051
|
+
filteredCandidates.push({
|
|
1052
|
+
taskId: candidate.taskId,
|
|
1053
|
+
reasonCode: "run-not-eligible",
|
|
1054
|
+
});
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
1057
|
+
if (run.id !== lifecycle.runId ||
|
|
1058
|
+
run.taskId !== candidate.taskId ||
|
|
1059
|
+
run.state !== "waiting" ||
|
|
1060
|
+
!!run.host ||
|
|
1061
|
+
!!run.candidateSnapshot ||
|
|
1062
|
+
!projected.gateSnapshot.runStart.phaseAllowsRun ||
|
|
1063
|
+
projected.gateSnapshot.runStart.activeRunId !== run.id) {
|
|
1064
|
+
filteredCandidates.push({
|
|
1065
|
+
taskId: candidate.taskId,
|
|
1066
|
+
reasonCode: "run-not-eligible",
|
|
1067
|
+
});
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
const authorization = run.authorization;
|
|
1071
|
+
if (typeof authorization?.approvedBy !== "string" ||
|
|
1072
|
+
!authorization.approvedBy.trim() ||
|
|
1073
|
+
typeof authorization.approvedAt !== "string" ||
|
|
1074
|
+
!Number.isFinite(Date.parse(authorization.approvedAt)) ||
|
|
1075
|
+
typeof authorization.scope !== "string" ||
|
|
1076
|
+
!authorization.scope.trim() ||
|
|
1077
|
+
typeof authorization.evidenceRef !== "string" ||
|
|
1078
|
+
!authorization.evidenceRef.trim()) {
|
|
1079
|
+
filteredCandidates.push({
|
|
1080
|
+
taskId: candidate.taskId,
|
|
1081
|
+
reasonCode: "approval-rejected",
|
|
1082
|
+
});
|
|
1083
|
+
continue;
|
|
1084
|
+
}
|
|
1085
|
+
approvalPassedTaskIds.push(candidate.taskId);
|
|
1086
|
+
if (lifecycle.writeSet === null ||
|
|
1087
|
+
!run.writeSetSnapshot.length ||
|
|
1088
|
+
!run.workspace?.manager ||
|
|
1089
|
+
run.workspace.ownerRunId !== run.id) {
|
|
1090
|
+
filteredCandidates.push({
|
|
1091
|
+
taskId: candidate.taskId,
|
|
1092
|
+
reasonCode: lifecycle.writeSet === null || !run.writeSetSnapshot.length
|
|
1093
|
+
? "write-set-unavailable"
|
|
1094
|
+
: "worktree-rejected",
|
|
1095
|
+
});
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
if (!sameTaskIds([...run.writeSetSnapshot].sort(compareText), [...run.workspace.writeSet].sort(compareText))) {
|
|
1099
|
+
filteredCandidates.push({
|
|
1100
|
+
taskId: candidate.taskId,
|
|
1101
|
+
reasonCode: "worktree-rejected",
|
|
1102
|
+
});
|
|
1103
|
+
continue;
|
|
1104
|
+
}
|
|
1105
|
+
let inspection;
|
|
1106
|
+
try {
|
|
1107
|
+
inspection = inspectRunWorktree({
|
|
1108
|
+
repoRoot: root,
|
|
1109
|
+
runId: run.id,
|
|
1110
|
+
runState: run.state,
|
|
1111
|
+
binding: run.workspace,
|
|
1112
|
+
knownOwners: [],
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
catch {
|
|
1116
|
+
filteredCandidates.push({
|
|
1117
|
+
taskId: candidate.taskId,
|
|
1118
|
+
reasonCode: "worktree-rejected",
|
|
1119
|
+
});
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
if (inspection.issues.some((issue) => issue !== "unintegrated") ||
|
|
1123
|
+
inspection.state !== "unintegrated" ||
|
|
1124
|
+
inspection.dirty ||
|
|
1125
|
+
inspection.headSha?.toLowerCase() !== run.workspace.baseSha.toLowerCase() ||
|
|
1126
|
+
!inspection.actualPath) {
|
|
1127
|
+
filteredCandidates.push({
|
|
1128
|
+
taskId: candidate.taskId,
|
|
1129
|
+
reasonCode: "worktree-rejected",
|
|
1130
|
+
});
|
|
1131
|
+
continue;
|
|
1132
|
+
}
|
|
1133
|
+
worktreePassedTaskIds.push(candidate.taskId);
|
|
1134
|
+
if (activeLeases === null) {
|
|
1135
|
+
filteredCandidates.push({
|
|
1136
|
+
taskId: candidate.taskId,
|
|
1137
|
+
reasonCode: "lease-state-unavailable",
|
|
1138
|
+
});
|
|
1139
|
+
continue;
|
|
1140
|
+
}
|
|
1141
|
+
if (activeLeases.some(({ lease }) => projectWriteSetsConflict(lifecycle.writeSet ?? [], lease.touches))) {
|
|
1142
|
+
filteredCandidates.push({
|
|
1143
|
+
taskId: candidate.taskId,
|
|
1144
|
+
reasonCode: "active-write-lease-conflict",
|
|
1145
|
+
});
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1148
|
+
eligible.push(candidate);
|
|
1149
|
+
}
|
|
1150
|
+
return {
|
|
1151
|
+
candidates: eligible,
|
|
1152
|
+
filteredCandidates,
|
|
1153
|
+
approvalPassedTaskIds,
|
|
1154
|
+
worktreePassedTaskIds,
|
|
1155
|
+
activeLeaseCheckAt,
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
function taskKernelScheduleSnapshotFingerprint(snapshot) {
|
|
1159
|
+
return fingerprintTaskValue({
|
|
1160
|
+
candidateTaskIds: snapshot.candidateTaskIds,
|
|
1161
|
+
taskKernelRevisions: snapshot.taskKernelRevisions,
|
|
1162
|
+
request: snapshot.request,
|
|
1163
|
+
lifecycle: snapshot.lifecycle,
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
function taskKernelEligibilityFingerprint(eligibility) {
|
|
1167
|
+
return fingerprintTaskValue({
|
|
1168
|
+
candidateTaskIds: eligibility.candidates.map(({ taskId }) => taskId),
|
|
1169
|
+
filteredCandidates: eligibility.filteredCandidates.map(({ taskId, reasonCode }) => ({ taskId, reasonCode })),
|
|
1170
|
+
approvalPassedTaskIds: eligibility.approvalPassedTaskIds,
|
|
1171
|
+
worktreePassedTaskIds: eligibility.worktreePassedTaskIds,
|
|
1172
|
+
leaseStateAvailable: eligibility.activeLeaseCheckAt !== null,
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
function projectEgressStatus(policy) {
|
|
1176
|
+
return policy.allowed ? "allowed" : policy.reasonCode;
|
|
1177
|
+
}
|
|
1178
|
+
function projectEgressAudit(atScheduleStart, beforeAdviceRequest, afterAdviceResponse) {
|
|
1179
|
+
const startStatus = projectEgressStatus(atScheduleStart);
|
|
1180
|
+
const beforeStatus = projectEgressStatus(beforeAdviceRequest);
|
|
1181
|
+
const afterStatus = projectEgressStatus(afterAdviceResponse);
|
|
1182
|
+
return {
|
|
1183
|
+
atScheduleStart: startStatus,
|
|
1184
|
+
beforeAdviceRequest: beforeStatus,
|
|
1185
|
+
afterAdviceResponse: afterStatus,
|
|
1186
|
+
changed: startStatus !== beforeStatus || startStatus !== afterStatus,
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Plans a Task Kernel v2 dependency graph directly. It does not read or create
|
|
1191
|
+
* a Parent Task Map and never mutates Task or Run state.
|
|
1192
|
+
*/
|
|
1193
|
+
export function planTaskKernelGraphV1(root, candidateTaskIds, options = {}) {
|
|
1194
|
+
const snapshot = buildTaskKernelGraphSnapshot(root, candidateTaskIds, options);
|
|
1195
|
+
return { ...snapshot, plan: planTaskScheduleV1(snapshot.request) };
|
|
1196
|
+
}
|
|
1197
|
+
function taskKernelPersistedValueFingerprint(value) {
|
|
1198
|
+
// Fingerprint the same JSON value that is persisted and later read back.
|
|
1199
|
+
// Null-prototype records are valid receipt values, but the generic task
|
|
1200
|
+
// fingerprint preserves their insertion order unlike parsed JSON records.
|
|
1201
|
+
const serialized = JSON.stringify(value);
|
|
1202
|
+
if (!serialized)
|
|
1203
|
+
throw new Error("Task Kernel schedule receipt is not JSON serializable");
|
|
1204
|
+
return fingerprintTaskValue(JSON.parse(serialized));
|
|
1205
|
+
}
|
|
1206
|
+
function taskKernelReceiptFingerprint(receipt) {
|
|
1207
|
+
// Jev confidence maps are null-prototype records, so normalize the envelope
|
|
1208
|
+
// before hashing both new receipt fingerprints and request identities.
|
|
1209
|
+
return taskKernelPersistedValueFingerprint(receipt);
|
|
1210
|
+
}
|
|
1211
|
+
function legacyTaskKernelReceiptFingerprint(receipt) {
|
|
1212
|
+
const audit = receipt.jevAdviceAudit;
|
|
1213
|
+
if (!audit)
|
|
1214
|
+
return null;
|
|
1215
|
+
const confidence = audit.transport.confidence;
|
|
1216
|
+
if (!confidence ||
|
|
1217
|
+
typeof confidence !== "object" ||
|
|
1218
|
+
Array.isArray(confidence))
|
|
1219
|
+
return null;
|
|
1220
|
+
// Older v2 receipts hashed the in-memory null-prototype confidence map.
|
|
1221
|
+
// Recreate that one JSON field so those already-written receipts remain
|
|
1222
|
+
// verifiable after parsing without mutating the returned receipt object.
|
|
1223
|
+
const legacyConfidence = Object.assign(Object.create(null), confidence);
|
|
1224
|
+
return fingerprintTaskValue({
|
|
1225
|
+
...receipt,
|
|
1226
|
+
jevAdviceAudit: {
|
|
1227
|
+
...audit,
|
|
1228
|
+
transport: {
|
|
1229
|
+
...audit.transport,
|
|
1230
|
+
confidence: legacyConfidence,
|
|
1231
|
+
},
|
|
1232
|
+
},
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
function taskKernelScheduleRequestFingerprint(receipt) {
|
|
1236
|
+
const { schemaVersion: _schemaVersion, scope: _scope, receiptFingerprint: _receiptFingerprint, createdAt: _createdAt, integrityVersion: _integrityVersion, scheduleKey: _scheduleKey, ...request } = receipt;
|
|
1237
|
+
return taskKernelPersistedValueFingerprint(request);
|
|
1238
|
+
}
|
|
1239
|
+
function readExistingTaskKernelReceipt(file, fingerprint) {
|
|
1240
|
+
const value = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
1241
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
1242
|
+
throw new Error(`Invalid Task Kernel schedule receipt: ${file}`);
|
|
1243
|
+
const receipt = value;
|
|
1244
|
+
const { schemaVersion, scope, receiptFingerprint: storedFingerprint, createdAt, integrityVersion, ...legacyBase } = receipt;
|
|
1245
|
+
const { receiptFingerprint: _excludedFingerprint, ...fullEnvelope } = receipt;
|
|
1246
|
+
const legacyFingerprint = integrityVersion === undefined && fingerprintTaskValue(legacyBase) === fingerprint;
|
|
1247
|
+
const fullFingerprint = integrityVersion === 2 &&
|
|
1248
|
+
typeof createdAt === "string" &&
|
|
1249
|
+
(taskKernelReceiptFingerprint(fullEnvelope) === fingerprint ||
|
|
1250
|
+
legacyTaskKernelReceiptFingerprint(fullEnvelope) === fingerprint);
|
|
1251
|
+
if (schemaVersion !== RECEIPT_SCHEMA_VERSION ||
|
|
1252
|
+
scope !== "task-kernel-v2" ||
|
|
1253
|
+
storedFingerprint !== fingerprint ||
|
|
1254
|
+
typeof createdAt !== "string" ||
|
|
1255
|
+
(!fullFingerprint && !legacyFingerprint)) {
|
|
1256
|
+
throw new Error(`Task Kernel schedule receipt fingerprint does not match its contents: ${file}`);
|
|
1257
|
+
}
|
|
1258
|
+
return receipt;
|
|
1259
|
+
}
|
|
1260
|
+
/** Reads a V2 schedule receipt and reports when a legacy digest excludes createdAt. */
|
|
1261
|
+
export function readTaskKernelScheduleReceiptV1(rootValue, fingerprint) {
|
|
1262
|
+
if (!/^[a-f0-9]{64}$/u.test(fingerprint))
|
|
1263
|
+
throw new Error("Task Kernel schedule receipt fingerprint must be a lowercase SHA-256 digest");
|
|
1264
|
+
const file = path.join(path.resolve(rootValue), ".pactile", ".runtime", "scheduler", "receipts", `${fingerprint}.json`);
|
|
1265
|
+
if (!fs.existsSync(file))
|
|
1266
|
+
throw new Error(`Task Kernel schedule receipt not found: ${fingerprint}`);
|
|
1267
|
+
const receipt = readExistingTaskKernelReceipt(file, fingerprint);
|
|
1268
|
+
return {
|
|
1269
|
+
receipt,
|
|
1270
|
+
integrity: receipt.integrityVersion === 2
|
|
1271
|
+
? "fingerprint-verified"
|
|
1272
|
+
: "legacy-fingerprint-excludes-createdAt",
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
function persistTaskKernelReceipt(root, receiptBase) {
|
|
1276
|
+
const requestFingerprint = taskKernelPersistedValueFingerprint(receiptBase);
|
|
1277
|
+
const legacyRequestFingerprint = fingerprintTaskValue(receiptBase);
|
|
1278
|
+
const requestFingerprints = new Set([
|
|
1279
|
+
requestFingerprint,
|
|
1280
|
+
legacyRequestFingerprint,
|
|
1281
|
+
]);
|
|
1282
|
+
const folder = path.join(root, ".pactile", ".runtime", "scheduler", "receipts");
|
|
1283
|
+
return withProjectSchedulerMutex(root, () => {
|
|
1284
|
+
fs.mkdirSync(folder, { recursive: true });
|
|
1285
|
+
for (const fingerprint of requestFingerprints) {
|
|
1286
|
+
const legacyFile = path.join(folder, `${fingerprint}.json`);
|
|
1287
|
+
if (!fs.existsSync(legacyFile))
|
|
1288
|
+
continue;
|
|
1289
|
+
const receipt = readExistingTaskKernelReceipt(legacyFile, fingerprint);
|
|
1290
|
+
if (taskKernelScheduleRequestFingerprint(receipt) !== requestFingerprint)
|
|
1291
|
+
throw new Error(`Task Kernel schedule receipt request does not match its content: ${legacyFile}`);
|
|
1292
|
+
return { receipt, receiptFile: legacyFile, created: false };
|
|
1293
|
+
}
|
|
1294
|
+
for (const entry of fs.readdirSync(folder, { withFileTypes: true })) {
|
|
1295
|
+
if (!entry.isFile() || !entry.name.endsWith(".json"))
|
|
1296
|
+
continue;
|
|
1297
|
+
const fingerprint = entry.name.slice(0, -".json".length);
|
|
1298
|
+
if (!/^[a-f0-9]{64}$/u.test(fingerprint))
|
|
1299
|
+
continue;
|
|
1300
|
+
const file = path.join(folder, entry.name);
|
|
1301
|
+
let value;
|
|
1302
|
+
try {
|
|
1303
|
+
value = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
1304
|
+
}
|
|
1305
|
+
catch {
|
|
1306
|
+
continue;
|
|
1307
|
+
}
|
|
1308
|
+
if (!value ||
|
|
1309
|
+
typeof value !== "object" ||
|
|
1310
|
+
Array.isArray(value) ||
|
|
1311
|
+
!requestFingerprints.has(value.scheduleKey ?? ""))
|
|
1312
|
+
continue;
|
|
1313
|
+
const receipt = readExistingTaskKernelReceipt(file, fingerprint);
|
|
1314
|
+
if (taskKernelScheduleRequestFingerprint(receipt) !== requestFingerprint)
|
|
1315
|
+
throw new Error(`Task Kernel schedule receipt request does not match its content: ${file}`);
|
|
1316
|
+
if (receipt.integrityVersion === 2)
|
|
1317
|
+
return { receipt, receiptFile: file, created: false };
|
|
1318
|
+
}
|
|
1319
|
+
const unsignedReceipt = {
|
|
1320
|
+
...receiptBase,
|
|
1321
|
+
schemaVersion: RECEIPT_SCHEMA_VERSION,
|
|
1322
|
+
scope: "task-kernel-v2",
|
|
1323
|
+
createdAt: new Date().toISOString(),
|
|
1324
|
+
integrityVersion: 2,
|
|
1325
|
+
scheduleKey: requestFingerprint,
|
|
1326
|
+
};
|
|
1327
|
+
const fingerprint = taskKernelReceiptFingerprint(unsignedReceipt);
|
|
1328
|
+
const file = path.join(folder, `${fingerprint}.json`);
|
|
1329
|
+
const receipt = {
|
|
1330
|
+
...unsignedReceipt,
|
|
1331
|
+
receiptFingerprint: fingerprint,
|
|
1332
|
+
};
|
|
1333
|
+
const temporary = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
1334
|
+
try {
|
|
1335
|
+
fs.writeFileSync(temporary, `${JSON.stringify(receipt, null, 2)}\n`, {
|
|
1336
|
+
flag: "wx",
|
|
1337
|
+
mode: 0o600,
|
|
1338
|
+
});
|
|
1339
|
+
try {
|
|
1340
|
+
fs.copyFileSync(temporary, file, fs.constants.COPYFILE_EXCL);
|
|
1341
|
+
return { receipt, receiptFile: file, created: true };
|
|
1342
|
+
}
|
|
1343
|
+
catch (error) {
|
|
1344
|
+
if (error.code !== "EEXIST")
|
|
1345
|
+
throw error;
|
|
1346
|
+
return {
|
|
1347
|
+
receipt: readExistingTaskKernelReceipt(file, fingerprint),
|
|
1348
|
+
receiptFile: file,
|
|
1349
|
+
created: false,
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
finally {
|
|
1354
|
+
fs.rmSync(temporary, { force: true });
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
/** Stores an idempotent Task/Run DAG decision receipt under project runtime state. */
|
|
1359
|
+
export function scheduleTaskKernelGraph(rootValue, candidateTaskIds, options = {}) {
|
|
1360
|
+
const root = path.resolve(rootValue);
|
|
1361
|
+
const snapshot = buildTaskKernelGraphSnapshot(root, candidateTaskIds, options);
|
|
1362
|
+
const receiptBase = {
|
|
1363
|
+
candidateTaskIds: snapshot.candidateTaskIds,
|
|
1364
|
+
taskKernelRevisions: snapshot.taskKernelRevisions,
|
|
1365
|
+
request: snapshot.request,
|
|
1366
|
+
plan: planTaskScheduleV1(snapshot.request),
|
|
1367
|
+
lifecycle: snapshot.lifecycle,
|
|
1368
|
+
};
|
|
1369
|
+
const stored = persistTaskKernelReceipt(root, receiptBase);
|
|
1370
|
+
return { ...stored, receiptFile: relativeTaskDir(root, stored.receiptFile) };
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Plans a V2 Task graph with optional Jev advice after the deterministic first
|
|
1374
|
+
* wave, approved waiting Run, manager-owned worktree, write-set, and lease gates.
|
|
1375
|
+
* The advice can only reorder the bounded equal-critical-path candidate set.
|
|
1376
|
+
*/
|
|
1377
|
+
export async function scheduleTaskKernelGraphWithJevV1(rootValue, candidateTaskIds, options = {}, jev, fallbackReasonCode) {
|
|
1378
|
+
const root = path.resolve(rootValue);
|
|
1379
|
+
const policyAtScheduleStart = resolveJevProjectEgressPolicyV1(root);
|
|
1380
|
+
const planningOptions = { ...options };
|
|
1381
|
+
delete planningOptions.jevAdvice;
|
|
1382
|
+
const previewSnapshot = buildTaskKernelGraphSnapshot(root, candidateTaskIds, planningOptions);
|
|
1383
|
+
const structuralCandidates = firstCriticalPathCandidates(planTaskScheduleV1(previewSnapshot.request));
|
|
1384
|
+
const initialEligibility = verifyJevTaskKernelScheduleCandidates(root, previewSnapshot, structuralCandidates);
|
|
1385
|
+
const policyBeforeAdviceRequest = resolveJevProjectEgressPolicyV1(root);
|
|
1386
|
+
const projectPolicyFallbackReasonCode = !policyAtScheduleStart.allowed
|
|
1387
|
+
? policyAtScheduleStart.reasonCode
|
|
1388
|
+
: !policyBeforeAdviceRequest.allowed
|
|
1389
|
+
? policyBeforeAdviceRequest.reasonCode
|
|
1390
|
+
: undefined;
|
|
1391
|
+
const requested = await requestJevTaskScheduleAdviceV1({
|
|
1392
|
+
candidates: initialEligibility.candidates,
|
|
1393
|
+
filteredCandidates: initialEligibility.filteredCandidates,
|
|
1394
|
+
eligibility: {
|
|
1395
|
+
approvalPassedTaskIds: initialEligibility.approvalPassedTaskIds,
|
|
1396
|
+
worktreePassedTaskIds: initialEligibility.worktreePassedTaskIds,
|
|
1397
|
+
activeLeaseCheckAt: initialEligibility.activeLeaseCheckAt,
|
|
1398
|
+
},
|
|
1399
|
+
options: jev,
|
|
1400
|
+
fallbackReasonCode: projectPolicyFallbackReasonCode ?? fallbackReasonCode,
|
|
1401
|
+
});
|
|
1402
|
+
const finalSnapshot = buildTaskKernelGraphSnapshot(root, candidateTaskIds, planningOptions);
|
|
1403
|
+
const finalStructuralCandidates = firstCriticalPathCandidates(planTaskScheduleV1(finalSnapshot.request));
|
|
1404
|
+
const finalEligibility = verifyJevTaskKernelScheduleCandidates(root, finalSnapshot, finalStructuralCandidates);
|
|
1405
|
+
const policyAfterAdviceResponse = resolveJevProjectEgressPolicyV1(root);
|
|
1406
|
+
let finalRequest = finalSnapshot.request;
|
|
1407
|
+
const advice = requested.advice;
|
|
1408
|
+
const requestStateStable = taskKernelScheduleSnapshotFingerprint(previewSnapshot) ===
|
|
1409
|
+
taskKernelScheduleSnapshotFingerprint(finalSnapshot) &&
|
|
1410
|
+
sameTaskIds(structuralCandidates.map(({ taskId }) => taskId), finalStructuralCandidates.map(({ taskId }) => taskId)) &&
|
|
1411
|
+
taskKernelEligibilityFingerprint(initialEligibility) ===
|
|
1412
|
+
taskKernelEligibilityFingerprint(finalEligibility);
|
|
1413
|
+
const adviceMatchesFinalCandidates = advice === null ||
|
|
1414
|
+
sameTaskIdSet(advice.taskOrder, finalEligibility.candidates.map(({ taskId }) => taskId));
|
|
1415
|
+
const eligibilityChanged = !requestStateStable || !adviceMatchesFinalCandidates;
|
|
1416
|
+
let audit = finalizeJevTaskScheduleEligibilityV1(requested.audit, finalEligibility.candidates, finalEligibility.filteredCandidates, {
|
|
1417
|
+
approvalPassedTaskIds: finalEligibility.approvalPassedTaskIds,
|
|
1418
|
+
worktreePassedTaskIds: finalEligibility.worktreePassedTaskIds,
|
|
1419
|
+
activeLeaseCheckAt: finalEligibility.activeLeaseCheckAt,
|
|
1420
|
+
}, eligibilityChanged);
|
|
1421
|
+
audit = {
|
|
1422
|
+
...audit,
|
|
1423
|
+
projectEgressPolicy: projectEgressAudit(policyAtScheduleStart, policyBeforeAdviceRequest, policyAfterAdviceResponse),
|
|
1424
|
+
};
|
|
1425
|
+
if (advice) {
|
|
1426
|
+
if (!eligibilityChanged && policyAfterAdviceResponse.allowed) {
|
|
1427
|
+
finalRequest = { ...finalSnapshot.request, jevAdvice: advice };
|
|
1428
|
+
audit = finalizeJevTaskScheduleAdviceV1(audit, planTaskScheduleV1(finalRequest));
|
|
1429
|
+
}
|
|
1430
|
+
else {
|
|
1431
|
+
audit = supersedeJevTaskScheduleAdviceV1(audit);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
if (!policyAfterAdviceResponse.allowed) {
|
|
1435
|
+
audit = applyJevTaskScheduleEgressFallbackV1(audit, policyAfterAdviceResponse.reasonCode);
|
|
1436
|
+
}
|
|
1437
|
+
const receiptBase = {
|
|
1438
|
+
candidateTaskIds: finalSnapshot.candidateTaskIds,
|
|
1439
|
+
taskKernelRevisions: finalSnapshot.taskKernelRevisions,
|
|
1440
|
+
request: finalRequest,
|
|
1441
|
+
plan: planTaskScheduleV1(finalRequest),
|
|
1442
|
+
lifecycle: finalSnapshot.lifecycle,
|
|
1443
|
+
jevAdviceAudit: audit,
|
|
1444
|
+
};
|
|
1445
|
+
const stored = persistTaskKernelReceipt(root, receiptBase);
|
|
1446
|
+
return { ...stored, receiptFile: relativeTaskDir(root, stored.receiptFile) };
|
|
1447
|
+
}
|
|
1448
|
+
//# sourceMappingURL=task-map-scheduler.js.map
|