@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
package/dist/commands/task.js
CHANGED
|
@@ -1,27 +1,54 @@
|
|
|
1
1
|
import { execFileSync, execSync } from "node:child_process";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { addTaskDependency, applyKernelArchive, applyKernelPatch, applyKernelRecordGate, applyKernelStart, assertFullQualityForPhase, buildAcEvidenceLedger, checkTaskClose, closeTaskKernel, createTaskKernel, emptyTaskRecord, fingerprintTaskValue, isTaskDeliveryLevel, listTaskKernelSnapshots, parseAcceptanceItems, qualityFingerprint, readDependencyGraph, readKernel, readTaskKernel, readTopology, recordTaskReview, recordTaskRunResult, resolveRequiredControls, startTaskRun, taskRecordSchema, unmetRequires, } from "../core/task/index.js";
|
|
5
|
+
import { createTaskCandidateEntry, isRunCandidateObservationEntry, observeTaskRunCandidate, } from "../core/task/task-candidate-observer.js";
|
|
6
|
+
import { addContextEntry, CONTEXT_FILES, readContextEntries, validateContextFile, } from "../pactile/task/context.js";
|
|
7
|
+
import * as piBridge from "../pactile/pi/bridge.js";
|
|
8
|
+
import { approvedExecuteTask } from "../pactile/task/authorization.js";
|
|
9
|
+
import { createTaskWithArtifacts } from "../pactile/task/creation.js";
|
|
6
10
|
import { readPactileConfig } from "../pactile/task/config.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
+
import { resumeTaskRunWithCoordinationBarrier } from "../pactile/coordination/index.js";
|
|
12
|
+
import { runTaskScheduleCli, runTaskScheduleCliAsync, runTaskScheduleDispatchCli, } from "./task-schedule.js";
|
|
13
|
+
import { checkArchive, checkStartExecution, dependencyStatus, } from "../pactile/task/guards.js";
|
|
14
|
+
import { exitTask, resolveSelectedTask, resolveTaskDir, selectTask, } from "../pactile/task/session.js";
|
|
15
|
+
import { artifactFingerprint, contractFingerprint, currentGateErrors, readStrategyContract, requiredGates, } from "../pactile/task/strategy.js";
|
|
16
|
+
import { CHILD_STATES, childStateErrors, ensureTaskMap, readTaskMap, writeTaskMap, } from "../pactile/task/task-map.js";
|
|
11
17
|
import { readDeveloper } from "../utils/developer.js";
|
|
12
18
|
import { sameGitRoot } from "../utils/git-root.js";
|
|
13
19
|
import { localDate } from "../utils/local-date.js";
|
|
14
20
|
import { getAllTaskTemplates } from "../templates/pactile/index.js";
|
|
15
|
-
import { learningScaffold, prepareArchiveEvidence } from "../pactile/task/scaffold.js";
|
|
21
|
+
import { learningScaffold, prepareArchiveEvidence, } from "../pactile/task/scaffold.js";
|
|
22
|
+
import { listLegacyTaskImportRecords, readLegacyTaskImportRecord, } from "../core/task/legacy-task-migration-reader.js";
|
|
23
|
+
import { TASK_ARTIFACT_STAGES_V1, projectTaskArtifactsForAgentV1, projectTaskArtifactsForHumanV1, projectTaskKernelArtifactsV1, readSelectedTaskArtifactDocumentSectionsV1, readSelectedTaskArtifactDocumentsV1, readSelectedTaskArtifactSourcesV1, readTaskArtifactDocumentIndexV1, renderTaskPrdScaffoldV1, } from "../pactile/artifacts/index.js";
|
|
16
24
|
function option(args, name) {
|
|
17
25
|
const index = args.indexOf(name);
|
|
18
26
|
return index >= 0 ? args[index + 1] : undefined;
|
|
19
27
|
}
|
|
28
|
+
function options(args, name) {
|
|
29
|
+
const values = [];
|
|
30
|
+
for (let index = 0; index < args.length; index++) {
|
|
31
|
+
if (args[index] !== name)
|
|
32
|
+
continue;
|
|
33
|
+
const value = args[index + 1];
|
|
34
|
+
if (!value || value.startsWith("--"))
|
|
35
|
+
throw new Error(`${name} requires a value`);
|
|
36
|
+
values.push(value);
|
|
37
|
+
index++;
|
|
38
|
+
}
|
|
39
|
+
return values;
|
|
40
|
+
}
|
|
20
41
|
function requireArgument(value, label) {
|
|
21
42
|
if (!value || value.startsWith("--"))
|
|
22
43
|
throw new Error(`${label} is required`);
|
|
23
44
|
return value;
|
|
24
45
|
}
|
|
46
|
+
function requireLast(values, label) {
|
|
47
|
+
const value = values.at(-1);
|
|
48
|
+
if (value === undefined)
|
|
49
|
+
throw new Error(`${label} was not recorded`);
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
25
52
|
function taskDir(root, reference) {
|
|
26
53
|
const dir = resolveTaskDir(root, reference);
|
|
27
54
|
if (!fs.statSync(dir, { throwIfNoEntry: false })?.isDirectory())
|
|
@@ -31,7 +58,11 @@ function taskDir(root, reference) {
|
|
|
31
58
|
function taskRecord(dir) {
|
|
32
59
|
try {
|
|
33
60
|
const value = JSON.parse(fs.readFileSync(path.join(dir, "task.json"), "utf8"));
|
|
34
|
-
|
|
61
|
+
const parsed = taskRecordSchema.safeParse(value);
|
|
62
|
+
// Keep the validated source object intact. Legacy task.json files may carry
|
|
63
|
+
// fields outside the current schema; stripping them here changes V1
|
|
64
|
+
// dependency/quality behavior when subsequent commands patch the record.
|
|
65
|
+
return parsed.success ? value : null;
|
|
35
66
|
}
|
|
36
67
|
catch {
|
|
37
68
|
return null;
|
|
@@ -39,14 +70,22 @@ function taskRecord(dir) {
|
|
|
39
70
|
}
|
|
40
71
|
function runTaskHooks(root, event, taskJson) {
|
|
41
72
|
const hooks = readPactileConfig(root).hooks;
|
|
42
|
-
const entries = hooks && typeof hooks === "object" && !Array.isArray(hooks)
|
|
73
|
+
const entries = hooks && typeof hooks === "object" && !Array.isArray(hooks)
|
|
74
|
+
? hooks[event]
|
|
75
|
+
: undefined;
|
|
43
76
|
if (!Array.isArray(entries))
|
|
44
77
|
return;
|
|
45
78
|
for (const command of entries) {
|
|
46
79
|
if (typeof command !== "string" || !command.trim())
|
|
47
80
|
continue;
|
|
48
81
|
try {
|
|
49
|
-
execSync(command, {
|
|
82
|
+
execSync(command, {
|
|
83
|
+
cwd: root,
|
|
84
|
+
env: { ...process.env, TASK_JSON_PATH: taskJson },
|
|
85
|
+
encoding: "utf8",
|
|
86
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
87
|
+
timeout: 60_000,
|
|
88
|
+
});
|
|
50
89
|
}
|
|
51
90
|
catch (error) {
|
|
52
91
|
console.warn(`[WARN] Hook failed (${event}): ${command} — ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -73,9 +112,16 @@ function activeTasks(root) {
|
|
|
73
112
|
const tasks = path.join(root, ".pactile", "tasks");
|
|
74
113
|
if (!fs.existsSync(tasks))
|
|
75
114
|
return [];
|
|
76
|
-
return fs
|
|
77
|
-
.
|
|
78
|
-
.
|
|
115
|
+
return fs
|
|
116
|
+
.readdirSync(tasks, { withFileTypes: true })
|
|
117
|
+
.filter((entry) => entry.isDirectory() &&
|
|
118
|
+
entry.name !== "archive" &&
|
|
119
|
+
entry.name !== "locale" &&
|
|
120
|
+
entry.name !== "templates")
|
|
121
|
+
.map((entry) => ({
|
|
122
|
+
dir: entry.name,
|
|
123
|
+
record: taskRecord(path.join(tasks, entry.name)),
|
|
124
|
+
}))
|
|
79
125
|
.filter((item) => item.record !== null)
|
|
80
126
|
.sort((left, right) => left.dir.localeCompare(right.dir));
|
|
81
127
|
}
|
|
@@ -93,9 +139,597 @@ function verifySeed(taskLocale) {
|
|
|
93
139
|
? "# 验证证据\n\n## Planning check\n\n_可选(Full 任务)— 记录规划阶段审查结果。_\n\n## Execution evidence\n\n### Validation commands\n\n<!-- 示例:Validation commands: <命令> — <结果> -->\n\n### Acceptance\n\n<!-- 示例:Final acceptance evidence: <验收标准达成说明> -->\n\n### Durable learning\n\n<!-- 示例:Durable learning decision: no durable learning -->\n"
|
|
94
140
|
: "# Verification Evidence\n\n## Planning check\n\n_Optional for Full tasks — record planning review outcomes._\n\n## Execution evidence\n\n### Validation commands\n\n<!-- Example: Validation commands: <command> — <outcome> -->\n\n### Acceptance\n\n<!-- Example: Final acceptance evidence: <criteria met> -->\n\n### Durable learning\n\n<!-- Example: Durable learning decision: no durable learning -->\n";
|
|
95
141
|
}
|
|
142
|
+
function parseAcceptanceCriteria(args) {
|
|
143
|
+
const raw = options(args, "--accept");
|
|
144
|
+
if (!raw.length)
|
|
145
|
+
throw new Error("at least one --accept <criterion> is required");
|
|
146
|
+
const parsed = raw.map((value, index) => {
|
|
147
|
+
const separator = value.indexOf("=");
|
|
148
|
+
if (separator > 0)
|
|
149
|
+
return {
|
|
150
|
+
id: value.slice(0, separator).trim(),
|
|
151
|
+
description: value.slice(separator + 1).trim(),
|
|
152
|
+
};
|
|
153
|
+
return { id: `AC-${index + 1}`, description: value.trim() };
|
|
154
|
+
});
|
|
155
|
+
if (parsed.some((item) => !item.id || !item.description))
|
|
156
|
+
throw new Error("--accept must be <criterion> or <id>=<criterion>");
|
|
157
|
+
return parsed;
|
|
158
|
+
}
|
|
159
|
+
function taskV2(root, reference) {
|
|
160
|
+
const dir = taskDir(root, reference);
|
|
161
|
+
const result = readTaskKernel({ root, taskDir: dir, cwd: root });
|
|
162
|
+
if (result.kind !== "task-kernel-v2")
|
|
163
|
+
throw new Error(`Task ${reference} uses legacy Kernel v1. It remains readable; automatic migration is reserved for P36.`);
|
|
164
|
+
return { dir, kernel: result.kernel };
|
|
165
|
+
}
|
|
166
|
+
function parseTaskArtifactDocumentSelection(value) {
|
|
167
|
+
const separator = value.lastIndexOf("@");
|
|
168
|
+
if (separator <= 0 || separator === value.length - 1) {
|
|
169
|
+
throw new Error("--document must be <document-id>@<sha256-fingerprint|absent> copied from a current artifact index");
|
|
170
|
+
}
|
|
171
|
+
const id = value.slice(0, separator);
|
|
172
|
+
const expectedFingerprint = value.slice(separator + 1);
|
|
173
|
+
if (expectedFingerprint !== "absent" &&
|
|
174
|
+
!/^sha256:[a-f0-9]{64}$/u.test(expectedFingerprint)) {
|
|
175
|
+
throw new Error("--document fingerprint must be a lowercase sha256 digest or absent");
|
|
176
|
+
}
|
|
177
|
+
return { id, expectedFingerprint };
|
|
178
|
+
}
|
|
179
|
+
function parseTaskArtifactSectionSelection(value) {
|
|
180
|
+
const separator = value.lastIndexOf("@");
|
|
181
|
+
if (separator <= 0 || separator === value.length - 1) {
|
|
182
|
+
throw new Error("--section must be <section-id>@<sha256-fingerprint> copied from a current artifact index");
|
|
183
|
+
}
|
|
184
|
+
const id = value.slice(0, separator);
|
|
185
|
+
const expectedFingerprint = value.slice(separator + 1);
|
|
186
|
+
if (!id.startsWith("section:") ||
|
|
187
|
+
!/^sha256:[a-f0-9]{64}$/u.test(expectedFingerprint)) {
|
|
188
|
+
throw new Error("--section requires a stable section ID and lowercase sha256 fingerprint");
|
|
189
|
+
}
|
|
190
|
+
return { id, expectedFingerprint };
|
|
191
|
+
}
|
|
192
|
+
function taskArtifacts(root, args) {
|
|
193
|
+
const reference = requireArgument(args[0], "task");
|
|
194
|
+
const { dir, kernel } = taskV2(root, reference);
|
|
195
|
+
const requestedStages = [...new Set(options(args, "--stage"))].map((value) => {
|
|
196
|
+
if (!TASK_ARTIFACT_STAGES_V1.includes(value)) {
|
|
197
|
+
throw new Error(`--stage must be one of: ${TASK_ARTIFACT_STAGES_V1.join(", ")}`);
|
|
198
|
+
}
|
|
199
|
+
return value;
|
|
200
|
+
});
|
|
201
|
+
const envelope = projectTaskKernelArtifactsV1(kernel);
|
|
202
|
+
const documents = readTaskArtifactDocumentIndexV1(dir, kernel.identity.taskId);
|
|
203
|
+
const projectionOptions = {
|
|
204
|
+
...(requestedStages.length ? { stages: requestedStages } : {}),
|
|
205
|
+
documents,
|
|
206
|
+
};
|
|
207
|
+
const requestedFactRefs = options(args, "--fact");
|
|
208
|
+
const selectedFactIds = requestedFactRefs.map((reference) => {
|
|
209
|
+
const fact = envelope.facts.find((candidate) => candidate.id === reference ||
|
|
210
|
+
`${candidate.ref.uri}#${candidate.ref.selector}` === reference);
|
|
211
|
+
if (!fact) {
|
|
212
|
+
throw new Error(`Unknown task artifact fact ID or locator: ${reference}`);
|
|
213
|
+
}
|
|
214
|
+
return fact.id;
|
|
215
|
+
});
|
|
216
|
+
const selectedDocuments = options(args, "--document").map(parseTaskArtifactDocumentSelection);
|
|
217
|
+
const selectedSections = options(args, "--section").map(parseTaskArtifactSectionSelection);
|
|
218
|
+
const agent = args.includes("--agent");
|
|
219
|
+
if (selectedDocuments.length) {
|
|
220
|
+
const visibleDocumentIds = new Set(requestedStages.length
|
|
221
|
+
? documents
|
|
222
|
+
.filter((document) => requestedStages.includes(document.stage))
|
|
223
|
+
.map(({ id }) => id)
|
|
224
|
+
: documents.map(({ id }) => id));
|
|
225
|
+
const outsideStage = selectedDocuments.find(({ id }) => !visibleDocumentIds.has(id));
|
|
226
|
+
if (outsideStage) {
|
|
227
|
+
throw new Error(`Task artifact document '${outsideStage.id}' is not present in the requested stage view`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const selectedDocumentContents = selectedDocuments.length
|
|
231
|
+
? readSelectedTaskArtifactDocumentsV1(dir, kernel.identity.taskId, selectedDocuments)
|
|
232
|
+
: undefined;
|
|
233
|
+
if (selectedSections.length) {
|
|
234
|
+
const visibleSectionIds = new Set(documents
|
|
235
|
+
.filter((document) => !requestedStages.length || requestedStages.includes(document.stage))
|
|
236
|
+
.flatMap((document) => (document.sections ?? []).map(({ id }) => id)));
|
|
237
|
+
const outsideStage = selectedSections.find(({ id }) => !visibleSectionIds.has(id));
|
|
238
|
+
if (outsideStage) {
|
|
239
|
+
throw new Error(`Task artifact document section '${outsideStage.id}' is not present in the requested stage view`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const selectedSectionContents = selectedSections.length
|
|
243
|
+
? readSelectedTaskArtifactDocumentSectionsV1(dir, kernel.identity.taskId, selectedSections)
|
|
244
|
+
: undefined;
|
|
245
|
+
if (selectedFactIds.length) {
|
|
246
|
+
const visibleFactIds = new Set(requestedStages.length
|
|
247
|
+
? requestedStages.flatMap((stage) => envelope.stageRefs[stage] ?? [])
|
|
248
|
+
: envelope.facts.map((fact) => fact.id));
|
|
249
|
+
const outsideStage = selectedFactIds.find((id) => !visibleFactIds.has(id));
|
|
250
|
+
if (outsideStage) {
|
|
251
|
+
throw new Error(`Task artifact fact '${outsideStage}' is not present in the requested stage view`);
|
|
252
|
+
}
|
|
253
|
+
const selectedSources = readSelectedTaskArtifactSourcesV1(kernel, envelope, selectedFactIds);
|
|
254
|
+
if (agent) {
|
|
255
|
+
console.log(JSON.stringify({
|
|
256
|
+
index: projectTaskArtifactsForAgentV1(envelope, projectionOptions),
|
|
257
|
+
selectedSources,
|
|
258
|
+
...(selectedDocumentContents
|
|
259
|
+
? { selectedDocuments: selectedDocumentContents }
|
|
260
|
+
: {}),
|
|
261
|
+
...(selectedSectionContents
|
|
262
|
+
? { selectedSections: selectedSectionContents }
|
|
263
|
+
: {}),
|
|
264
|
+
}, null, 2));
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
console.log(JSON.stringify({
|
|
268
|
+
taskId: kernel.identity.taskId,
|
|
269
|
+
selectedSources,
|
|
270
|
+
...(selectedDocumentContents
|
|
271
|
+
? { selectedDocuments: selectedDocumentContents }
|
|
272
|
+
: {}),
|
|
273
|
+
...(selectedSectionContents
|
|
274
|
+
? { selectedSections: selectedSectionContents }
|
|
275
|
+
: {}),
|
|
276
|
+
}, null, 2));
|
|
277
|
+
}
|
|
278
|
+
return 0;
|
|
279
|
+
}
|
|
280
|
+
if (selectedDocumentContents || selectedSectionContents) {
|
|
281
|
+
console.log(JSON.stringify({
|
|
282
|
+
...(agent
|
|
283
|
+
? {
|
|
284
|
+
index: projectTaskArtifactsForAgentV1(envelope, projectionOptions),
|
|
285
|
+
}
|
|
286
|
+
: { taskId: kernel.identity.taskId }),
|
|
287
|
+
...(selectedDocumentContents
|
|
288
|
+
? { selectedDocuments: selectedDocumentContents }
|
|
289
|
+
: {}),
|
|
290
|
+
...(selectedSectionContents
|
|
291
|
+
? { selectedSections: selectedSectionContents }
|
|
292
|
+
: {}),
|
|
293
|
+
}, null, 2));
|
|
294
|
+
return 0;
|
|
295
|
+
}
|
|
296
|
+
if (agent) {
|
|
297
|
+
console.log(JSON.stringify(projectTaskArtifactsForAgentV1(envelope, projectionOptions), null, 2));
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
console.log(projectTaskArtifactsForHumanV1(envelope, projectionOptions));
|
|
301
|
+
}
|
|
302
|
+
return 0;
|
|
303
|
+
}
|
|
304
|
+
function actor(root, args) {
|
|
305
|
+
return option(args, "--actor") ?? readDeveloper(root) ?? "user";
|
|
306
|
+
}
|
|
96
307
|
function createTask(args, root) {
|
|
308
|
+
if (args.includes("--legacy")) {
|
|
309
|
+
createLegacyTask(args.filter((value) => value !== "--legacy"), root);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (args.includes("--rigor") || args.includes("--parent")) {
|
|
313
|
+
throw new Error("--rigor and --parent are legacy task presets. Use `pactile task legacy-create --legacy` for existing 0.5.x workflows, or create a Task with --deliverable, --delivery-level, and --accept.");
|
|
314
|
+
}
|
|
97
315
|
const title = requireArgument(args[0], "title");
|
|
98
|
-
const slug =
|
|
316
|
+
const slug = option(args, "--slug") ??
|
|
317
|
+
title
|
|
318
|
+
.toLowerCase()
|
|
319
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
320
|
+
.replace(/^-|-$/g, "");
|
|
321
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug))
|
|
322
|
+
throw new Error("slug must contain lowercase letters, digits and hyphens");
|
|
323
|
+
const deliverable = requireArgument(option(args, "--deliverable"), "--deliverable");
|
|
324
|
+
const deliveryLevel = option(args, "--delivery-level");
|
|
325
|
+
if (!isTaskDeliveryLevel(deliveryLevel))
|
|
326
|
+
throw new Error("--delivery-level must be local-result, pull-request, merged-result, or documentation");
|
|
327
|
+
const dependencies = [...new Set(options(args, "--depends-on"))];
|
|
328
|
+
const directoryName = `${localDate(new Date()).slice(5)}-${slug}`;
|
|
329
|
+
const dir = path.resolve(root, ".pactile", "tasks", directoryName);
|
|
330
|
+
const result = createTaskKernel({
|
|
331
|
+
root,
|
|
332
|
+
taskDir: dir,
|
|
333
|
+
actor: actor(root, args),
|
|
334
|
+
idempotencyKey: option(args, "--idempotency-key") ?? `task-create:${slug}`,
|
|
335
|
+
definition: {
|
|
336
|
+
taskId: slug,
|
|
337
|
+
title,
|
|
338
|
+
description: option(args, "--description") ?? "",
|
|
339
|
+
deliverable,
|
|
340
|
+
deliveryLevel,
|
|
341
|
+
acceptanceCriteria: parseAcceptanceCriteria(args),
|
|
342
|
+
dependencies,
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
if (!result.idempotent) {
|
|
346
|
+
const prdPath = path.join(dir, "prd.md");
|
|
347
|
+
if (!fs.existsSync(prdPath)) {
|
|
348
|
+
try {
|
|
349
|
+
fs.writeFileSync(prdPath, renderTaskPrdScaffoldV1(result.kernel), {
|
|
350
|
+
encoding: "utf8",
|
|
351
|
+
flag: "wx",
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
if (!(error instanceof Error) ||
|
|
356
|
+
!("code" in error) ||
|
|
357
|
+
error.code !== "EEXIST")
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
console.log(`${path.relative(root, dir).replaceAll("\\", "/")}\nTask Kernel schema: ${result.kernel.schemaVersion}\nTask ID: ${result.kernel.identity.taskId}\nPRD: ${path.relative(root, path.join(dir, "prd.md")).replaceAll("\\", "/")}\nLive fact view: pactile task artifacts ${result.kernel.identity.taskId}`);
|
|
363
|
+
}
|
|
364
|
+
function runStartTask(root, args) {
|
|
365
|
+
const reference = requireArgument(args[0], "task");
|
|
366
|
+
const { dir, kernel } = taskV2(root, reference);
|
|
367
|
+
const authorization = {
|
|
368
|
+
approvedBy: requireArgument(option(args, "--approved-by"), "--approved-by"),
|
|
369
|
+
approvedAt: option(args, "--approved-at") ?? new Date().toISOString(),
|
|
370
|
+
scope: requireArgument(option(args, "--authorization-scope"), "--authorization-scope"),
|
|
371
|
+
evidenceRef: requireArgument(option(args, "--authorization-evidence"), "--authorization-evidence"),
|
|
372
|
+
};
|
|
373
|
+
const workspaceInputs = ["--workspace-path", "--branch", "--base-sha"].some((flag) => args.includes(flag));
|
|
374
|
+
const taskActor = actor(root, args);
|
|
375
|
+
let workspace;
|
|
376
|
+
if (workspaceInputs) {
|
|
377
|
+
workspace = {
|
|
378
|
+
canonicalPath: requireArgument(option(args, "--workspace-path"), "--workspace-path"),
|
|
379
|
+
branch: requireArgument(option(args, "--branch"), "--branch"),
|
|
380
|
+
baseSha: requireArgument(option(args, "--base-sha"), "--base-sha"),
|
|
381
|
+
writeSet: options(args, "--write-set"),
|
|
382
|
+
integrationState: "not-integrated",
|
|
383
|
+
reclamationState: "not-requested",
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
const hostInputs = [
|
|
387
|
+
"--host",
|
|
388
|
+
"--role",
|
|
389
|
+
"--session-id",
|
|
390
|
+
"--thread-id",
|
|
391
|
+
"--request-ref",
|
|
392
|
+
"--event-ref",
|
|
393
|
+
"--result-ref",
|
|
394
|
+
"--assurance-source",
|
|
395
|
+
].some((flag) => args.includes(flag));
|
|
396
|
+
let host;
|
|
397
|
+
if (hostInputs) {
|
|
398
|
+
host = {
|
|
399
|
+
host: requireArgument(option(args, "--host"), "--host"),
|
|
400
|
+
role: requireArgument(option(args, "--role"), "--role"),
|
|
401
|
+
sessionId: option(args, "--session-id") ?? null,
|
|
402
|
+
threadId: option(args, "--thread-id") ?? null,
|
|
403
|
+
requestRefs: options(args, "--request-ref"),
|
|
404
|
+
eventRefs: options(args, "--event-ref"),
|
|
405
|
+
resultRefs: options(args, "--result-ref"),
|
|
406
|
+
assuranceSource: option(args, "--assurance-source") ?? null,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
const durationOption = (name) => {
|
|
410
|
+
const value = option(args, name);
|
|
411
|
+
if (value === undefined)
|
|
412
|
+
return null;
|
|
413
|
+
const parsed = Number(value);
|
|
414
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0)
|
|
415
|
+
throw new Error(`${name} must be a non-negative integer`);
|
|
416
|
+
return parsed;
|
|
417
|
+
};
|
|
418
|
+
const input = {
|
|
419
|
+
summary: requireArgument(option(args, "--input-summary"), "--input-summary"),
|
|
420
|
+
references: options(args, "--input-ref"),
|
|
421
|
+
};
|
|
422
|
+
const initialState = args.includes("--wait")
|
|
423
|
+
? "waiting"
|
|
424
|
+
: "running";
|
|
425
|
+
const writeSetSnapshot = args.includes("--write-set-snapshot")
|
|
426
|
+
? options(args, "--write-set-snapshot")
|
|
427
|
+
: options(args, "--write-set");
|
|
428
|
+
const estimatedDurations = {
|
|
429
|
+
executionMs: durationOption("--estimate-execution-ms"),
|
|
430
|
+
waitingMs: durationOption("--estimate-waiting-ms"),
|
|
431
|
+
reviewMs: durationOption("--estimate-review-ms"),
|
|
432
|
+
};
|
|
433
|
+
const requestFingerprint = fingerprintTaskValue({
|
|
434
|
+
actor: taskActor,
|
|
435
|
+
input,
|
|
436
|
+
authorization: {
|
|
437
|
+
approvedBy: authorization.approvedBy,
|
|
438
|
+
scope: authorization.scope,
|
|
439
|
+
evidenceRef: authorization.evidenceRef,
|
|
440
|
+
},
|
|
441
|
+
initialState,
|
|
442
|
+
writeSetSnapshot,
|
|
443
|
+
estimatedDurations,
|
|
444
|
+
workspace: workspace ?? null,
|
|
445
|
+
host: host ?? null,
|
|
446
|
+
});
|
|
447
|
+
const activeRun = kernel.runs.find((run) => run.state === "running" || run.state === "waiting");
|
|
448
|
+
const attempt = activeRun?.attempt ?? kernel.runs.length + 1;
|
|
449
|
+
const idempotencyKey = option(args, "--idempotency-key") ??
|
|
450
|
+
`run-start:${kernel.identity.taskId}:${attempt}:${requestFingerprint}`;
|
|
451
|
+
const priorStart = kernel.events.find((event) => event.idempotencyKey === idempotencyKey &&
|
|
452
|
+
(event.type === "run.started" || event.type === "run.queued"));
|
|
453
|
+
const priorRun = priorStart
|
|
454
|
+
? kernel.runs.find((run) => run.id === priorStart.entityId)
|
|
455
|
+
: undefined;
|
|
456
|
+
if (!option(args, "--approved-at") && priorRun)
|
|
457
|
+
authorization.approvedAt = priorRun.authorization.approvedAt;
|
|
458
|
+
const result = startTaskRun({
|
|
459
|
+
root,
|
|
460
|
+
taskDir: dir,
|
|
461
|
+
expectedRevision: kernel.revision,
|
|
462
|
+
actor: taskActor,
|
|
463
|
+
idempotencyKey,
|
|
464
|
+
input,
|
|
465
|
+
initialState,
|
|
466
|
+
writeSetSnapshot,
|
|
467
|
+
estimatedDurations,
|
|
468
|
+
authorization,
|
|
469
|
+
...(workspace ? { workspace } : {}),
|
|
470
|
+
...(host ? { host } : {}),
|
|
471
|
+
});
|
|
472
|
+
const run = requireLast(result.kernel.runs, "Run");
|
|
473
|
+
console.log(`Run ${run.state === "waiting" ? "queued" : "started"}: ${run.id}\nTask: ${run.taskId}\nAttempt: ${run.attempt}\nKernel revision: ${result.kernel.revision}`);
|
|
474
|
+
return 0;
|
|
475
|
+
}
|
|
476
|
+
function resumeTask(root, args) {
|
|
477
|
+
const reference = requireArgument(args[0], "task");
|
|
478
|
+
const runId = requireArgument(args[1], "run ID");
|
|
479
|
+
const { dir, kernel } = taskV2(root, reference);
|
|
480
|
+
const result = resumeTaskRunWithCoordinationBarrier({
|
|
481
|
+
root,
|
|
482
|
+
taskDir: dir,
|
|
483
|
+
expectedRevision: kernel.revision,
|
|
484
|
+
runId,
|
|
485
|
+
actor: actor(root, args),
|
|
486
|
+
idempotencyKey: option(args, "--idempotency-key") ?? `run-resume:${runId}`,
|
|
487
|
+
});
|
|
488
|
+
console.log(`Run resumed: ${runId}\nKernel revision: ${result.kernel.revision}`);
|
|
489
|
+
return 0;
|
|
490
|
+
}
|
|
491
|
+
function parseCandidateEntries(args) {
|
|
492
|
+
return options(args, "--candidate").map((value) => {
|
|
493
|
+
const separator = value.lastIndexOf("=");
|
|
494
|
+
if (separator <= 0)
|
|
495
|
+
throw new Error("--candidate must be <reference>=<sha256-fingerprint>");
|
|
496
|
+
return {
|
|
497
|
+
ref: value.slice(0, separator),
|
|
498
|
+
fingerprint: value.slice(separator + 1),
|
|
499
|
+
};
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
function runResultTask(root, args) {
|
|
503
|
+
const reference = requireArgument(args[0], "task");
|
|
504
|
+
const runId = requireArgument(args[1], "run ID");
|
|
505
|
+
const { dir, kernel } = taskV2(root, reference);
|
|
506
|
+
const outcome = requireArgument(option(args, "--outcome"), "--outcome");
|
|
507
|
+
if (outcome !== "completed" && outcome !== "failed" && outcome !== "blocked")
|
|
508
|
+
throw new Error("--outcome must be completed, failed, or blocked");
|
|
509
|
+
const failure = outcome === "completed"
|
|
510
|
+
? undefined
|
|
511
|
+
: {
|
|
512
|
+
category: requireArgument(option(args, "--failure-category"), "--failure-category"),
|
|
513
|
+
message: requireArgument(option(args, "--failure-message"), "--failure-message"),
|
|
514
|
+
...(option(args, "--failure-evidence")
|
|
515
|
+
? { evidenceRef: option(args, "--failure-evidence") }
|
|
516
|
+
: {}),
|
|
517
|
+
};
|
|
518
|
+
let candidateEntries = [];
|
|
519
|
+
let evidenceRefs = options(args, "--evidence");
|
|
520
|
+
let executionMeasurementRef = option(args, "--execution-measurement-ref");
|
|
521
|
+
if (outcome === "completed") {
|
|
522
|
+
const run = kernel.runs.find((item) => item.id === runId);
|
|
523
|
+
if (!run)
|
|
524
|
+
throw new Error(`Run ${runId} does not exist on Task ${kernel.identity.taskId}`);
|
|
525
|
+
const candidateInputs = parseCandidateEntries(args);
|
|
526
|
+
if (candidateInputs.some((entry) => isRunCandidateObservationEntry(entry.ref))) {
|
|
527
|
+
throw new Error("--candidate cannot set a reserved P41 machine-observation reference");
|
|
528
|
+
}
|
|
529
|
+
const piBound = run.host?.host === "pi";
|
|
530
|
+
const hostEvidenceRefs = run.host
|
|
531
|
+
? [
|
|
532
|
+
...run.host.requestRefs,
|
|
533
|
+
...run.host.eventRefs,
|
|
534
|
+
...run.host.resultRefs,
|
|
535
|
+
]
|
|
536
|
+
: [];
|
|
537
|
+
if (!piBound &&
|
|
538
|
+
hostEvidenceRefs.some((reference) => reference.replaceAll("\\", "/").startsWith("pi-bridge/"))) {
|
|
539
|
+
throw new Error("Pi evidence references cannot complete a Run bound to another host");
|
|
540
|
+
}
|
|
541
|
+
let piEvidence = null;
|
|
542
|
+
if (piBound) {
|
|
543
|
+
if (run.host?.role !== "implement" ||
|
|
544
|
+
run.host.assuranceSource !== "manager-owned-child-exit") {
|
|
545
|
+
throw new Error("Pi Run completion requires implement role and manager-owned-child-exit assurance");
|
|
546
|
+
}
|
|
547
|
+
piEvidence = piBridge.readPiTaskRunEvidence(root, dir, runId);
|
|
548
|
+
if (!piEvidence) {
|
|
549
|
+
throw new Error("Pi Run has no valid settled host-stop and persisted result receipt; it cannot be recorded completed");
|
|
550
|
+
}
|
|
551
|
+
if (piEvidence.taskRunId !== run.id ||
|
|
552
|
+
piEvidence.taskId !== run.taskId ||
|
|
553
|
+
piEvidence.outcome !== "settled" ||
|
|
554
|
+
piEvidence.assurance !== "manager-owned-child-exit") {
|
|
555
|
+
throw new Error("Pi success evidence does not match the selected Task Run binding");
|
|
556
|
+
}
|
|
557
|
+
const suppliedMeasurement = executionMeasurementRef;
|
|
558
|
+
if (suppliedMeasurement &&
|
|
559
|
+
suppliedMeasurement !== piEvidence.executionMeasurementRef) {
|
|
560
|
+
throw new Error("--execution-measurement-ref conflicts with the verified Pi Run receipt");
|
|
561
|
+
}
|
|
562
|
+
executionMeasurementRef = piEvidence.executionMeasurementRef;
|
|
563
|
+
evidenceRefs = [
|
|
564
|
+
...new Set([...evidenceRefs, ...piEvidence.evidenceRefs]),
|
|
565
|
+
];
|
|
566
|
+
}
|
|
567
|
+
const runObservation = observeTaskRunCandidate({
|
|
568
|
+
run,
|
|
569
|
+
...(run.workspace === null ? { repositoryRoot: root } : {}),
|
|
570
|
+
});
|
|
571
|
+
candidateEntries = [
|
|
572
|
+
...candidateInputs,
|
|
573
|
+
createTaskCandidateEntry(runObservation),
|
|
574
|
+
];
|
|
575
|
+
}
|
|
576
|
+
const result = recordTaskRunResult({
|
|
577
|
+
root,
|
|
578
|
+
taskDir: dir,
|
|
579
|
+
expectedRevision: kernel.revision,
|
|
580
|
+
runId,
|
|
581
|
+
outcome,
|
|
582
|
+
...(option(args, "--summary")
|
|
583
|
+
? { summary: option(args, "--summary") }
|
|
584
|
+
: {}),
|
|
585
|
+
evidenceRefs,
|
|
586
|
+
candidateEntries,
|
|
587
|
+
failure,
|
|
588
|
+
measurementRefs: {
|
|
589
|
+
...(executionMeasurementRef
|
|
590
|
+
? { execution: executionMeasurementRef }
|
|
591
|
+
: {}),
|
|
592
|
+
...(option(args, "--waiting-measurement-ref")
|
|
593
|
+
? { waiting: option(args, "--waiting-measurement-ref") }
|
|
594
|
+
: {}),
|
|
595
|
+
},
|
|
596
|
+
actor: actor(root, args),
|
|
597
|
+
idempotencyKey: option(args, "--idempotency-key") ?? `run-result:${runId}`,
|
|
598
|
+
});
|
|
599
|
+
console.log(`Run ${runId}: ${outcome}\nTask phase: ${result.kernel.phase}\nKernel revision: ${result.kernel.revision}`);
|
|
600
|
+
return 0;
|
|
601
|
+
}
|
|
602
|
+
function parseAcceptanceEvidenceArgs(args) {
|
|
603
|
+
const result = {};
|
|
604
|
+
for (const item of options(args, "--criterion")) {
|
|
605
|
+
const separator = item.indexOf("=");
|
|
606
|
+
if (separator <= 0 || separator === item.length - 1)
|
|
607
|
+
throw new Error("--criterion must be <criterion-id>=<evidence-reference>");
|
|
608
|
+
const id = item.slice(0, separator);
|
|
609
|
+
if (Object.prototype.hasOwnProperty.call(result, id))
|
|
610
|
+
result[id]?.push(item.slice(separator + 1));
|
|
611
|
+
else
|
|
612
|
+
Object.defineProperty(result, id, {
|
|
613
|
+
value: [item.slice(separator + 1)],
|
|
614
|
+
enumerable: true,
|
|
615
|
+
writable: true,
|
|
616
|
+
configurable: true,
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
return result;
|
|
620
|
+
}
|
|
621
|
+
function reviewTask(root, args) {
|
|
622
|
+
const reference = requireArgument(args[0], "task");
|
|
623
|
+
const { dir, kernel } = taskV2(root, reference);
|
|
624
|
+
const runId = requireArgument(option(args, "--run"), "--run");
|
|
625
|
+
const decision = requireArgument(option(args, "--decision"), "--decision");
|
|
626
|
+
if (decision !== "pass" &&
|
|
627
|
+
decision !== "fail" &&
|
|
628
|
+
decision !== "needs-changes")
|
|
629
|
+
throw new Error("--decision must be pass, fail, or needs-changes");
|
|
630
|
+
const candidateSnapshotId = requireArgument(option(args, "--candidate-id"), "--candidate-id");
|
|
631
|
+
const candidateFingerprint = requireArgument(option(args, "--candidate-fingerprint"), "--candidate-fingerprint");
|
|
632
|
+
const reviewer = requireArgument(option(args, "--reviewer"), "--reviewer");
|
|
633
|
+
const evidenceRefs = options(args, "--evidence");
|
|
634
|
+
const acceptanceEvidence = parseAcceptanceEvidenceArgs(args);
|
|
635
|
+
const unresolvedBlockers = options(args, "--blocker");
|
|
636
|
+
const measurementRef = option(args, "--review-measurement-ref");
|
|
637
|
+
const taskActor = actor(root, args);
|
|
638
|
+
const idempotencyKey = option(args, "--idempotency-key") ??
|
|
639
|
+
`review:${runId}:${fingerprintTaskValue({
|
|
640
|
+
actor: taskActor,
|
|
641
|
+
runId,
|
|
642
|
+
candidateSnapshotId,
|
|
643
|
+
candidateFingerprint,
|
|
644
|
+
reviewer,
|
|
645
|
+
decision,
|
|
646
|
+
evidenceRefs,
|
|
647
|
+
acceptanceEvidence,
|
|
648
|
+
unresolvedBlockers,
|
|
649
|
+
measurementRef: measurementRef ?? null,
|
|
650
|
+
})}`;
|
|
651
|
+
const result = recordTaskReview({
|
|
652
|
+
root,
|
|
653
|
+
taskDir: dir,
|
|
654
|
+
expectedRevision: kernel.revision,
|
|
655
|
+
runId,
|
|
656
|
+
candidateSnapshotId,
|
|
657
|
+
candidateFingerprint,
|
|
658
|
+
reviewer,
|
|
659
|
+
decision,
|
|
660
|
+
evidenceRefs,
|
|
661
|
+
acceptanceEvidence,
|
|
662
|
+
unresolvedBlockers,
|
|
663
|
+
measurementRef,
|
|
664
|
+
actor: taskActor,
|
|
665
|
+
idempotencyKey,
|
|
666
|
+
});
|
|
667
|
+
const review = requireLast(result.kernel.reviews, "Review");
|
|
668
|
+
console.log(`Review recorded: ${review.id}\nDecision: ${review.decision}\nCandidate: ${review.candidateFingerprint}`);
|
|
669
|
+
return 0;
|
|
670
|
+
}
|
|
671
|
+
function closeTask(root, args) {
|
|
672
|
+
const reference = requireArgument(args[0], "task");
|
|
673
|
+
const { dir, kernel } = taskV2(root, reference);
|
|
674
|
+
const runId = requireArgument(option(args, "--run"), "--run");
|
|
675
|
+
const reviewId = requireArgument(option(args, "--review"), "--review");
|
|
676
|
+
const deliveryEvidence = {
|
|
677
|
+
level: option(args, "--delivery-level"),
|
|
678
|
+
reference: requireArgument(option(args, "--delivery-ref"), "--delivery-ref"),
|
|
679
|
+
summary: requireArgument(option(args, "--delivery-summary"), "--delivery-summary"),
|
|
680
|
+
...(option(args, "--delivery-path")
|
|
681
|
+
? { path: option(args, "--delivery-path") }
|
|
682
|
+
: {}),
|
|
683
|
+
...(option(args, "--target-branch")
|
|
684
|
+
? { targetBranch: option(args, "--target-branch") }
|
|
685
|
+
: {}),
|
|
686
|
+
};
|
|
687
|
+
if (!isTaskDeliveryLevel(deliveryEvidence.level))
|
|
688
|
+
throw new Error("--delivery-level must be local-result, pull-request, merged-result, or documentation");
|
|
689
|
+
const candidateObservation = {
|
|
690
|
+
snapshotId: requireArgument(option(args, "--candidate-id"), "--candidate-id"),
|
|
691
|
+
fingerprint: requireArgument(option(args, "--candidate-fingerprint"), "--candidate-fingerprint"),
|
|
692
|
+
observedBy: requireArgument(option(args, "--candidate-observed-by"), "--candidate-observed-by"),
|
|
693
|
+
observedAt: option(args, "--candidate-observed-at") ?? new Date().toISOString(),
|
|
694
|
+
source: requireArgument(option(args, "--candidate-observation-source"), "--candidate-observation-source"),
|
|
695
|
+
evidenceRef: requireArgument(option(args, "--candidate-observation-ref"), "--candidate-observation-ref"),
|
|
696
|
+
};
|
|
697
|
+
if (args.includes("--check")) {
|
|
698
|
+
const errors = checkTaskClose({
|
|
699
|
+
root,
|
|
700
|
+
taskDir: dir,
|
|
701
|
+
expectedRevision: kernel.revision,
|
|
702
|
+
runId,
|
|
703
|
+
reviewId,
|
|
704
|
+
candidateObservation,
|
|
705
|
+
deliveryEvidence,
|
|
706
|
+
});
|
|
707
|
+
console.log(`Task Close check: ${errors.length ? "FAIL" : "PASS"}${errors.length ? `\n${errors.map((error) => ` - ${error}`).join("\n")}` : ""}`);
|
|
708
|
+
console.log("Candidate freshness: Close re-observes current project state against the frozen Run candidate.");
|
|
709
|
+
return errors.length ? 1 : 0;
|
|
710
|
+
}
|
|
711
|
+
const result = closeTaskKernel({
|
|
712
|
+
root,
|
|
713
|
+
taskDir: dir,
|
|
714
|
+
expectedRevision: kernel.revision,
|
|
715
|
+
runId,
|
|
716
|
+
reviewId,
|
|
717
|
+
candidateObservation,
|
|
718
|
+
deliveryEvidence,
|
|
719
|
+
actor: actor(root, args),
|
|
720
|
+
idempotencyKey: option(args, "--idempotency-key") ??
|
|
721
|
+
`task-close:${kernel.identity.taskId}`,
|
|
722
|
+
});
|
|
723
|
+
console.log(`Task closed: ${result.kernel.identity.taskId}\nDelivery level: ${deliveryEvidence.level}\nCandidate freshness: current project state re-observed\nDelivery evidence: machine-observed\nKernel revision: ${result.kernel.revision}`);
|
|
724
|
+
return 0;
|
|
725
|
+
}
|
|
726
|
+
function createLegacyTask(args, root) {
|
|
727
|
+
const title = requireArgument(args[0], "title");
|
|
728
|
+
const slug = option(args, "--slug") ??
|
|
729
|
+
title
|
|
730
|
+
.toLowerCase()
|
|
731
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
732
|
+
.replace(/^-|-$/g, "");
|
|
99
733
|
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug))
|
|
100
734
|
throw new Error("slug must contain lowercase letters, digits and hyphens");
|
|
101
735
|
const parentRef = option(args, "--parent");
|
|
@@ -117,7 +751,11 @@ function createTask(args, root) {
|
|
|
117
751
|
if (fs.existsSync(path.join(dir, "task.json")))
|
|
118
752
|
throw new Error(`Task already exists: ${dirName}`);
|
|
119
753
|
const archive = path.join(tasks, "archive");
|
|
120
|
-
if (fs.existsSync(archive) &&
|
|
754
|
+
if (fs.existsSync(archive) &&
|
|
755
|
+
fs
|
|
756
|
+
.readdirSync(archive, { withFileTypes: true })
|
|
757
|
+
.some((month) => month.isDirectory() &&
|
|
758
|
+
fs.existsSync(path.join(archive, month.name, dirName)))) {
|
|
121
759
|
throw new Error(`Task already archived: ${dirName}`);
|
|
122
760
|
}
|
|
123
761
|
const taskLocale = locale(root);
|
|
@@ -128,20 +766,33 @@ function createTask(args, root) {
|
|
|
128
766
|
if (!prdTemplate)
|
|
129
767
|
throw new Error(`Task PRD template not found for locale ${taskLocale}`);
|
|
130
768
|
const contextSeeds = fs.existsSync(path.join(root, ".codex"))
|
|
131
|
-
? [
|
|
132
|
-
.
|
|
769
|
+
? [
|
|
770
|
+
".pactile/spec/guides/index.md",
|
|
771
|
+
".pactile/framework/verification-strength-guide.md",
|
|
772
|
+
".pactile/framework/injection-budget-guide.md",
|
|
773
|
+
].filter((entry) => fs.existsSync(path.join(root, entry)))
|
|
133
774
|
: [];
|
|
134
775
|
if (fs.existsSync(path.join(root, ".codex")) && contextSeeds.length < 2) {
|
|
135
776
|
throw new Error("task create requires at least two existing spec guide paths for JSONL seed");
|
|
136
777
|
}
|
|
137
778
|
let baseBranch = "main";
|
|
138
779
|
try {
|
|
139
|
-
baseBranch =
|
|
780
|
+
baseBranch =
|
|
781
|
+
execFileSync("git", ["branch", "--show-current"], {
|
|
782
|
+
cwd: root,
|
|
783
|
+
encoding: "utf8",
|
|
784
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
785
|
+
}).trim() || "main";
|
|
786
|
+
}
|
|
787
|
+
catch {
|
|
788
|
+
/* A non-Git project still has a valid default base branch. */
|
|
140
789
|
}
|
|
141
|
-
catch { /* A non-Git project still has a valid default base branch. */ }
|
|
142
790
|
const description = option(args, "--description") ?? "";
|
|
143
791
|
const record = emptyTaskRecord({
|
|
144
|
-
id: slug,
|
|
792
|
+
id: slug,
|
|
793
|
+
name: slug,
|
|
794
|
+
title,
|
|
795
|
+
description,
|
|
145
796
|
priority: option(args, "--priority") ?? "P2",
|
|
146
797
|
package: option(args, "--package") ?? null,
|
|
147
798
|
creator: developer ?? assignee,
|
|
@@ -152,23 +803,31 @@ function createTask(args, root) {
|
|
|
152
803
|
const rigor = option(args, "--rigor") ?? "lite";
|
|
153
804
|
if (rigor !== "lite" && rigor !== "full")
|
|
154
805
|
throw new Error("--rigor must be lite or full");
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
fs.writeFileSync(verify, verifySeed(taskLocale), "utf8");
|
|
806
|
+
const artifacts = new Map([
|
|
807
|
+
[
|
|
808
|
+
"prd.md",
|
|
809
|
+
prdTemplate
|
|
810
|
+
.replaceAll("{title}", title)
|
|
811
|
+
.replaceAll("{goal}", description || (taskLocale === "zh" ? "待补充。" : "TBD.")),
|
|
812
|
+
],
|
|
813
|
+
["verify.md", verifySeed(taskLocale)],
|
|
814
|
+
]);
|
|
165
815
|
if (fs.existsSync(path.join(root, ".codex"))) {
|
|
166
816
|
for (const name of CONTEXT_FILES) {
|
|
167
|
-
|
|
168
|
-
if (!fs.existsSync(file))
|
|
169
|
-
fs.writeFileSync(file, `${contextSeeds.map((entry) => JSON.stringify({ file: entry, reason: "default spec seed — curate entries for this task" })).join("\n")}\n`, "utf8");
|
|
817
|
+
artifacts.set(name, `${contextSeeds.map((entry) => JSON.stringify({ file: entry, reason: "default spec seed — curate entries for this task" })).join("\n")}\n`);
|
|
170
818
|
}
|
|
171
819
|
}
|
|
820
|
+
createTaskWithArtifacts({
|
|
821
|
+
root,
|
|
822
|
+
dirName,
|
|
823
|
+
record,
|
|
824
|
+
artifacts,
|
|
825
|
+
actor: "pactile task create",
|
|
826
|
+
idempotencyKey: `create:${slug}`,
|
|
827
|
+
evidence: "pactile task create",
|
|
828
|
+
ifExists: "error",
|
|
829
|
+
extras: { required_controls: resolveRequiredControls({ rigor }) },
|
|
830
|
+
});
|
|
172
831
|
if (parentRef)
|
|
173
832
|
linkChild(root, parentRef, dirName);
|
|
174
833
|
runTaskHooks(root, "after_create", path.join(dir, "task.json"));
|
|
@@ -185,19 +844,29 @@ function linkChild(root, parentRef, childRef) {
|
|
|
185
844
|
throw new Error("parent and child need task.json");
|
|
186
845
|
if (child.parent && child.parent !== path.basename(parentDir))
|
|
187
846
|
throw new Error(`single-parent tree: child already belongs to ${child.parent}`);
|
|
188
|
-
if (parent.parent === path.basename(childDir) ||
|
|
847
|
+
if (parent.parent === path.basename(childDir) ||
|
|
848
|
+
child.children.includes(path.basename(parentDir)))
|
|
189
849
|
throw new Error("parent-child cycle rejected");
|
|
190
850
|
if (parent.status !== "planning" || child.status !== "planning")
|
|
191
851
|
throw new Error("link children while both tasks are planning");
|
|
192
|
-
if (parent.children.includes(path.basename(childDir)) &&
|
|
852
|
+
if (parent.children.includes(path.basename(childDir)) &&
|
|
853
|
+
child.parent === path.basename(parentDir))
|
|
193
854
|
return;
|
|
194
|
-
const nextChildren = [
|
|
855
|
+
const nextChildren = [
|
|
856
|
+
...new Set([...parent.children, path.basename(childDir)]),
|
|
857
|
+
];
|
|
195
858
|
patchTask(root, path.basename(parentDir), { children: nextChildren }, "add-subtask");
|
|
196
859
|
try {
|
|
197
860
|
patchTask(root, path.basename(childDir), { parent: path.basename(parentDir) }, "add-subtask");
|
|
198
861
|
}
|
|
199
862
|
catch (error) {
|
|
200
|
-
patchTask(root, path.basename(parentDir), { children: parent.children }, "add-subtask-rollback", {
|
|
863
|
+
patchTask(root, path.basename(parentDir), { children: parent.children }, "add-subtask-rollback", {
|
|
864
|
+
topology: {
|
|
865
|
+
...readTopology(readKernel({ taskDir: parentDir, cwd: root }).kernel.projection
|
|
866
|
+
?.extras ?? {}),
|
|
867
|
+
children: parent.children,
|
|
868
|
+
},
|
|
869
|
+
});
|
|
201
870
|
throw error;
|
|
202
871
|
}
|
|
203
872
|
const nextParent = taskRecord(parentDir);
|
|
@@ -212,16 +881,23 @@ function unlinkChild(root, parentRef, childRef) {
|
|
|
212
881
|
const child = taskRecord(childDir);
|
|
213
882
|
if (!parent || !child)
|
|
214
883
|
throw new Error("parent and child need task.json");
|
|
215
|
-
if (!parent.children.includes(path.basename(childDir)) ||
|
|
884
|
+
if (!parent.children.includes(path.basename(childDir)) ||
|
|
885
|
+
child.parent !== path.basename(parentDir))
|
|
216
886
|
throw new Error("parent-child link not found");
|
|
217
887
|
const map = readTaskMap(parentDir).data;
|
|
218
888
|
const entry = map?.children.find((item) => item.id === path.basename(childDir));
|
|
219
|
-
if (parent.status !== "planning" ||
|
|
889
|
+
if (parent.status !== "planning" ||
|
|
890
|
+
child.status !== "planning" ||
|
|
891
|
+
(entry && entry.state !== "open"))
|
|
220
892
|
throw new Error("unlink only an open Child while both tasks are planning");
|
|
221
893
|
const nextChildren = parent.children.filter((id) => id !== path.basename(childDir));
|
|
222
|
-
const parentTopology = readTopology(readKernel({ taskDir: parentDir, cwd: root }).kernel.projection?.extras ??
|
|
223
|
-
|
|
224
|
-
|
|
894
|
+
const parentTopology = readTopology(readKernel({ taskDir: parentDir, cwd: root }).kernel.projection?.extras ??
|
|
895
|
+
{});
|
|
896
|
+
const childTopology = readTopology(readKernel({ taskDir: childDir, cwd: root }).kernel.projection?.extras ??
|
|
897
|
+
{});
|
|
898
|
+
patchTask(root, path.basename(childDir), { parent: null }, "remove-subtask", {
|
|
899
|
+
topology: { ...childTopology, parent_id: null },
|
|
900
|
+
});
|
|
225
901
|
patchTask(root, path.basename(parentDir), { children: nextChildren }, "remove-subtask", { topology: { ...parentTopology, children: nextChildren } });
|
|
226
902
|
const nextParent = taskRecord(parentDir);
|
|
227
903
|
if (nextParent)
|
|
@@ -235,7 +911,8 @@ function prepareChildWorktree(root, args) {
|
|
|
235
911
|
const child = taskRecord(childDir);
|
|
236
912
|
if (!parent || !child)
|
|
237
913
|
throw new Error("Parent and Child need task.json");
|
|
238
|
-
if (child.parent !== path.basename(parentDir) ||
|
|
914
|
+
if (child.parent !== path.basename(parentDir) ||
|
|
915
|
+
!parent.children.includes(path.basename(childDir)))
|
|
239
916
|
throw new Error("Child must be linked to the Parent");
|
|
240
917
|
const map = readTaskMap(parentDir);
|
|
241
918
|
if (!map.data?.children.some((entry) => entry.id === path.basename(childDir)))
|
|
@@ -245,9 +922,14 @@ function prepareChildWorktree(root, args) {
|
|
|
245
922
|
const destination = path.resolve(root, option(args, "--path") ?? path.join(worktreeRoot, path.basename(childDir)));
|
|
246
923
|
const relative = path.relative(worktreeRoot, destination);
|
|
247
924
|
const errors = [];
|
|
248
|
-
if (!relative ||
|
|
925
|
+
if (!relative ||
|
|
926
|
+
relative === ".." ||
|
|
927
|
+
relative.startsWith(`..${path.sep}`) ||
|
|
928
|
+
path.isAbsolute(relative))
|
|
249
929
|
errors.push("worktree path must stay under .pactile/worktrees");
|
|
250
|
-
if (fs
|
|
930
|
+
if (fs
|
|
931
|
+
.lstatSync(path.join(root, ".pactile"), { throwIfNoEntry: false })
|
|
932
|
+
?.isSymbolicLink())
|
|
251
933
|
errors.push(".pactile must not be a symlink");
|
|
252
934
|
if (fs.lstatSync(worktreeRoot, { throwIfNoEntry: false })?.isSymbolicLink())
|
|
253
935
|
errors.push(".pactile/worktrees must not be a symlink");
|
|
@@ -263,7 +945,11 @@ function prepareChildWorktree(root, args) {
|
|
|
263
945
|
errors.push("invalid branch name");
|
|
264
946
|
if (typeof base !== "string" || base.startsWith("-") || /[\r\n]/.test(base))
|
|
265
947
|
errors.push("invalid base ref");
|
|
266
|
-
const git = (...gitArgs) => execFileSync("git", gitArgs, {
|
|
948
|
+
const git = (...gitArgs) => execFileSync("git", gitArgs, {
|
|
949
|
+
cwd: root,
|
|
950
|
+
encoding: "utf8",
|
|
951
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
952
|
+
}).trim();
|
|
267
953
|
try {
|
|
268
954
|
if (!sameGitRoot(git("rev-parse", "--show-toplevel"), root))
|
|
269
955
|
errors.push("prepare-child-worktree requires the project Git root");
|
|
@@ -289,7 +975,9 @@ function prepareChildWorktree(root, args) {
|
|
|
289
975
|
git("rev-parse", "--verify", `refs/heads/${branch}`);
|
|
290
976
|
branchExists = true;
|
|
291
977
|
}
|
|
292
|
-
catch {
|
|
978
|
+
catch {
|
|
979
|
+
/* New local branch. */
|
|
980
|
+
}
|
|
293
981
|
try {
|
|
294
982
|
git("worktree", "add", ...(branchExists ? [] : ["-b", branch]), destination, ...(branchExists ? [branch] : [base]));
|
|
295
983
|
}
|
|
@@ -301,7 +989,13 @@ function prepareChildWorktree(root, args) {
|
|
|
301
989
|
const entry = map.data?.children.find((item) => item.id === path.basename(childDir));
|
|
302
990
|
if (!map.data || !entry)
|
|
303
991
|
throw new Error("Child vanished from Parent task-map.md after worktree creation");
|
|
304
|
-
Object.assign(entry, {
|
|
992
|
+
Object.assign(entry, {
|
|
993
|
+
isolation: "git-worktree",
|
|
994
|
+
branch,
|
|
995
|
+
worktree_path: worktreePath,
|
|
996
|
+
base_ref: base,
|
|
997
|
+
ref: `refs/heads/${branch}`,
|
|
998
|
+
});
|
|
305
999
|
writeTaskMap(parentDir, map.data, map.body, `Prepared Child ${entry.id} git worktree at ${worktreePath} from ${base}.`);
|
|
306
1000
|
console.log(`Child worktree prepared: ${entry.id}\nBranch: ${branch}\nPath: ${worktreePath}`);
|
|
307
1001
|
return 0;
|
|
@@ -312,8 +1006,11 @@ function updateChildState(root, args, integrated) {
|
|
|
312
1006
|
const state = requireArgument(args[2], "state");
|
|
313
1007
|
if (!CHILD_STATES.includes(state))
|
|
314
1008
|
throw new Error(`unknown child state: ${state}`);
|
|
315
|
-
if (integrated !==
|
|
316
|
-
|
|
1009
|
+
if (integrated !==
|
|
1010
|
+
["changes", "accepted", "integrating", "integrated", "cancelled"].includes(state))
|
|
1011
|
+
throw new Error(integrated
|
|
1012
|
+
? "use a Parent-controlled integration state"
|
|
1013
|
+
: "use integrate-child for Parent-controlled states");
|
|
317
1014
|
const parent = taskRecord(parentDir);
|
|
318
1015
|
const child = taskRecord(childDir);
|
|
319
1016
|
if (!parent || !child)
|
|
@@ -325,12 +1022,15 @@ function updateChildState(root, args, integrated) {
|
|
|
325
1022
|
const reason = option(args, "--reason");
|
|
326
1023
|
const errors = childStateErrors(parentDir, parent, childDir, state, evidence, { ref, reason });
|
|
327
1024
|
if (integrated && ["accepted", "integrating", "integrated"].includes(state)) {
|
|
328
|
-
const extras = readKernel({ taskDir: childDir, cwd: root }).kernel.projection?.extras ??
|
|
329
|
-
|
|
1025
|
+
const extras = readKernel({ taskDir: childDir, cwd: root }).kernel.projection?.extras ??
|
|
1026
|
+
{};
|
|
1027
|
+
if (extras.required_controls
|
|
1028
|
+
?.rigor === "full") {
|
|
330
1029
|
const parsed = readStrategyContract(childDir);
|
|
331
1030
|
errors.push(...parsed.errors);
|
|
332
1031
|
if (parsed.contract)
|
|
333
|
-
errors.push(...currentGateErrors(childDir, child, "child-review", requiredGates("child-review", parsed.contract), parsed.contract, readKernel({ taskDir: childDir, cwd: root }).kernel
|
|
1032
|
+
errors.push(...currentGateErrors(childDir, child, "child-review", requiredGates("child-review", parsed.contract), parsed.contract, readKernel({ taskDir: childDir, cwd: root }).kernel
|
|
1033
|
+
.gates));
|
|
334
1034
|
try {
|
|
335
1035
|
assertFullQualityForPhase(childDir, extras, "archive");
|
|
336
1036
|
}
|
|
@@ -345,7 +1045,11 @@ function updateChildState(root, args, integrated) {
|
|
|
345
1045
|
errors.push("--execute-merge is only valid with integrate-child ... integrated");
|
|
346
1046
|
if (!ref || ref.startsWith("-") || /[\r\n]/.test(ref))
|
|
347
1047
|
errors.push("--execute-merge requires a valid --ref");
|
|
348
|
-
const git = (...gitArgs) => execFileSync("git", gitArgs, {
|
|
1048
|
+
const git = (...gitArgs) => execFileSync("git", gitArgs, {
|
|
1049
|
+
cwd: root,
|
|
1050
|
+
encoding: "utf8",
|
|
1051
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1052
|
+
}).trim();
|
|
349
1053
|
try {
|
|
350
1054
|
if (!sameGitRoot(git("rev-parse", "--show-toplevel"), root))
|
|
351
1055
|
errors.push("merge execution requires the project Git root");
|
|
@@ -356,7 +1060,9 @@ function updateChildState(root, args, integrated) {
|
|
|
356
1060
|
errors.push(`merge ref does not resolve to a commit: ${ref ?? "(missing)"}`);
|
|
357
1061
|
}
|
|
358
1062
|
try {
|
|
359
|
-
const dirty = git("status", "--porcelain", "--untracked-files=all")
|
|
1063
|
+
const dirty = git("status", "--porcelain", "--untracked-files=all")
|
|
1064
|
+
.split(/\r?\n/)
|
|
1065
|
+
.filter(Boolean)
|
|
360
1066
|
.map((line) => line.slice(3).trim().replaceAll("\\", "/").replace(/^"|"$/g, ""))
|
|
361
1067
|
.filter((file) => file !== ".pactile" && !file.startsWith(".pactile/"));
|
|
362
1068
|
if (dirty.length)
|
|
@@ -376,7 +1082,10 @@ function updateChildState(root, args, integrated) {
|
|
|
376
1082
|
}
|
|
377
1083
|
if (executeMerge) {
|
|
378
1084
|
try {
|
|
379
|
-
execFileSync("git", ["merge", "--no-ff", "--no-commit", ref], {
|
|
1085
|
+
execFileSync("git", ["merge", "--no-ff", "--no-commit", ref], {
|
|
1086
|
+
cwd: root,
|
|
1087
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1088
|
+
});
|
|
380
1089
|
}
|
|
381
1090
|
catch (error) {
|
|
382
1091
|
console.error(`git merge failed; Parent task-map was not advanced to integrated: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -399,9 +1108,17 @@ function updateChildState(root, args, integrated) {
|
|
|
399
1108
|
data.integration_queue.push(entry.id);
|
|
400
1109
|
else if (state !== "integrating")
|
|
401
1110
|
data.integration_queue = data.integration_queue.filter((id) => id !== entry.id);
|
|
402
|
-
const previous = readKernel({ taskDir: parentDir, cwd: root }).kernel.projection?.extras ??
|
|
403
|
-
|
|
404
|
-
|
|
1111
|
+
const previous = readKernel({ taskDir: parentDir, cwd: root }).kernel.projection?.extras ??
|
|
1112
|
+
{};
|
|
1113
|
+
const states = previous.task_map_states && typeof previous.task_map_states === "object"
|
|
1114
|
+
? previous.task_map_states
|
|
1115
|
+
: {};
|
|
1116
|
+
patchTask(root, path.basename(parentDir), {}, integrated ? "integrate-child" : "set-child-state", {
|
|
1117
|
+
task_map_states: { ...states, [entry.id]: state },
|
|
1118
|
+
...(integrated
|
|
1119
|
+
? { integration_action: "integrate-child", integration_actor: "parent" }
|
|
1120
|
+
: {}),
|
|
1121
|
+
});
|
|
405
1122
|
writeTaskMap(parentDir, data, body, `${integrated ? "Parent integrated" : "Child reported"} ${entry.id} as ${state}. Evidence: ${evidence}.`);
|
|
406
1123
|
console.log(`${entry.id}: ${state}`);
|
|
407
1124
|
return 0;
|
|
@@ -411,14 +1128,16 @@ function reviewChild(root, args) {
|
|
|
411
1128
|
const childDir = taskDir(root, requireArgument(args[1], "child task"));
|
|
412
1129
|
const parent = taskRecord(parentDir);
|
|
413
1130
|
const child = taskRecord(childDir);
|
|
414
|
-
if (child?.parent !== path.basename(parentDir) ||
|
|
1131
|
+
if (child?.parent !== path.basename(parentDir) ||
|
|
1132
|
+
!parent?.children.includes(path.basename(childDir)))
|
|
415
1133
|
throw new Error("linked Parent and Child task.json are required");
|
|
416
1134
|
const map = readTaskMap(parentDir).data;
|
|
417
1135
|
const entry = map?.children.find((item) => item.id === path.basename(childDir));
|
|
418
1136
|
if (!entry)
|
|
419
1137
|
throw new Error("Child missing from Parent task-map.md");
|
|
420
1138
|
const decision = option(args, "--decision");
|
|
421
|
-
if (decision &&
|
|
1139
|
+
if (decision &&
|
|
1140
|
+
!["accept", "changes", "cancel", "integrate-through"].includes(decision))
|
|
422
1141
|
throw new Error("--decision must be accept, changes, cancel, or integrate-through");
|
|
423
1142
|
const check = args.includes("--check") || !decision;
|
|
424
1143
|
const ref = option(args, "--ref");
|
|
@@ -426,40 +1145,86 @@ function reviewChild(root, args) {
|
|
|
426
1145
|
const notes = option(args, "--notes");
|
|
427
1146
|
const verifyFile = path.join(childDir, "verify.md");
|
|
428
1147
|
const handoffFile = path.join(childDir, "handoff.md");
|
|
429
|
-
const verify = fs.existsSync(verifyFile)
|
|
430
|
-
|
|
431
|
-
|
|
1148
|
+
const verify = fs.existsSync(verifyFile)
|
|
1149
|
+
? fs.readFileSync(verifyFile, "utf8")
|
|
1150
|
+
: "";
|
|
1151
|
+
const handoff = fs.existsSync(handoffFile)
|
|
1152
|
+
? fs.readFileSync(handoffFile, "utf8")
|
|
1153
|
+
: "";
|
|
1154
|
+
const excerpt = (content) => content
|
|
1155
|
+
.slice(0, 800)
|
|
1156
|
+
.trim()
|
|
1157
|
+
.split(/\r?\n/)
|
|
1158
|
+
.map((line) => `> ${line}`)
|
|
1159
|
+
.join("\n") || "> (empty)";
|
|
432
1160
|
const report = [
|
|
433
|
-
`# Parent review — \`${path.basename(childDir)}\``,
|
|
434
|
-
|
|
435
|
-
|
|
1161
|
+
`# Parent review — \`${path.basename(childDir)}\``,
|
|
1162
|
+
"",
|
|
1163
|
+
`- Timestamp: ${new Date().toISOString()}`,
|
|
1164
|
+
`- Current integration state: \`${entry.state}\``,
|
|
1165
|
+
`- Decision: \`${check ? "check-only" : decision}\``,
|
|
1166
|
+
"",
|
|
1167
|
+
"## Handoff artifacts",
|
|
1168
|
+
"",
|
|
1169
|
+
`- verify.md: ${verify.trim() ? "present" : "missing"}`,
|
|
436
1170
|
`- handoff.md: ${handoff.trim() ? "present" : "missing"}`,
|
|
437
1171
|
`- validation signal: ${/^\s*(?:[-*]\s*)?validation(?:\s+(?:commands?|results?|evidence))?\s*:\s*\S.{2,}$/im.test(verify)}`,
|
|
438
1172
|
`- acceptance signal: ${/^\s*(?:[-*]\s*)?(?:(?:final|user)\s+)?acceptance(?:\s+evidence)?\s*:\s*\S.{2,}$/im.test(verify)}`,
|
|
439
|
-
`- durable learning decision signal: ${/durable learning decision\s*:/i.test(verify)}`,
|
|
1173
|
+
`- durable learning decision signal: ${/durable learning decision\s*:/i.test(verify)}`,
|
|
1174
|
+
"",
|
|
440
1175
|
...(notes ? ["## Parent notes", "", excerpt(notes), ""] : []),
|
|
441
|
-
"## verify.md excerpt",
|
|
442
|
-
"
|
|
1176
|
+
"## verify.md excerpt",
|
|
1177
|
+
"",
|
|
1178
|
+
excerpt(verify),
|
|
1179
|
+
"",
|
|
1180
|
+
"## handoff.md excerpt",
|
|
1181
|
+
"",
|
|
1182
|
+
excerpt(handoff),
|
|
1183
|
+
"",
|
|
443
1184
|
].join("\n");
|
|
444
1185
|
if (check) {
|
|
445
1186
|
const errors = [
|
|
446
|
-
...(entry.state !== "review"
|
|
1187
|
+
...(entry.state !== "review"
|
|
1188
|
+
? [`Child must be in review state, got ${entry.state}`]
|
|
1189
|
+
: []),
|
|
447
1190
|
...(!verify.trim() ? ["verify.md missing"] : []),
|
|
448
1191
|
...(!handoff.trim() ? ["handoff.md missing"] : []),
|
|
449
1192
|
];
|
|
450
1193
|
console.log(`Review-child check: ${errors.length ? "FAIL" : "PASS"}\n${errors.map((error) => ` - ${error}`).join("\n")}\n\n${report}`);
|
|
451
1194
|
return errors.length ? 1 : 0;
|
|
452
1195
|
}
|
|
453
|
-
const state = decision === "accept" || decision === "integrate-through"
|
|
454
|
-
|
|
1196
|
+
const state = decision === "accept" || decision === "integrate-through"
|
|
1197
|
+
? "accepted"
|
|
1198
|
+
: decision === "changes"
|
|
1199
|
+
? "changes"
|
|
1200
|
+
: "cancelled";
|
|
1201
|
+
const transition = (target, evidence) => updateChildState(root, [
|
|
1202
|
+
path.basename(parentDir),
|
|
1203
|
+
path.basename(childDir),
|
|
1204
|
+
target,
|
|
1205
|
+
"--evidence",
|
|
1206
|
+
evidence,
|
|
1207
|
+
...(ref ? ["--ref", ref] : []),
|
|
1208
|
+
...(reason ? ["--reason", reason] : []),
|
|
1209
|
+
], true);
|
|
455
1210
|
// Validate the first transition before writing any review artifact or Parent evidence.
|
|
456
|
-
const firstCheck = updateChildState(root, [
|
|
1211
|
+
const firstCheck = updateChildState(root, [
|
|
1212
|
+
path.basename(parentDir),
|
|
1213
|
+
path.basename(childDir),
|
|
1214
|
+
state,
|
|
1215
|
+
"--evidence",
|
|
1216
|
+
"handoff.md",
|
|
1217
|
+
...(ref ? ["--ref", ref] : []),
|
|
1218
|
+
...(reason ? ["--reason", reason] : []),
|
|
1219
|
+
"--check",
|
|
1220
|
+
], true);
|
|
457
1221
|
if (firstCheck !== 0)
|
|
458
1222
|
return firstCheck;
|
|
459
1223
|
if (transition(state, "handoff.md") !== 0)
|
|
460
1224
|
return 1;
|
|
461
1225
|
if (decision === "integrate-through") {
|
|
462
|
-
if (transition("integrating", "task-map.md") !== 0 ||
|
|
1226
|
+
if (transition("integrating", "task-map.md") !== 0 ||
|
|
1227
|
+
transition("integrated", "task-map.md") !== 0)
|
|
463
1228
|
return 1;
|
|
464
1229
|
}
|
|
465
1230
|
const reviewFile = path.join(parentDir, `review-${path.basename(childDir)}.md`);
|
|
@@ -467,13 +1232,90 @@ function reviewChild(root, args) {
|
|
|
467
1232
|
fs.writeFileSync(reviewFile, `${report}\n`, "utf8");
|
|
468
1233
|
if (!args.includes("--no-append-parent-verify")) {
|
|
469
1234
|
const parentVerify = path.join(parentDir, "verify.md");
|
|
470
|
-
const current = fs.existsSync(parentVerify)
|
|
1235
|
+
const current = fs.existsSync(parentVerify)
|
|
1236
|
+
? fs.readFileSync(parentVerify, "utf8")
|
|
1237
|
+
: "# Verification Evidence\n";
|
|
471
1238
|
const note = `## Parent review — \`${path.basename(childDir)}\`\n\n- Decision: ${decision}\n- Child handoff: ${path.basename(childDir)}/handoff.md\n- Child verification: ${path.basename(childDir)}/verify.md\n- Review artifact: ${args.includes("--write-artifact") ? path.basename(reviewFile) : "not written"}\n`;
|
|
472
1239
|
fs.writeFileSync(parentVerify, `${current.trimEnd()}\n\n${note}`, "utf8");
|
|
473
1240
|
}
|
|
474
1241
|
console.log(report);
|
|
475
1242
|
return 0;
|
|
476
1243
|
}
|
|
1244
|
+
function showTask(root, args) {
|
|
1245
|
+
const reference = requireArgument(args[0], "task");
|
|
1246
|
+
const dir = taskDir(root, reference);
|
|
1247
|
+
const migration = readLegacyTaskImportRecord(root, dir);
|
|
1248
|
+
if (migration && migration.status !== "imported") {
|
|
1249
|
+
const status = migration.status;
|
|
1250
|
+
const details = {
|
|
1251
|
+
status,
|
|
1252
|
+
taskId: migration.legacyTaskId,
|
|
1253
|
+
missingDefinitionFields: migration.missingDefinitionFields,
|
|
1254
|
+
coordinationReasons: migration.coordinationReasons,
|
|
1255
|
+
hardDependencies: migration.dependencyFacts.hardDependencies,
|
|
1256
|
+
sourceFingerprint: migration.sourceFingerprint,
|
|
1257
|
+
};
|
|
1258
|
+
if (args.includes("--json"))
|
|
1259
|
+
console.log(JSON.stringify(details, null, 2));
|
|
1260
|
+
else
|
|
1261
|
+
console.log([
|
|
1262
|
+
`${migration.legacyTaskId} (legacy migration: ${status})`,
|
|
1263
|
+
migration.missingDefinitionFields.length
|
|
1264
|
+
? `Definition fields to supply: ${migration.missingDefinitionFields.join(", ")}`
|
|
1265
|
+
: null,
|
|
1266
|
+
migration.coordinationReasons.length
|
|
1267
|
+
? `Dependency coordination: ${migration.coordinationReasons.join(", ")}`
|
|
1268
|
+
: null,
|
|
1269
|
+
"No runnable V2 Task Kernel was created; original Task, Kernel, and documents remain preserved.",
|
|
1270
|
+
]
|
|
1271
|
+
.filter((line) => line !== null)
|
|
1272
|
+
.join("\n"));
|
|
1273
|
+
return 0;
|
|
1274
|
+
}
|
|
1275
|
+
if (!fs.existsSync(path.join(dir, "kernel.json")) && !migration) {
|
|
1276
|
+
const record = taskRecord(dir);
|
|
1277
|
+
if (!record)
|
|
1278
|
+
throw new Error(`No readable legacy task record exists at ${dir}`);
|
|
1279
|
+
console.log(args.includes("--json")
|
|
1280
|
+
? JSON.stringify({ schemaVersion: 0, task: record }, null, 2)
|
|
1281
|
+
: `${record.title || record.name} (${record.id})\nLegacy task.json record without Kernel\nStatus: ${record.status}\nAutomatic migration: reserved for P36.`);
|
|
1282
|
+
return 0;
|
|
1283
|
+
}
|
|
1284
|
+
const read = readTaskKernel({ root, taskDir: dir, cwd: root });
|
|
1285
|
+
if (args.includes("--json")) {
|
|
1286
|
+
console.log(JSON.stringify(read.kind === "task-kernel-v2"
|
|
1287
|
+
? read.kernel
|
|
1288
|
+
: { ...read.kernel, task: taskRecord(dir) }, null, 2));
|
|
1289
|
+
return 0;
|
|
1290
|
+
}
|
|
1291
|
+
if (read.kind === "task-kernel-v2") {
|
|
1292
|
+
const { definition, runs, reviews, phase, condition, revision } = read.kernel;
|
|
1293
|
+
console.log([
|
|
1294
|
+
`${definition.title} (${definition.taskId})`,
|
|
1295
|
+
`Kernel: v2 @ revision ${revision}`,
|
|
1296
|
+
`Phase: ${phase} | condition: ${condition}`,
|
|
1297
|
+
`Deliverable: ${definition.deliverable}`,
|
|
1298
|
+
`Delivery level: ${definition.deliveryLevel}`,
|
|
1299
|
+
`Dependencies: ${definition.dependencies.length ? definition.dependencies.join(", ") : "none"}`,
|
|
1300
|
+
"Acceptance criteria:",
|
|
1301
|
+
...definition.acceptanceCriteria.map((criterion) => ` - ${criterion.id}: ${criterion.description}`),
|
|
1302
|
+
`Runs: ${runs.length} | Reviews: ${reviews.length}`,
|
|
1303
|
+
].join("\n"));
|
|
1304
|
+
return 0;
|
|
1305
|
+
}
|
|
1306
|
+
const record = taskRecord(dir);
|
|
1307
|
+
console.log(`${record?.title ?? record?.id ?? path.basename(dir)} (legacy Kernel v1)\nPhase: ${read.kernel.kernel.phase}\nStatus: ${record?.status ?? "unknown"}\nAutomatic migration: reserved for P36.`);
|
|
1308
|
+
return 0;
|
|
1309
|
+
}
|
|
1310
|
+
function activeV2Tasks(root) {
|
|
1311
|
+
const archivePrefix = `${path.resolve(root, ".pactile", "tasks", "archive")}${path.sep}`.toLowerCase();
|
|
1312
|
+
return listTaskKernelSnapshots(root)
|
|
1313
|
+
.filter(({ taskDir: dir }) => !path.resolve(dir).toLowerCase().startsWith(archivePrefix))
|
|
1314
|
+
.map(({ taskDir: dir, kernel }) => ({ dir: path.basename(dir), kernel }));
|
|
1315
|
+
}
|
|
1316
|
+
function v2DisplayStatus(kernel) {
|
|
1317
|
+
return kernel.phase === "close" ? "closed" : kernel.phase;
|
|
1318
|
+
}
|
|
477
1319
|
function listTasks(args, root) {
|
|
478
1320
|
const developer = readDeveloper(root);
|
|
479
1321
|
const mine = args.includes("--mine") || args.includes("-m");
|
|
@@ -482,16 +1324,67 @@ function listTasks(args, root) {
|
|
|
482
1324
|
const assignee = option(args, "--assignee") ?? (mine ? developer : undefined);
|
|
483
1325
|
const status = option(args, "--status") ?? option(args, "-s");
|
|
484
1326
|
const selected = resolveSelectedTask(root).taskPath;
|
|
485
|
-
const
|
|
1327
|
+
const phaseForStatus = {
|
|
1328
|
+
planning: "define",
|
|
1329
|
+
in_progress: "execute",
|
|
1330
|
+
review: "verify",
|
|
1331
|
+
completed: "close",
|
|
1332
|
+
closed: "close",
|
|
1333
|
+
};
|
|
1334
|
+
const requiredPhase = status ? (phaseForStatus[status] ?? status) : undefined;
|
|
1335
|
+
const migrationRecords = listLegacyTaskImportRecords(root);
|
|
1336
|
+
const restoredSourcePaths = new Set(migrationRecords.flatMap(({ record }) => {
|
|
1337
|
+
if (record.status !== "imported")
|
|
1338
|
+
return [];
|
|
1339
|
+
const taskJsonPath = record.legacySourceMetadata?.fileReferences.taskJson?.path;
|
|
1340
|
+
if (!taskJsonPath?.startsWith(".pactile/tasks/") ||
|
|
1341
|
+
!taskJsonPath.endsWith("/task.json"))
|
|
1342
|
+
return [];
|
|
1343
|
+
const sourceTaskPath = taskJsonPath.slice(0, -"/task.json".length);
|
|
1344
|
+
return sourceTaskPath === record.taskPath
|
|
1345
|
+
? []
|
|
1346
|
+
: [sourceTaskPath.slice(".pactile/tasks/".length)];
|
|
1347
|
+
}));
|
|
1348
|
+
const migrationByDirectory = new Map(migrationRecords.map((item) => [path.resolve(item.taskDir), item.record]));
|
|
1349
|
+
const tasks = activeTasks(root).filter(({ dir, record }) => !migrationByDirectory.has(path.resolve(root, ".pactile", "tasks", dir)) &&
|
|
1350
|
+
(!assignee || record.assignee === assignee) &&
|
|
1351
|
+
(!status || record.status === status));
|
|
1352
|
+
const pendingImports = migrationRecords.filter(({ taskDir, record }) => {
|
|
1353
|
+
if (record.status === "imported" ||
|
|
1354
|
+
record.status === "archived-historical-only" ||
|
|
1355
|
+
restoredSourcePaths.has(path
|
|
1356
|
+
.relative(path.join(root, ".pactile", "tasks"), taskDir)
|
|
1357
|
+
.replaceAll("\\", "/")))
|
|
1358
|
+
return false;
|
|
1359
|
+
const raw = taskRecord(taskDir);
|
|
1360
|
+
return ((!assignee || raw?.assignee === assignee) &&
|
|
1361
|
+
(!status || raw?.status === status));
|
|
1362
|
+
});
|
|
1363
|
+
const v2Tasks = activeV2Tasks(root).filter(({ kernel }) => (!assignee || kernel.definition.createdBy === assignee) &&
|
|
1364
|
+
(!requiredPhase || kernel.phase === requiredPhase));
|
|
486
1365
|
console.log(assignee ? `Tasks (assignee: ${assignee}):` : "All active tasks:");
|
|
487
1366
|
console.log();
|
|
488
1367
|
for (const { dir, record } of tasks) {
|
|
489
1368
|
const marker = `.pactile/tasks/${dir}` === selected ? " <- selected" : "";
|
|
490
1369
|
console.log(` - ${dir}/ (${record.status})${record.package ? ` @${record.package}` : ""} [${record.assignee || "-"}]${marker}`);
|
|
491
1370
|
}
|
|
492
|
-
|
|
1371
|
+
for (const { dir, kernel } of v2Tasks) {
|
|
1372
|
+
const marker = `.pactile/tasks/${dir}` === selected ? " <- selected" : "";
|
|
1373
|
+
console.log(` - ${dir}/ (${v2DisplayStatus(kernel)}; Kernel v2) [${kernel.definition.createdBy}]${marker}`);
|
|
1374
|
+
}
|
|
1375
|
+
for (const { taskDir: importedDir, record } of pendingImports) {
|
|
1376
|
+
const dir = path
|
|
1377
|
+
.relative(path.join(root, ".pactile", "tasks"), importedDir)
|
|
1378
|
+
.replaceAll("\\", "/");
|
|
1379
|
+
const marker = `.pactile/tasks/${dir}` === selected ? " <- selected" : "";
|
|
1380
|
+
const detail = record.status === "needs-definition"
|
|
1381
|
+
? `needs definition: ${record.missingDefinitionFields.join(", ")}`
|
|
1382
|
+
: `needs dependency coordination: ${record.coordinationReasons.join(", ")}`;
|
|
1383
|
+
console.log(` - ${dir}/ (${detail}; not runnable)${marker}`);
|
|
1384
|
+
}
|
|
1385
|
+
if (!tasks.length && !v2Tasks.length && !pendingImports.length)
|
|
493
1386
|
console.log(" (no active tasks)");
|
|
494
|
-
console.log(`\nTotal: ${tasks.length} task(s)`);
|
|
1387
|
+
console.log(`\nTotal: ${tasks.length + v2Tasks.length + pendingImports.length} task(s)`);
|
|
495
1388
|
}
|
|
496
1389
|
function listArchive(root, month) {
|
|
497
1390
|
if (month && !/^\d{4}-(?:0[1-9]|1[0-2])$/.test(month))
|
|
@@ -500,14 +1393,24 @@ function listArchive(root, month) {
|
|
|
500
1393
|
console.log("Archived tasks:\n");
|
|
501
1394
|
if (!fs.existsSync(archive))
|
|
502
1395
|
return;
|
|
503
|
-
const months = month
|
|
1396
|
+
const months = month
|
|
1397
|
+
? [month]
|
|
1398
|
+
: fs
|
|
1399
|
+
.readdirSync(archive, { withFileTypes: true })
|
|
1400
|
+
.filter((entry) => entry.isDirectory())
|
|
1401
|
+
.map((entry) => entry.name)
|
|
1402
|
+
.sort();
|
|
504
1403
|
for (const name of months) {
|
|
505
1404
|
const monthDir = path.join(archive, name);
|
|
506
1405
|
if (!fs.statSync(monthDir, { throwIfNoEntry: false })?.isDirectory()) {
|
|
507
1406
|
console.log(` No archives for ${name}`);
|
|
508
1407
|
continue;
|
|
509
1408
|
}
|
|
510
|
-
const tasks = fs
|
|
1409
|
+
const tasks = fs
|
|
1410
|
+
.readdirSync(monthDir, { withFileTypes: true })
|
|
1411
|
+
.filter((entry) => entry.isDirectory())
|
|
1412
|
+
.map((entry) => entry.name)
|
|
1413
|
+
.sort();
|
|
511
1414
|
console.log(`[${name}]${month ? "" : ` - ${tasks.length} task(s)`}`);
|
|
512
1415
|
if (month)
|
|
513
1416
|
for (const task of tasks)
|
|
@@ -519,9 +1422,14 @@ function dashboard(root) {
|
|
|
519
1422
|
console.log("Task Dashboard\nPactile framework: active");
|
|
520
1423
|
console.log(`Selected task: ${selected.taskPath ?? "none"}${selected.taskPath ? ` (${selected.source})` : ""}\n`);
|
|
521
1424
|
const tasks = activeTasks(root);
|
|
1425
|
+
const v2Tasks = activeV2Tasks(root);
|
|
522
1426
|
if (!tasks.length)
|
|
523
1427
|
console.log("Tasks: none");
|
|
524
|
-
for (const [status, heading] of [
|
|
1428
|
+
for (const [status, heading] of [
|
|
1429
|
+
["planning", "Define"],
|
|
1430
|
+
["in_progress", "Execute"],
|
|
1431
|
+
["review", "Verify"],
|
|
1432
|
+
]) {
|
|
525
1433
|
const matching = tasks.filter(({ record }) => record.status === status);
|
|
526
1434
|
if (!matching.length)
|
|
527
1435
|
continue;
|
|
@@ -530,10 +1438,20 @@ function dashboard(root) {
|
|
|
530
1438
|
console.log(` - .pactile/tasks/${dir} (${heading}) [${record.assignee || "-"}]`);
|
|
531
1439
|
console.log();
|
|
532
1440
|
}
|
|
533
|
-
|
|
1441
|
+
if (v2Tasks.length) {
|
|
1442
|
+
console.log("Task Kernel v2:");
|
|
1443
|
+
for (const { dir, kernel } of v2Tasks)
|
|
1444
|
+
console.log(` - .pactile/tasks/${dir} (${v2DisplayStatus(kernel)}) [${kernel.definition.deliveryLevel}]`);
|
|
1445
|
+
console.log();
|
|
1446
|
+
}
|
|
1447
|
+
console.log('Suggested actions:\n - Select a task: pactile task select <task>\n - Create a deliverable Task: pactile task create "<title>" --slug <slug> --deliverable <text> --delivery-level <level> --accept <criterion>\n - Inspect tasks: pactile task list | pactile task show <task>');
|
|
534
1448
|
}
|
|
535
1449
|
function startExecution(root, args) {
|
|
536
1450
|
const dir = taskDir(root, requireArgument(args[0], "task"));
|
|
1451
|
+
const migration = readLegacyTaskImportRecord(root, dir);
|
|
1452
|
+
if (migration) {
|
|
1453
|
+
throw new Error(`Task ${migration.legacyTaskId} has an active V2 migration record (${migration.status}); use the Task Kernel Run command. Legacy --ignore-deps and execution approvals do not carry over.`);
|
|
1454
|
+
}
|
|
537
1455
|
const record = taskRecord(dir);
|
|
538
1456
|
if (!record)
|
|
539
1457
|
throw new Error(`task.json not found at ${dir}`);
|
|
@@ -541,6 +1459,14 @@ function startExecution(root, args) {
|
|
|
541
1459
|
const approved = args.includes("--approved");
|
|
542
1460
|
if (check === approved)
|
|
543
1461
|
throw new Error("choose --check or --approved");
|
|
1462
|
+
const existing = readKernel({ taskDir: dir, cwd: root }).kernel;
|
|
1463
|
+
if (approved &&
|
|
1464
|
+
existing.phase === "execute" &&
|
|
1465
|
+
existing.projection?.status === "in_progress") {
|
|
1466
|
+
approvedExecuteTask(root, path.basename(dir));
|
|
1467
|
+
console.log(`Execution already approved for: ${path.relative(root, dir).replaceAll("\\", "/")}\nStatus: in_progress`);
|
|
1468
|
+
return 0;
|
|
1469
|
+
}
|
|
544
1470
|
const guard = checkStartExecution(root, dir, record, approved, args.includes("--ignore-deps"));
|
|
545
1471
|
for (const warning of guard.warnings)
|
|
546
1472
|
console.error(`[dependencies] WARN: ${warning}`);
|
|
@@ -554,63 +1480,58 @@ function startExecution(root, args) {
|
|
|
554
1480
|
return 0;
|
|
555
1481
|
}
|
|
556
1482
|
const prior = record;
|
|
557
|
-
const priorApproval = prior.execution_approval;
|
|
558
|
-
if (priorApproval && typeof priorApproval === "object") {
|
|
559
|
-
const old = priorApproval;
|
|
560
|
-
if (old.contract_fingerprint !== guard.contractFingerprint || old.artifact_fingerprint !== guard.artifactFingerprint) {
|
|
561
|
-
throw new Error("stale execution approval; return to Planning and ask for explicit approval again");
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
1483
|
const checkedAt = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
565
1484
|
const baseline = {
|
|
566
|
-
schema_version: 1,
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
1485
|
+
schema_version: 1,
|
|
1486
|
+
transition: "start-execution",
|
|
1487
|
+
gate: "baseline-check",
|
|
1488
|
+
result: "PASS",
|
|
1489
|
+
reviewer: "pactile-cli",
|
|
1490
|
+
evidence: "task.json",
|
|
1491
|
+
checked_at: checkedAt,
|
|
1492
|
+
contract_fingerprint: guard.contractFingerprint,
|
|
1493
|
+
artifact_fingerprint: guard.artifactFingerprint,
|
|
1494
|
+
issue_fingerprint: null,
|
|
1495
|
+
consecutive_failures: 0,
|
|
1496
|
+
approved_skip: null,
|
|
570
1497
|
};
|
|
571
1498
|
const existingQgr = prior.quality_gate_results && typeof prior.quality_gate_results === "object"
|
|
572
|
-
? prior.quality_gate_results
|
|
1499
|
+
? prior.quality_gate_results
|
|
1500
|
+
: {};
|
|
573
1501
|
const transitions = existingQgr.transitions && typeof existingQgr.transitions === "object"
|
|
574
|
-
? existingQgr.transitions
|
|
1502
|
+
? existingQgr.transitions
|
|
1503
|
+
: {};
|
|
575
1504
|
const qualityGateResults = {
|
|
576
1505
|
...existingQgr,
|
|
577
1506
|
schema_version: 1,
|
|
578
1507
|
contract_fingerprint: guard.contractFingerprint,
|
|
579
1508
|
artifact_fingerprint: guard.artifactFingerprint,
|
|
580
|
-
transitions: {
|
|
1509
|
+
transitions: {
|
|
1510
|
+
...transitions,
|
|
1511
|
+
"start-execution": {
|
|
1512
|
+
...(transitions["start-execution"] ?? {}),
|
|
1513
|
+
"baseline-check": baseline,
|
|
1514
|
+
},
|
|
1515
|
+
},
|
|
581
1516
|
};
|
|
582
1517
|
const approval = {
|
|
583
|
-
schema_version: 1,
|
|
584
|
-
|
|
585
|
-
|
|
1518
|
+
schema_version: 1,
|
|
1519
|
+
transition: "start-execution",
|
|
1520
|
+
approved_at: checkedAt,
|
|
1521
|
+
approved_by: "user",
|
|
1522
|
+
task_id: record.id,
|
|
1523
|
+
assurance: "caller-asserted",
|
|
1524
|
+
approval_source: "pactile task start-execution --approved",
|
|
1525
|
+
contract_fingerprint: guard.contractFingerprint,
|
|
1526
|
+
artifact_fingerprint: guard.artifactFingerprint,
|
|
1527
|
+
};
|
|
1528
|
+
const extras = {
|
|
1529
|
+
quality_gate_results: qualityGateResults,
|
|
1530
|
+
execution_approval: approval,
|
|
586
1531
|
};
|
|
587
|
-
const extras = { quality_gate_results: qualityGateResults, execution_approval: approval };
|
|
588
1532
|
const dependencyFacts = dependencyStatus(root, dir, record);
|
|
589
1533
|
const priorExtras = readKernel({ taskDir: dir, cwd: root }).kernel.projection?.extras ?? {};
|
|
590
|
-
|
|
591
|
-
? priorExtras.dependency_satisfied.filter((item) => typeof item === "string") : [];
|
|
592
|
-
extras.dependency_satisfied = [...new Set([...attestedSatisfied, ...dependencyFacts.satisfied])];
|
|
593
|
-
const controls = readKernel({ taskDir: dir, cwd: root }).kernel.projection?.extras?.required_controls;
|
|
594
|
-
if (controls && typeof controls === "object" && controls.rigor === "full") {
|
|
595
|
-
const parsed = readStrategyContract(dir);
|
|
596
|
-
if (!parsed.contract)
|
|
597
|
-
throw new Error(parsed.errors.join("; "));
|
|
598
|
-
const currentContract = contractFingerprint(dir, record, parsed.contract);
|
|
599
|
-
const autoGates = requiredGates("start-execution", parsed.contract);
|
|
600
|
-
for (const gate of autoGates) {
|
|
601
|
-
const stamp = new Date().toISOString();
|
|
602
|
-
const artifact = artifactFingerprint(dir, record, "start-execution", gate);
|
|
603
|
-
const gateRecord = { schema_version: 1, transition: "start-execution", gate, result: "PASS", reviewer: "pactile-cli", evidence: gate === "requirements-review" ? "prd.md" : "design.md + implement.md",
|
|
604
|
-
checked_at: stamp, contract_fingerprint: currentContract, artifact_fingerprint: artifact,
|
|
605
|
-
issue_fingerprint: null, consecutive_failures: 0, approved_skip: null };
|
|
606
|
-
const gateRevision = readKernel({ taskDir: dir, cwd: root }).kernel.revision;
|
|
607
|
-
applyKernelRecordGate({ taskDir: dir, cwd: root, expectedRevision: gateRevision,
|
|
608
|
-
actor: "pactile task start-execution planning review", idempotencyKey: `planning:${record.id}:${gate}:r${gateRevision}`,
|
|
609
|
-
transition: "start-execution", gateName: gate, record: gateRecord, evidence: String(gateRecord.evidence) });
|
|
610
|
-
const transitionRows = qualityGateResults.transitions["start-execution"];
|
|
611
|
-
transitionRows[gate] = gateRecord;
|
|
612
|
-
}
|
|
613
|
-
}
|
|
1534
|
+
extras.dependency_satisfied = dependencyFacts.satisfied;
|
|
614
1535
|
if (args.includes("--ignore-deps")) {
|
|
615
1536
|
extras.dependency_override = {
|
|
616
1537
|
schema_version: 1,
|
|
@@ -623,7 +1544,9 @@ function startExecution(root, args) {
|
|
|
623
1544
|
}
|
|
624
1545
|
const revision = readKernel({ taskDir: dir, cwd: root }).kernel.revision;
|
|
625
1546
|
applyKernelStart({
|
|
626
|
-
taskDir: dir,
|
|
1547
|
+
taskDir: dir,
|
|
1548
|
+
cwd: root,
|
|
1549
|
+
expectedRevision: revision,
|
|
627
1550
|
actor: "pactile task start-execution --approved",
|
|
628
1551
|
idempotencyKey: `start:${record.id || path.basename(dir)}:${checkedAt}`,
|
|
629
1552
|
evidence: "pactile task start-execution --approved",
|
|
@@ -648,8 +1571,16 @@ function recordGate(root, args) {
|
|
|
648
1571
|
throw new Error("--result must be PASS, FAIL, or SKIPPED");
|
|
649
1572
|
const allowed = {
|
|
650
1573
|
"start-execution": ["requirements-review", "architecture-review"],
|
|
651
|
-
"full-task-complete": [
|
|
652
|
-
|
|
1574
|
+
"full-task-complete": [
|
|
1575
|
+
"code-review",
|
|
1576
|
+
"architecture-review",
|
|
1577
|
+
"architecture-deep-review",
|
|
1578
|
+
],
|
|
1579
|
+
"child-review": [
|
|
1580
|
+
"code-review",
|
|
1581
|
+
"architecture-review",
|
|
1582
|
+
"architecture-deep-review",
|
|
1583
|
+
],
|
|
653
1584
|
"parent-integrated": ["integration-review"],
|
|
654
1585
|
};
|
|
655
1586
|
if (!allowed[transition]?.includes(gate))
|
|
@@ -658,34 +1589,63 @@ function recordGate(root, args) {
|
|
|
658
1589
|
throw new Error("--reviewer must be a short identifier");
|
|
659
1590
|
if (!evidence.trim() || evidence.length > 240 || /[\r\n]/.test(evidence))
|
|
660
1591
|
throw new Error("--evidence must be a short single-line reference");
|
|
661
|
-
if (result === "FAIL" &&
|
|
1592
|
+
if (result === "FAIL" &&
|
|
1593
|
+
(!option(args, "--issue-fingerprint") || !option(args, "--root-cause")))
|
|
662
1594
|
throw new Error("FAIL requires --issue-fingerprint and --root-cause");
|
|
663
|
-
if (result === "FAIL" &&
|
|
1595
|
+
if (result === "FAIL" &&
|
|
1596
|
+
![
|
|
1597
|
+
"implementation-defect",
|
|
1598
|
+
"contract-changing-defect",
|
|
1599
|
+
"validation-environment-blocker",
|
|
1600
|
+
].includes(option(args, "--root-cause") ?? ""))
|
|
664
1601
|
throw new Error("--root-cause is not a known failure route");
|
|
665
|
-
if (result === "SKIPPED" &&
|
|
1602
|
+
if (result === "SKIPPED" &&
|
|
1603
|
+
(option(args, "--skip-approved-by") !== "user" ||
|
|
1604
|
+
!option(args, "--skip-reason")))
|
|
666
1605
|
throw new Error("SKIPPED requires --skip-approved-by user and --skip-reason");
|
|
667
|
-
const parsed = fs.existsSync(path.join(dir, "implement.md"))
|
|
1606
|
+
const parsed = fs.existsSync(path.join(dir, "implement.md"))
|
|
1607
|
+
? readStrategyContract(dir)
|
|
1608
|
+
: { contract: null, errors: [] };
|
|
668
1609
|
if (parsed.errors.length && transition !== "parent-integrated")
|
|
669
1610
|
throw new Error(parsed.errors.join("; "));
|
|
670
1611
|
const currentContract = contractFingerprint(dir, task, parsed.contract);
|
|
671
1612
|
const currentArtifact = artifactFingerprint(dir, task, transition, gate);
|
|
672
|
-
if (option(args, "--contract-fingerprint") &&
|
|
1613
|
+
if (option(args, "--contract-fingerprint") &&
|
|
1614
|
+
option(args, "--contract-fingerprint") !== currentContract)
|
|
673
1615
|
throw new Error("contract fingerprint assertion is stale");
|
|
674
|
-
if (option(args, "--artifact-fingerprint") &&
|
|
1616
|
+
if (option(args, "--artifact-fingerprint") &&
|
|
1617
|
+
option(args, "--artifact-fingerprint") !== currentArtifact)
|
|
675
1618
|
throw new Error("artifact fingerprint assertion is stale");
|
|
676
1619
|
const record = {
|
|
677
|
-
schema_version: 1,
|
|
678
|
-
|
|
1620
|
+
schema_version: 1,
|
|
1621
|
+
transition,
|
|
1622
|
+
gate,
|
|
1623
|
+
result,
|
|
1624
|
+
reviewer,
|
|
1625
|
+
evidence,
|
|
1626
|
+
checked_at: new Date().toISOString(),
|
|
1627
|
+
contract_fingerprint: currentContract,
|
|
1628
|
+
artifact_fingerprint: currentArtifact,
|
|
679
1629
|
issue_fingerprint: result === "FAIL" ? option(args, "--issue-fingerprint") : null,
|
|
680
1630
|
root_cause: result === "FAIL" ? option(args, "--root-cause") : null,
|
|
681
|
-
issue_summary: result === "FAIL" ? option(args, "--issue-summary") ?? null : null,
|
|
1631
|
+
issue_summary: result === "FAIL" ? (option(args, "--issue-summary") ?? null) : null,
|
|
682
1632
|
consecutive_failures: result === "FAIL" ? 1 : 0,
|
|
683
|
-
approved_skip: result === "SKIPPED"
|
|
1633
|
+
approved_skip: result === "SKIPPED"
|
|
1634
|
+
? { approved_by: "user", reason: option(args, "--skip-reason") }
|
|
1635
|
+
: null,
|
|
684
1636
|
};
|
|
685
1637
|
const revision = readKernel({ taskDir: dir, cwd: root }).kernel.revision;
|
|
686
|
-
applyKernelRecordGate({
|
|
1638
|
+
applyKernelRecordGate({
|
|
1639
|
+
taskDir: dir,
|
|
1640
|
+
cwd: root,
|
|
1641
|
+
expectedRevision: revision,
|
|
1642
|
+
actor: `pactile task record-gate ${reviewer}`,
|
|
687
1643
|
idempotencyKey: `record-gate:${task.id}:${transition}:${gate}:r${revision}`,
|
|
688
|
-
transition,
|
|
1644
|
+
transition,
|
|
1645
|
+
gateName: gate,
|
|
1646
|
+
record,
|
|
1647
|
+
evidence,
|
|
1648
|
+
});
|
|
689
1649
|
console.log(`${transition}/${gate}: ${result}`);
|
|
690
1650
|
return 0;
|
|
691
1651
|
}
|
|
@@ -702,15 +1662,25 @@ function recordAcEvidence(root, args) {
|
|
|
702
1662
|
const equal = assignment.indexOf("=");
|
|
703
1663
|
if (equal < 0)
|
|
704
1664
|
throw new Error("--map must be AC-N=reference");
|
|
705
|
-
mappings.push({
|
|
1665
|
+
mappings.push({
|
|
1666
|
+
ac_id: assignment.slice(0, equal),
|
|
1667
|
+
evidence_ref: assignment.slice(equal + 1),
|
|
1668
|
+
});
|
|
706
1669
|
index += 1;
|
|
707
1670
|
}
|
|
708
1671
|
if (!mappings.length)
|
|
709
1672
|
throw new Error("at least one --map AC-N=reference is required");
|
|
710
1673
|
const codeRef = requireArgument(option(args, "--code-ref"), "--code-ref");
|
|
711
|
-
const ledger = buildAcEvidenceLedger({
|
|
712
|
-
|
|
713
|
-
|
|
1674
|
+
const ledger = buildAcEvidenceLedger({
|
|
1675
|
+
acceptanceItems: parseAcceptanceItems(prd),
|
|
1676
|
+
mappings,
|
|
1677
|
+
sourceFingerprint: qualityFingerprint(prd),
|
|
1678
|
+
evidenceFingerprint: qualityFingerprint(verify),
|
|
1679
|
+
testedCodeFingerprint: codeRef,
|
|
1680
|
+
});
|
|
1681
|
+
patchTask(root, reference, {}, "record-ac-evidence", {
|
|
1682
|
+
ac_evidence_ledger: ledger,
|
|
1683
|
+
});
|
|
714
1684
|
console.log(`AC evidence ledger recorded: ${ledger.items.length} mapped criteria`);
|
|
715
1685
|
return 0;
|
|
716
1686
|
}
|
|
@@ -730,9 +1700,20 @@ function recordIndependentCheck(root, args) {
|
|
|
730
1700
|
const reviewer = option(args, "--reviewer");
|
|
731
1701
|
if (mode === "true-independent" && result === "PASS" && !reviewer)
|
|
732
1702
|
throw new Error("true-independent PASS requires --reviewer");
|
|
733
|
-
const verdict = {
|
|
734
|
-
|
|
735
|
-
|
|
1703
|
+
const verdict = {
|
|
1704
|
+
schema_version: 1,
|
|
1705
|
+
mode,
|
|
1706
|
+
readonly: true,
|
|
1707
|
+
result,
|
|
1708
|
+
evidence,
|
|
1709
|
+
independent_worker: independentWorker,
|
|
1710
|
+
reviewer: reviewer ?? null,
|
|
1711
|
+
code_fingerprint: codeRef,
|
|
1712
|
+
checked_at: new Date().toISOString(),
|
|
1713
|
+
};
|
|
1714
|
+
patchTask(root, reference, {}, "record-independent-check", {
|
|
1715
|
+
independent_check: verdict,
|
|
1716
|
+
});
|
|
736
1717
|
console.log(`Independent check: ${result} (${mode})`);
|
|
737
1718
|
return 0;
|
|
738
1719
|
}
|
|
@@ -742,7 +1723,9 @@ function archiveTask(root, args) {
|
|
|
742
1723
|
if (!record)
|
|
743
1724
|
throw new Error(`task.json not found at ${dir}`);
|
|
744
1725
|
const guard = checkArchive(dir, record);
|
|
745
|
-
const activeChildren = record.children
|
|
1726
|
+
const activeChildren = record.children
|
|
1727
|
+
.map((id) => path.join(root, ".pactile", "tasks", id))
|
|
1728
|
+
.filter((candidate) => fs.existsSync(candidate));
|
|
746
1729
|
if (activeChildren.length && !args.includes("--archive-integrated-children"))
|
|
747
1730
|
guard.errors.push(`active children remain: ${activeChildren.map((candidate) => path.basename(candidate)).join(", ")}; use --archive-integrated-children after preflight`);
|
|
748
1731
|
if (activeChildren.length && args.includes("--archive-integrated-children")) {
|
|
@@ -778,20 +1761,34 @@ function archiveTask(root, args) {
|
|
|
778
1761
|
if (fs.existsSync(destination))
|
|
779
1762
|
throw new Error(`Archive destination already exists: ${destination}`);
|
|
780
1763
|
const revision = readKernel({ taskDir: dir, cwd: root }).kernel.revision;
|
|
781
|
-
const archivedRelative = path
|
|
1764
|
+
const archivedRelative = path
|
|
1765
|
+
.relative(root, destination)
|
|
1766
|
+
.replaceAll("\\", "/");
|
|
782
1767
|
const verifyText = fs.readFileSync(path.join(dir, "verify.md"), "utf8");
|
|
783
|
-
const acceptance = verifyText
|
|
784
|
-
|
|
1768
|
+
const acceptance = verifyText
|
|
1769
|
+
.match(/^\s*(?:[-*]\s*)?(?:(?:final|user)\s+)?acceptance(?:\s+evidence)?\s*:\s*(\S[^\r\n]*)$/im)?.[1]
|
|
1770
|
+
?.trim();
|
|
1771
|
+
const points = acceptance
|
|
1772
|
+
? [{ text: acceptance, source: "verify.md", confidence: "low" }]
|
|
1773
|
+
: [];
|
|
785
1774
|
const notesProjection = {
|
|
786
1775
|
schema_version: 1,
|
|
787
|
-
summary: points
|
|
1776
|
+
summary: points
|
|
1777
|
+
.map((point) => point.text)
|
|
1778
|
+
.join("; ")
|
|
1779
|
+
.split(/\s+/)
|
|
1780
|
+
.slice(0, 150)
|
|
1781
|
+
.join(" "),
|
|
788
1782
|
pointers: [archivedRelative, `${archivedRelative}/verify.md`],
|
|
789
1783
|
source: "cmd_archive",
|
|
790
1784
|
points,
|
|
791
1785
|
};
|
|
792
1786
|
applyKernelArchive({
|
|
793
|
-
taskDir: dir,
|
|
794
|
-
|
|
1787
|
+
taskDir: dir,
|
|
1788
|
+
cwd: root,
|
|
1789
|
+
expectedRevision: revision,
|
|
1790
|
+
actor: "pactile task archive",
|
|
1791
|
+
idempotencyKey: `archive:${path.basename(dir)}:${completedAt}`,
|
|
795
1792
|
evidence: "pactile task archive",
|
|
796
1793
|
record: { ...record, status: "completed", completedAt },
|
|
797
1794
|
extras: { notes_projection: notesProjection },
|
|
@@ -802,14 +1799,24 @@ function archiveTask(root, args) {
|
|
|
802
1799
|
const selected = resolveSelectedTask(root);
|
|
803
1800
|
if (selected.taskPath === `.pactile/tasks/${path.basename(dir)}`)
|
|
804
1801
|
exitTask(root);
|
|
805
|
-
if (!args.includes("--no-commit") &&
|
|
1802
|
+
if (!args.includes("--no-commit") &&
|
|
1803
|
+
readPactileConfig(root).session_auto_commit !== "false") {
|
|
806
1804
|
const relativeSource = path.relative(root, dir).replaceAll("\\", "/");
|
|
807
|
-
const relativeDestination = path
|
|
1805
|
+
const relativeDestination = path
|
|
1806
|
+
.relative(root, destination)
|
|
1807
|
+
.replaceAll("\\", "/");
|
|
808
1808
|
let isRepo = false;
|
|
809
1809
|
try {
|
|
810
|
-
isRepo =
|
|
1810
|
+
isRepo =
|
|
1811
|
+
execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
1812
|
+
cwd: root,
|
|
1813
|
+
encoding: "utf8",
|
|
1814
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1815
|
+
}).trim() === "true";
|
|
1816
|
+
}
|
|
1817
|
+
catch {
|
|
1818
|
+
/* Archive remains valid in a non-Git project. */
|
|
811
1819
|
}
|
|
812
|
-
catch { /* Archive remains valid in a non-Git project. */ }
|
|
813
1820
|
try {
|
|
814
1821
|
if (isRepo) {
|
|
815
1822
|
let ignored = false;
|
|
@@ -817,11 +1824,21 @@ function archiveTask(root, args) {
|
|
|
817
1824
|
execFileSync("git", ["check-ignore", "-q", "--", relativeDestination], { cwd: root, stdio: ["ignore", "pipe", "ignore"] });
|
|
818
1825
|
ignored = true;
|
|
819
1826
|
}
|
|
820
|
-
catch {
|
|
1827
|
+
catch {
|
|
1828
|
+
/* The archive path is not ignored. */
|
|
1829
|
+
}
|
|
821
1830
|
if (!ignored) {
|
|
822
1831
|
execFileSync("git", ["add", "-A", "--", relativeSource, relativeDestination], { cwd: root, stdio: ["ignore", "pipe", "pipe"] });
|
|
823
1832
|
const message = `chore(task): archive ${path.basename(dir)}`;
|
|
824
|
-
execFileSync("git", [
|
|
1833
|
+
execFileSync("git", [
|
|
1834
|
+
"commit",
|
|
1835
|
+
"--only",
|
|
1836
|
+
"-m",
|
|
1837
|
+
message,
|
|
1838
|
+
"--",
|
|
1839
|
+
relativeSource,
|
|
1840
|
+
relativeDestination,
|
|
1841
|
+
], { cwd: root, stdio: ["ignore", "pipe", "pipe"] });
|
|
825
1842
|
}
|
|
826
1843
|
}
|
|
827
1844
|
}
|
|
@@ -837,32 +1854,80 @@ export function runTaskCli(argv, root = process.cwd()) {
|
|
|
837
1854
|
const [command, ...args] = argv;
|
|
838
1855
|
try {
|
|
839
1856
|
switch (command) {
|
|
1857
|
+
case "schedule":
|
|
1858
|
+
return runTaskScheduleCli(args, root);
|
|
840
1859
|
case "create":
|
|
841
1860
|
createTask(args, root);
|
|
842
1861
|
return 0;
|
|
843
|
-
case "
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
case "
|
|
1862
|
+
case "legacy-create":
|
|
1863
|
+
createLegacyTask(args, root);
|
|
1864
|
+
return 0;
|
|
1865
|
+
case "show":
|
|
1866
|
+
return showTask(root, args);
|
|
1867
|
+
case "artifacts":
|
|
1868
|
+
return taskArtifacts(root, args);
|
|
1869
|
+
case "add-dependency": {
|
|
1870
|
+
const reference = requireArgument(args[0], "task");
|
|
1871
|
+
const dependencyId = requireArgument(args[1], "dependency task ID");
|
|
1872
|
+
const { dir, kernel } = taskV2(root, reference);
|
|
1873
|
+
const result = addTaskDependency({
|
|
1874
|
+
root,
|
|
1875
|
+
taskDir: dir,
|
|
1876
|
+
expectedRevision: kernel.revision,
|
|
1877
|
+
dependencyId,
|
|
1878
|
+
actor: actor(root, args),
|
|
1879
|
+
idempotencyKey: option(args, "--idempotency-key") ??
|
|
1880
|
+
`dependency:${kernel.identity.taskId}:${dependencyId}`,
|
|
1881
|
+
});
|
|
1882
|
+
console.log(`Hard dependency added: ${result.kernel.identity.taskId} -> ${dependencyId}\nKernel revision: ${result.kernel.revision}`);
|
|
1883
|
+
return 0;
|
|
1884
|
+
}
|
|
1885
|
+
case "run-start":
|
|
1886
|
+
return runStartTask(root, args);
|
|
1887
|
+
case "run-resume":
|
|
1888
|
+
return resumeTask(root, args);
|
|
1889
|
+
case "run-result":
|
|
1890
|
+
return runResultTask(root, args);
|
|
1891
|
+
case "review":
|
|
1892
|
+
return reviewTask(root, args);
|
|
1893
|
+
case "close":
|
|
1894
|
+
return closeTask(root, args);
|
|
1895
|
+
case "start-execution":
|
|
1896
|
+
return startExecution(root, args);
|
|
1897
|
+
case "record-gate":
|
|
1898
|
+
return recordGate(root, args);
|
|
1899
|
+
case "record-ac-evidence":
|
|
1900
|
+
return recordAcEvidence(root, args);
|
|
1901
|
+
case "record-independent-check":
|
|
1902
|
+
return recordIndependentCheck(root, args);
|
|
847
1903
|
case "add-subtask":
|
|
848
1904
|
linkChild(root, requireArgument(args[0], "parent task"), requireArgument(args[1], "child task"));
|
|
849
1905
|
return 0;
|
|
850
1906
|
case "remove-subtask":
|
|
851
1907
|
unlinkChild(root, requireArgument(args[0], "parent task"), requireArgument(args[1], "child task"));
|
|
852
1908
|
return 0;
|
|
853
|
-
case "prepare-child-worktree":
|
|
854
|
-
|
|
855
|
-
case "
|
|
856
|
-
|
|
1909
|
+
case "prepare-child-worktree":
|
|
1910
|
+
return prepareChildWorktree(root, args);
|
|
1911
|
+
case "set-child-state":
|
|
1912
|
+
return updateChildState(root, args, false);
|
|
1913
|
+
case "integrate-child":
|
|
1914
|
+
return updateChildState(root, args, true);
|
|
1915
|
+
case "review-child":
|
|
1916
|
+
return reviewChild(root, args);
|
|
857
1917
|
case "parent-status": {
|
|
858
1918
|
const dir = taskDir(root, requireArgument(args[0], "parent task"));
|
|
859
1919
|
const snapshot = readTaskMap(dir).data;
|
|
860
1920
|
if (!snapshot)
|
|
861
1921
|
throw new Error("task-map.md missing");
|
|
862
|
-
console.log(args.includes("--json")
|
|
1922
|
+
console.log(args.includes("--json")
|
|
1923
|
+
? JSON.stringify(snapshot, null, 2)
|
|
1924
|
+
: snapshot.children
|
|
1925
|
+
.map((child) => `${child.id}: ${child.state}`)
|
|
1926
|
+
.join("\n"));
|
|
863
1927
|
return 0;
|
|
864
1928
|
}
|
|
865
|
-
case "archive":
|
|
1929
|
+
case "archive":
|
|
1930
|
+
return archiveTask(root, args);
|
|
866
1931
|
case "prepare-archive-evidence": {
|
|
867
1932
|
const dir = taskDir(root, requireArgument(args[0], "task"));
|
|
868
1933
|
const record = taskRecord(dir);
|
|
@@ -896,10 +1961,83 @@ export function runTaskCli(argv, root = process.cwd()) {
|
|
|
896
1961
|
}
|
|
897
1962
|
case "selected": {
|
|
898
1963
|
const chosen = resolveSelectedTask(root);
|
|
1964
|
+
if (args.includes("--json")) {
|
|
1965
|
+
if (!chosen.taskPath || chosen.stale) {
|
|
1966
|
+
console.log(JSON.stringify({
|
|
1967
|
+
selectedTask: chosen.taskPath,
|
|
1968
|
+
source: chosen.source,
|
|
1969
|
+
stale: chosen.stale,
|
|
1970
|
+
}, null, 2));
|
|
1971
|
+
return chosen.taskPath ? 0 : 1;
|
|
1972
|
+
}
|
|
1973
|
+
const dir = resolveTaskDir(root, chosen.taskPath);
|
|
1974
|
+
const imported = readLegacyTaskImportRecord(root, dir);
|
|
1975
|
+
if (imported && imported.status !== "imported") {
|
|
1976
|
+
console.log(JSON.stringify({
|
|
1977
|
+
taskPath: chosen.taskPath,
|
|
1978
|
+
source: chosen.source,
|
|
1979
|
+
taskId: imported.legacyTaskId,
|
|
1980
|
+
migrationStatus: imported.status,
|
|
1981
|
+
runnable: false,
|
|
1982
|
+
missingDefinitionFields: imported.missingDefinitionFields,
|
|
1983
|
+
coordinationReasons: imported.coordinationReasons,
|
|
1984
|
+
kernelVersion: null,
|
|
1985
|
+
}, null, 2));
|
|
1986
|
+
return 0;
|
|
1987
|
+
}
|
|
1988
|
+
if (!fs.existsSync(path.join(dir, "kernel.json")) && !imported) {
|
|
1989
|
+
const record = taskRecord(dir);
|
|
1990
|
+
console.log(JSON.stringify({
|
|
1991
|
+
taskPath: chosen.taskPath,
|
|
1992
|
+
source: chosen.source,
|
|
1993
|
+
taskId: record?.id ?? null,
|
|
1994
|
+
title: record?.title ?? record?.name ?? null,
|
|
1995
|
+
status: record?.status ?? null,
|
|
1996
|
+
kernelVersion: 0,
|
|
1997
|
+
}, null, 2));
|
|
1998
|
+
return 0;
|
|
1999
|
+
}
|
|
2000
|
+
const read = readTaskKernel({ root, taskDir: dir, cwd: root });
|
|
2001
|
+
const summary = read.kind === "task-kernel-v2"
|
|
2002
|
+
? {
|
|
2003
|
+
taskId: read.kernel.identity.taskId,
|
|
2004
|
+
title: read.kernel.definition.title,
|
|
2005
|
+
phase: read.kernel.phase,
|
|
2006
|
+
condition: read.kernel.condition,
|
|
2007
|
+
revision: read.kernel.revision,
|
|
2008
|
+
kernelVersion: 2,
|
|
2009
|
+
}
|
|
2010
|
+
: {
|
|
2011
|
+
taskId: read.kernel.kernel.identity.taskId,
|
|
2012
|
+
phase: read.kernel.kernel.phase,
|
|
2013
|
+
condition: read.kernel.kernel.condition,
|
|
2014
|
+
revision: read.kernel.kernel.revision,
|
|
2015
|
+
kernelVersion: 1,
|
|
2016
|
+
};
|
|
2017
|
+
console.log(JSON.stringify({ taskPath: chosen.taskPath, source: chosen.source, ...summary }, null, 2));
|
|
2018
|
+
return 0;
|
|
2019
|
+
}
|
|
899
2020
|
if (args.includes("--source")) {
|
|
900
2021
|
console.log(`Selected task: ${chosen.taskPath ?? "(none)"}\nSource: ${chosen.source}`);
|
|
901
2022
|
if (chosen.stale)
|
|
902
2023
|
console.log("State: stale");
|
|
2024
|
+
else if (chosen.taskPath) {
|
|
2025
|
+
const dir = resolveTaskDir(root, chosen.taskPath);
|
|
2026
|
+
const imported = readLegacyTaskImportRecord(root, dir);
|
|
2027
|
+
if (imported && imported.status !== "imported") {
|
|
2028
|
+
console.log(`Migration: ${imported.status}; Run unavailable`);
|
|
2029
|
+
}
|
|
2030
|
+
else if (fs.existsSync(path.join(dir, "kernel.json")) ||
|
|
2031
|
+
imported) {
|
|
2032
|
+
const read = readTaskKernel({ root, taskDir: dir, cwd: root });
|
|
2033
|
+
const kernel = read.kind === "task-kernel-v2"
|
|
2034
|
+
? read.kernel
|
|
2035
|
+
: read.kernel.kernel;
|
|
2036
|
+
console.log(`Kernel: ${read.kind === "task-kernel-v2" ? "v2" : "v1"}\nPhase: ${kernel.phase}\nKernel revision: ${kernel.revision}`);
|
|
2037
|
+
}
|
|
2038
|
+
else
|
|
2039
|
+
console.log("Kernel: unavailable (legacy task.json only)");
|
|
2040
|
+
}
|
|
903
2041
|
}
|
|
904
2042
|
else if (chosen.taskPath)
|
|
905
2043
|
console.log(chosen.taskPath);
|
|
@@ -909,7 +2047,9 @@ export function runTaskCli(argv, root = process.cwd()) {
|
|
|
909
2047
|
}
|
|
910
2048
|
case "exit": {
|
|
911
2049
|
const previous = exitTask(root);
|
|
912
|
-
console.log(previous.taskPath
|
|
2050
|
+
console.log(previous.taskPath
|
|
2051
|
+
? `Cleared selected task (was: ${previous.taskPath})\nTask status unchanged.`
|
|
2052
|
+
: "No selected task set");
|
|
913
2053
|
return 0;
|
|
914
2054
|
}
|
|
915
2055
|
case "add-context": {
|
|
@@ -925,26 +2065,46 @@ export function runTaskCli(argv, root = process.cwd()) {
|
|
|
925
2065
|
case "set-scope": {
|
|
926
2066
|
const reference = requireArgument(args[0], "task");
|
|
927
2067
|
const value = requireArgument(args[1], command === "set-scope" ? "scope" : "branch");
|
|
928
|
-
const key = command === "set-branch"
|
|
2068
|
+
const key = command === "set-branch"
|
|
2069
|
+
? "branch"
|
|
2070
|
+
: command === "set-base-branch"
|
|
2071
|
+
? "base_branch"
|
|
2072
|
+
: "scope";
|
|
929
2073
|
patchTask(root, reference, { [key]: value }, command);
|
|
930
2074
|
console.log(`${key} set to: ${value}`);
|
|
931
2075
|
return 0;
|
|
932
2076
|
}
|
|
933
2077
|
case "set-deps": {
|
|
934
2078
|
const reference = requireArgument(args[0], "task");
|
|
935
|
-
const dependencies = [
|
|
2079
|
+
const dependencies = [
|
|
2080
|
+
...new Set(args.slice(1).filter((item) => item.trim())),
|
|
2081
|
+
];
|
|
936
2082
|
const dir = taskDir(root, reference);
|
|
937
2083
|
const record = taskRecord(dir);
|
|
938
2084
|
if (!record)
|
|
939
2085
|
throw new Error(`task.json not found at ${dir}`);
|
|
940
|
-
const projection = readKernel({ taskDir: dir, cwd: root }).kernel.projection?.extras ??
|
|
941
|
-
|
|
942
|
-
const
|
|
2086
|
+
const projection = readKernel({ taskDir: dir, cwd: root }).kernel.projection?.extras ??
|
|
2087
|
+
{};
|
|
2088
|
+
const rawDeps = record
|
|
2089
|
+
.depends_on;
|
|
2090
|
+
const oldDeps = Array.isArray(rawDeps)
|
|
2091
|
+
? rawDeps.filter((item) => typeof item === "string")
|
|
2092
|
+
: [];
|
|
943
2093
|
const graph = readDependencyGraph(projection);
|
|
944
|
-
const edges = graph.edges.filter((edge) => !(edge.from === record.id &&
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
2094
|
+
const edges = graph.edges.filter((edge) => !(edge.from === record.id &&
|
|
2095
|
+
edge.type === "requires" &&
|
|
2096
|
+
oldDeps.includes(edge.to)));
|
|
2097
|
+
const satisfied = Array.isArray(projection.dependency_satisfied)
|
|
2098
|
+
? projection.dependency_satisfied.filter((item) => typeof item === "string" && !oldDeps.includes(item))
|
|
2099
|
+
: [];
|
|
2100
|
+
patchTask(root, reference, {}, command, {
|
|
2101
|
+
depends_on: dependencies,
|
|
2102
|
+
dependency_graph: { ...graph, edges },
|
|
2103
|
+
dependency_satisfied: satisfied,
|
|
2104
|
+
});
|
|
2105
|
+
console.log(dependencies.length
|
|
2106
|
+
? `depends_on set: ${dependencies.join(", ")}`
|
|
2107
|
+
: "depends_on cleared to []");
|
|
948
2108
|
return 0;
|
|
949
2109
|
}
|
|
950
2110
|
case "set-depends-mode": {
|
|
@@ -964,8 +2124,12 @@ export function runTaskCli(argv, root = process.cwd()) {
|
|
|
964
2124
|
const reference = option(args, "--task");
|
|
965
2125
|
if (operation === "get") {
|
|
966
2126
|
const task = reference ? taskRecord(taskDir(root, reference)) : null;
|
|
967
|
-
const taskLocale = task?.meta && typeof task.meta === "object"
|
|
968
|
-
|
|
2127
|
+
const taskLocale = task?.meta && typeof task.meta === "object"
|
|
2128
|
+
? task.meta.artifact_locale
|
|
2129
|
+
: undefined;
|
|
2130
|
+
console.log(taskLocale === "en" || taskLocale === "zh"
|
|
2131
|
+
? taskLocale
|
|
2132
|
+
: locale(root));
|
|
969
2133
|
return 0;
|
|
970
2134
|
}
|
|
971
2135
|
if (operation !== "set")
|
|
@@ -982,7 +2146,9 @@ export function runTaskCli(argv, root = process.cwd()) {
|
|
|
982
2146
|
return 0;
|
|
983
2147
|
}
|
|
984
2148
|
const configFile = path.join(root, ".pactile", "config.yaml");
|
|
985
|
-
const content = fs.existsSync(configFile)
|
|
2149
|
+
const content = fs.existsSync(configFile)
|
|
2150
|
+
? fs.readFileSync(configFile, "utf8")
|
|
2151
|
+
: "# Pactile Configuration\n";
|
|
986
2152
|
const replacement = `artifact_locale: ${value}`;
|
|
987
2153
|
fs.mkdirSync(path.dirname(configFile), { recursive: true });
|
|
988
2154
|
fs.writeFileSync(configFile, /^\s*artifact_locale:\s*[^\r\n]*/m.test(content)
|
|
@@ -1014,7 +2180,7 @@ export function runTaskCli(argv, root = process.cwd()) {
|
|
|
1014
2180
|
return 0;
|
|
1015
2181
|
}
|
|
1016
2182
|
default:
|
|
1017
|
-
console.error("Usage: pactile task <create|start-execution|archive|prepare-archive-evidence|prepare-learning-scaffold|review-child|dashboard|list|select|selected|exit|add-context|validate|list-context|set-branch|set-base-branch|set-scope|set-deps|set-depends-mode> ...");
|
|
2183
|
+
console.error("Usage: pactile task <create|legacy-create|show|artifacts|schedule|add-dependency|run-start|run-resume|run-result|review|close|start-execution|archive|prepare-archive-evidence|prepare-learning-scaffold|review-child|dashboard|list|select|selected|exit|add-context|validate|list-context|set-branch|set-base-branch|set-scope|set-deps|set-depends-mode> ...");
|
|
1018
2184
|
return 1;
|
|
1019
2185
|
}
|
|
1020
2186
|
}
|
|
@@ -1023,4 +2189,27 @@ export function runTaskCli(argv, root = process.cwd()) {
|
|
|
1023
2189
|
return 1;
|
|
1024
2190
|
}
|
|
1025
2191
|
}
|
|
2192
|
+
/** Promise-aware task entry for the existing CLI wrapper's async operations. */
|
|
2193
|
+
export async function runTaskCliAsync(argv, root = process.cwd(), scheduleDispatchOptions = {}) {
|
|
2194
|
+
const [command, operation, ...args] = argv;
|
|
2195
|
+
if (command === "schedule" && operation === "dispatch") {
|
|
2196
|
+
try {
|
|
2197
|
+
return await runTaskScheduleDispatchCli(args, root, scheduleDispatchOptions);
|
|
2198
|
+
}
|
|
2199
|
+
catch (error) {
|
|
2200
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
2201
|
+
return 1;
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
if (command === "schedule") {
|
|
2205
|
+
try {
|
|
2206
|
+
return await runTaskScheduleCliAsync([operation ?? "", ...args], root);
|
|
2207
|
+
}
|
|
2208
|
+
catch (error) {
|
|
2209
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
2210
|
+
return 1;
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
return runTaskCli(argv, root);
|
|
2214
|
+
}
|
|
1026
2215
|
//# sourceMappingURL=task.js.map
|