@oisincoveney/pipeline 3.24.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/defaults/pipeline.yaml +23 -5
- package/defaults/profiles.yaml +104 -21
- package/dist/argo-graph.js +90 -88
- package/dist/argo-submit.d.ts +110 -138
- package/dist/argo-submit.js +363 -378
- package/dist/argo-workflow.d.ts +156 -177
- package/dist/argo-workflow.js +96 -138
- package/dist/backlog.js +5 -4
- package/dist/bench/eval-report.js +21 -12
- package/dist/claude-settings-config.js +23 -31
- package/dist/claude-user-config.js +6 -16
- package/dist/cli/bootstrap-commands.js +3 -3
- package/dist/cli/doctor.d.ts +1 -1
- package/dist/cli/doctor.js +176 -171
- package/dist/cli/factory-commands.js +13 -13
- package/dist/cli/format.js +92 -86
- package/dist/cli/loop-commands.js +21 -21
- package/dist/cli/mcp-gateway-commands.js +12 -12
- package/dist/cli/plan-commands.js +85 -116
- package/dist/cli/program.d.ts +3 -3
- package/dist/cli/program.js +41 -43
- package/dist/cli/run-command.d.ts +1 -1
- package/dist/cli/run-command.js +25 -25
- package/dist/cli/run-commands.js +31 -29
- package/dist/cli/run-resolver.d.ts +1 -1
- package/dist/cli/run-resolver.js +30 -32
- package/dist/cli/run-service.d.ts +2 -3
- package/dist/cli/run-service.js +229 -239
- package/dist/cli/submit-options.js +85 -83
- package/dist/cluster-doctor.js +147 -175
- package/dist/codex-config.js +24 -30
- package/dist/commands/bench-command.js +9 -7
- package/dist/commands/coordinator-command.js +35 -0
- package/dist/commands/pipeline-command.js +13 -7
- package/dist/commands/runner-command-command.js +39 -24
- package/dist/commands/runner-node-command.js +31 -0
- package/dist/commands/ticket/complete.js +29 -33
- package/dist/commands/ticket/create.js +84 -82
- package/dist/commands/ticket/graph-check.js +11 -11
- package/dist/commands/ticket/next.js +36 -36
- package/dist/commands/ticket/sequence.js +11 -12
- package/dist/commands/ticket/shared.d.ts +5 -2
- package/dist/commands/ticket/shared.js +63 -66
- package/dist/commands/ticket/start.js +58 -67
- package/dist/commands/ticket-command.js +2 -2
- package/dist/config/defaults.d.ts +139 -140
- package/dist/config/defaults.js +81 -80
- package/dist/config/lint.js +52 -64
- package/dist/config/load.d.ts +4 -5
- package/dist/config/load.js +64 -74
- package/dist/config/schema/catalog.d.ts +2 -1
- package/dist/config/schema/catalog.js +3 -2
- package/dist/config/schema/mcp.js +46 -52
- package/dist/config/schemas.d.ts +382 -528
- package/dist/config/schemas.js +400 -398
- package/dist/config/validate.d.ts +1 -2
- package/dist/config/validate.js +236 -201
- package/dist/config.d.ts +2 -2
- package/dist/context/repo-map.js +105 -119
- package/dist/coordinator/composition.js +169 -0
- package/dist/coordinator/config.js +71 -0
- package/dist/coordinator/run-owner.js +64 -0
- package/dist/coordinator/wave-advance.js +148 -0
- package/dist/coordinator/wave-scheduler.js +50 -0
- package/dist/credentials/broker.d.ts +6 -8
- package/dist/credentials/broker.js +17 -16
- package/dist/credentials/codex-config.js +15 -19
- package/dist/credentials/file-targets.js +9 -15
- package/dist/credentials/local-codex-auth-sync.js +55 -58
- package/dist/credentials/opencode-config.js +28 -32
- package/dist/credentials/runner.js +13 -13
- package/dist/effect-sync-errors.js +28 -0
- package/dist/factory/create-experiment.js +285 -121
- package/dist/factory/exec.js +10 -12
- package/dist/factory/git-credentials.js +4 -4
- package/dist/factory/stamp-answers.js +10 -11
- package/dist/factory/template-update.js +65 -44
- package/dist/factory-lane.d.ts +26 -40
- package/dist/factory-lane.js +26 -25
- package/dist/gates.js +3 -3
- package/dist/git-remote-url.js +17 -19
- package/dist/hooks.d.ts +26 -26
- package/dist/hooks.js +20 -22
- package/dist/index.d.ts +4 -3
- package/dist/index.js +27 -36
- package/dist/install-commands/claude-code.js +100 -92
- package/dist/install-commands/host-selection.js +3 -6
- package/dist/install-commands/opencode.js +264 -259
- package/dist/install-commands/planner.js +163 -181
- package/dist/install-commands/result-format.js +1 -3
- package/dist/install-commands/shared.js +25 -42
- package/dist/install-commands/writer.js +22 -25
- package/dist/install-commands.js +8 -8
- package/dist/jscpd-output.js +29 -0
- package/dist/json-config-merge.js +14 -20
- package/dist/json-line-values.js +17 -6
- package/dist/loop/argo-poll.d.ts +0 -1
- package/dist/loop/argo-poll.js +15 -95
- package/dist/loop/backlog-records.js +1 -3
- package/dist/loop/controller-deps.js +150 -161
- package/dist/loop/controller.js +164 -186
- package/dist/loop/gh-checks.js +62 -61
- package/dist/loop/gh-runner.js +22 -25
- package/dist/loop/loop-command.js +66 -58
- package/dist/loop/loop-controller-entrypoint.js +29 -30
- package/dist/loop/merge.js +36 -40
- package/dist/loop/run-terminal-poll.js +69 -0
- package/dist/mcp/gateway-config.js +25 -29
- package/dist/mcp/gateway-doctor.js +77 -119
- package/dist/mcp/gateway-reconcile.js +17 -22
- package/dist/mcp/gateway-runtime.js +1 -3
- package/dist/mcp/host-config.js +37 -40
- package/dist/mcp/host-renderers.js +10 -18
- package/dist/mcp/repo-local-backends.js +51 -54
- package/dist/mcp/toolhive-vmcp.js +39 -39
- package/dist/model-resolver.js +29 -33
- package/dist/moka-global-config.d.ts +78 -55
- package/dist/moka-global-config.js +93 -92
- package/dist/moka-submit.d.ts +2179 -866
- package/dist/moka-submit.js +97 -99
- package/dist/opencode-project-config.js +34 -40
- package/dist/package-assets.js +1 -3
- package/dist/path-refs.js +2 -2
- package/dist/pipeline-init.js +25 -25
- package/dist/pipeline-runtime.d.ts +15 -14
- package/dist/pipeline-runtime.js +51 -50
- package/dist/planned-node.js +6 -3
- package/dist/planning/compile.d.ts +5 -5
- package/dist/planning/compile.js +140 -139
- package/dist/planning/dependency-refs.js +5 -5
- package/dist/planning/generate.d.ts +1296 -1296
- package/dist/planning/generate.js +307 -314
- package/dist/planning/graph.d.ts +0 -1
- package/dist/planning/graph.js +131 -149
- package/dist/remote/argo/model.d.ts +104 -122
- package/dist/remote/argo/model.js +205 -197
- package/dist/remote/argo/policy.js +39 -47
- package/dist/remote/argo/storage.js +81 -85
- package/dist/remote/jobs/execution-identity.js +70 -0
- package/dist/remote/jobs/job-outcome.js +108 -0
- package/dist/remote/jobs/manifest.js +96 -0
- package/dist/remote/jobs/model.d.ts +158 -0
- package/dist/remote/jobs/model.js +162 -0
- package/dist/remote/jobs/node-bootstrap.js +213 -0
- package/dist/remote/jobs/policy.js +123 -0
- package/dist/remote/jobs/reconcile.js +100 -0
- package/dist/remote/jobs/run-spec-client.js +64 -0
- package/dist/remote/jobs/run-spec-path.js +31 -0
- package/dist/remote/jobs/run-spec-server.d.ts +34 -0
- package/dist/remote/jobs/run-spec-server.js +116 -0
- package/dist/remote/jobs/submit.d.ts +23 -0
- package/dist/remote/jobs/submit.js +116 -0
- package/dist/remote/jobs/watch.js +59 -0
- package/dist/remote/submit/argo-submission.d.ts +3 -1
- package/dist/remote/submit/argo-submission.js +53 -58
- package/dist/remote/submit/compilation.d.ts +2 -0
- package/dist/remote/submit/compilation.js +46 -49
- package/dist/remote/submit/event-boundary.js +62 -76
- package/dist/remote/submit/hook-events.d.ts +6 -0
- package/dist/remote/submit/io.d.ts +7 -1
- package/dist/remote/submit/io.js +40 -43
- package/dist/remote/submit/jobs-submission.d.ts +59 -0
- package/dist/remote/submit/jobs-submission.js +97 -0
- package/dist/remote/submit/jobs-submit-result.d.ts +25 -0
- package/dist/remote/submit/jobs-submit-result.js +34 -0
- package/dist/remote/submit/options.d.ts +49 -0
- package/dist/remote/submit/options.js +27 -0
- package/dist/remote/submit/service.d.ts +40 -1
- package/dist/remote/submit/service.js +67 -49
- package/dist/run-control/command-context.js +6 -8
- package/dist/run-control/commands.js +10 -10
- package/dist/run-control/contracts.d.ts +52 -101
- package/dist/run-control/contracts.js +83 -69
- package/dist/run-control/detach.js +66 -72
- package/dist/run-control/execution-store.js +0 -0
- package/dist/run-control/file-errors.js +1 -3
- package/dist/run-control/logical-segment.js +3 -3
- package/dist/run-control/next-node.js +67 -78
- package/dist/run-control/postgres/postgres-run-control-store.js +196 -225
- package/dist/run-control/resume-command.js +34 -41
- package/dist/run-control/run-artifacts-command.js +84 -105
- package/dist/run-control/run-command-domain.js +7 -11
- package/dist/run-control/run-control-store.js +9 -9
- package/dist/run-control/run-query-command.js +73 -100
- package/dist/run-control/run-record.js +5 -3
- package/dist/run-control/run-state-lock.js +5 -5
- package/dist/run-control/runtime-event-projection.js +38 -50
- package/dist/run-control/runtime-reporter.js +86 -102
- package/dist/run-control/stop-command.js +42 -53
- package/dist/run-control/store-fs-effects.js +3 -1
- package/dist/run-control/store-manifest.js +38 -13
- package/dist/run-control/store-paths.js +3 -3
- package/dist/run-control/store.js +1 -1
- package/dist/run-control/submit-result.js +21 -21
- package/dist/run-control/supervisor.js +78 -68
- package/dist/run-control/workspace.js +2 -2
- package/dist/run-state/git-refs.js +258 -307
- package/dist/runner/opencode-excludes.js +4 -4
- package/dist/runner/subprocess-result.js +28 -37
- package/dist/runner/subprocess.js +41 -41
- package/dist/runner/timeouts.js +14 -13
- package/dist/runner-command/dynamic-command.js +46 -39
- package/dist/runner-command/environment-commands.js +91 -0
- package/dist/runner-command/finalize.js +174 -153
- package/dist/runner-command/lifecycle-context.js +115 -130
- package/dist/runner-command/lifecycle.js +78 -72
- package/dist/runner-command/pre-schedule.js +203 -223
- package/dist/runner-command/run.js +450 -448
- package/dist/runner-command/schedule-source-options.js +8 -10
- package/dist/runner-command/select-ready-wave.js +39 -30
- package/dist/runner-command/task-descriptor.js +11 -17
- package/dist/runner-command-contract.d.ts +147 -157
- package/dist/runner-command-contract.js +403 -411
- package/dist/runner-event-schema.d.ts +364 -505
- package/dist/runner-event-schema.js +187 -175
- package/dist/runner-event-sink.js +37 -43
- package/dist/runner-output.js +6 -6
- package/dist/runner.d.ts +57 -37
- package/dist/runner.js +176 -127
- package/dist/runtime/actor-ids.d.ts +1 -0
- package/dist/runtime/actor-ids.js +2 -2
- package/dist/runtime/agent-node/agent-node.js +99 -110
- package/dist/runtime/agent-node/handoff-finalization.js +23 -24
- package/dist/runtime/agent-node/model-selection.js +19 -22
- package/dist/runtime/agent-node/output-finalization.js +174 -204
- package/dist/runtime/agent-node/prompt-rendering.js +208 -251
- package/dist/runtime/agent-node/session-execution.js +43 -39
- package/dist/runtime/builtins/builtins.js +218 -284
- package/dist/runtime/changed-files/changed-files.js +29 -34
- package/dist/runtime/command-executor/command-executor.js +31 -23
- package/dist/runtime/config-error.js +1 -3
- package/dist/runtime/context/context.js +35 -37
- package/dist/runtime/contracts/contracts.d.ts +3 -0
- package/dist/runtime/contracts/index.d.ts +1 -1
- package/dist/runtime/detached-race.js +11 -14
- package/dist/runtime/drain-merge/drain-merge.js +139 -164
- package/dist/runtime/durable-store/acquisition.js +5 -3
- package/dist/runtime/durable-store/durable-store.d.ts +3 -1
- package/dist/runtime/durable-store/postgres/client.js +29 -0
- package/dist/runtime/durable-store/postgres/migrate-substrate.js +34 -29
- package/dist/runtime/durable-store/postgres/postgres-store.js +18 -20
- package/dist/runtime/events/events.js +273 -331
- package/dist/runtime/events/index.js +2 -2
- package/dist/runtime/execution-envelope.js +44 -0
- package/dist/runtime/gates/adjudication/llm-judge.js +21 -24
- package/dist/runtime/gates/adjudication/structured-claim.js +15 -16
- package/dist/runtime/gates/adjudicator/adjudicator.js +27 -27
- package/dist/runtime/gates/adjudicator/index.js +2 -2
- package/dist/runtime/gates/contract.js +5 -9
- package/dist/runtime/gates/gates.js +9 -8
- package/dist/runtime/gates/kinds/acceptance/acceptance.js +68 -71
- package/dist/runtime/gates/kinds/acceptance/index.js +2 -2
- package/dist/runtime/gates/kinds/artifact/artifact.js +4 -4
- package/dist/runtime/gates/kinds/artifact/index.js +2 -2
- package/dist/runtime/gates/kinds/builtin/builtin.js +4 -4
- package/dist/runtime/gates/kinds/builtin/index.js +2 -2
- package/dist/runtime/gates/kinds/changed-files/changed-files.js +31 -35
- package/dist/runtime/gates/kinds/changed-files/index.js +2 -2
- package/dist/runtime/gates/kinds/command/command.js +3 -3
- package/dist/runtime/gates/kinds/command/index.js +2 -2
- package/dist/runtime/gates/kinds/json-schema/index.js +2 -2
- package/dist/runtime/gates/kinds/json-schema/json-schema.js +7 -6
- package/dist/runtime/gates/kinds/verdict/index.js +2 -2
- package/dist/runtime/gates/kinds/verdict/verdict.js +3 -3
- package/dist/runtime/gates/orchestrator.js +72 -86
- package/dist/runtime/gates/registry.js +1 -3
- package/dist/runtime/handoff.d.ts +2 -1
- package/dist/runtime/handoff.js +43 -46
- package/dist/runtime/hooks/command-hook.js +57 -71
- package/dist/runtime/hooks/context.js +21 -25
- package/dist/runtime/hooks/events.js +84 -100
- package/dist/runtime/hooks/execution.js +6 -12
- package/dist/runtime/hooks/hooks.js +34 -23
- package/dist/runtime/hooks/invocation.js +23 -29
- package/dist/runtime/hooks/module-hook.js +36 -35
- package/dist/runtime/hooks/policy.js +32 -29
- package/dist/runtime/hooks/results.js +56 -66
- package/dist/runtime/journal-acquisition.d.ts +3 -2
- package/dist/runtime/journal-acquisition.js +14 -14
- package/dist/runtime/json-validation/json-validation.js +51 -72
- package/dist/runtime/launch-plan-args.js +39 -0
- package/dist/runtime/local-scheduler.js +46 -48
- package/dist/runtime/node-execution.js +376 -445
- package/dist/runtime/node-protocol/node-protocol.js +36 -79
- package/dist/runtime/node-state-store.d.ts +4 -1
- package/dist/runtime/node-state-store.js +13 -57
- package/dist/runtime/node-state-tracker.js +46 -54
- package/dist/runtime/open-pull-request/open-pull-request.js +201 -187
- package/dist/runtime/opencode-adapter.js +23 -24
- package/dist/runtime/opencode-agent-name.js +6 -6
- package/dist/runtime/opencode-runtime.js +115 -93
- package/dist/runtime/opencode-server.js +39 -53
- package/dist/runtime/opencode-session-executor.js +312 -344
- package/dist/runtime/parallel-node/parallel-node.js +123 -134
- package/dist/runtime/parallel-worktrees/parallel-worktrees.js +90 -73
- package/dist/runtime/protected-paths/protected-paths.js +70 -71
- package/dist/runtime/remediation/remediation.js +202 -231
- package/dist/runtime/retry.js +8 -10
- package/dist/runtime/run-journal.d.ts +2 -0
- package/dist/runtime/run-journal.js +7 -7
- package/dist/runtime/runtime-results.js +54 -69
- package/dist/runtime/scheduled-dependencies.d.ts +2 -1
- package/dist/runtime/scheduled-dependencies.js +61 -75
- package/dist/runtime/scheduler.js +179 -202
- package/dist/runtime/services/agent-node-runtime-service.js +3 -3
- package/dist/runtime/services/backlog-service.d.ts +1 -2
- package/dist/runtime/services/backlog-service.js +8 -14
- package/dist/runtime/services/command-executor-service.js +1 -1
- package/dist/runtime/services/config-io-service.js +24 -21
- package/dist/runtime/services/drain-merge-git-service.js +1 -1
- package/dist/runtime/services/file-system-service.js +9 -7
- package/dist/runtime/services/git-porcelain-service.js +6 -6
- package/dist/runtime/services/kubernetes-argo-service.d.ts +10 -3
- package/dist/runtime/services/kubernetes-argo-service.js +90 -84
- package/dist/runtime/services/kubernetes-jobs-read-service.js +115 -0
- package/dist/runtime/services/kubernetes-jobs-service.d.ts +37 -0
- package/dist/runtime/services/kubernetes-jobs-service.js +45 -0
- package/dist/runtime/services/kubernetes-jobs-watch-service.js +61 -0
- package/dist/runtime/services/kubernetes-lease-service.js +21 -0
- package/dist/runtime/services/mcp-gateway-service.js +143 -94
- package/dist/runtime/services/open-pull-request-git-service.js +1 -3
- package/dist/runtime/services/opencode-runtime-server-service.js +10 -12
- package/dist/runtime/services/opencode-sdk-service.js +7 -7
- package/dist/runtime/services/repo-io-service.js +10 -12
- package/dist/runtime/services/run-journal-file-service.js +2 -2
- package/dist/runtime/services/runner-command-io-service.js +48 -52
- package/dist/runtime/services/runner-event-sink-http-service.js +32 -39
- package/dist/runtime/step/step-node.js +15 -15
- package/dist/runtime/workflow-execution.js +56 -57
- package/dist/runtime/workflow-lifecycle.js +82 -101
- package/dist/runtime/yeet-event-record.js +104 -0
- package/dist/runtime/yeet-executor.js +184 -0
- package/dist/safe-json.js +16 -16
- package/dist/schedule/backlog-context.js +122 -116
- package/dist/schedule/baseline.js +249 -255
- package/dist/schedule/passes/coverage.js +63 -68
- package/dist/schedule/passes/drain-merge.js +34 -40
- package/dist/schedule/passes/ids.js +15 -17
- package/dist/schedule/passes/models.js +40 -37
- package/dist/schedule/passes/open-pull-request.js +16 -20
- package/dist/schedule/passes/references.js +18 -22
- package/dist/schedule/prompts.js +135 -141
- package/dist/schedule/scheduling-roles.js +8 -14
- package/dist/schema-boundary.d.ts +4 -0
- package/dist/schema-boundary.js +145 -0
- package/dist/serialized-write-queue.js +2 -2
- package/dist/standard-output-schemas.js +61 -59
- package/dist/strings.js +12 -12
- package/dist/task-ref.js +5 -5
- package/dist/tickets/apply-ticket-plan.js +52 -61
- package/dist/tickets/backlog-task-store.d.ts +15 -23
- package/dist/tickets/backlog-task-store.js +82 -94
- package/dist/tickets/completion/complete-ticket.js +53 -59
- package/dist/tickets/ticket-graph-dto.d.ts +24 -48
- package/dist/tickets/ticket-graph-dto.js +40 -40
- package/dist/tickets/ticket-graph.d.ts +7 -2
- package/dist/tickets/ticket-graph.js +36 -50
- package/dist/tickets/ticket-plan-command-args.js +6 -10
- package/dist/tickets/ticket-plan-render.js +10 -16
- package/dist/tickets/ticket-plan.d.ts +32 -41
- package/dist/tickets/ticket-plan.js +97 -77
- package/dist/tickets/ticket-selection.d.ts +3 -1
- package/dist/tickets/ticket-selection.js +37 -52
- package/dist/tickets/ticket-task-index.js +13 -17
- package/dist/tickets/validation-error-format.js +3 -10
- package/dist/token-estimator.js +12 -7
- package/dist/unknown-fields.js +21 -0
- package/dist/workflow-submit-contract.d.ts +9 -10
- package/dist/workflow-submit-contract.js +10 -9
- package/docs/adr-moka-postgres-schema-isolation.md +48 -0
- package/docs/adr-opencode-first-goal-loop-runtime.md +11 -11
- package/docs/config-architecture.md +13 -13
- package/docs/mcp-host-isolation.md +1 -0
- package/docs/moka-orchestrator-design.md +3 -3
- package/docs/operator-guide.md +1 -1
- package/docs/pipeline-console-runner-contract.md +10 -6
- package/docs/runtime-actor-model.md +18 -18
- package/docs/runtime-test-subsystems.md +200 -0
- package/docs/slash-command-adapter-contract.md +3 -3
- package/package.json +49 -85
- package/dist/config/schema/reference-validation.js +0 -83
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { childReporter, createPublicRuntimeObservabilityEmitter, emit, emitAgentFinish, emitAgentStart, emitGateFinish, emitGateStart, emitNodeFinish, emitNodeOutputRecorded, emitNodeStart, emitWorkflowFinish, emitWorkflowPlanned, emitWorkflowStarted, runtimeNodeActorDescriptor, runtimeSystemId } from "./events.js";
|
|
2
|
-
export { childReporter, createPublicRuntimeObservabilityEmitter, emit, emitAgentFinish, emitAgentStart, emitGateFinish, emitGateStart, emitNodeFinish, emitNodeOutputRecorded, emitNodeStart, emitWorkflowFinish, emitWorkflowPlanned, emitWorkflowStarted, runtimeNodeActorDescriptor, runtimeSystemId };
|
|
1
|
+
import { agentFinishEvent, agentStartEvent, childReporter, createPublicRuntimeObservabilityEmitter, emit, emitAgentFinish, emitAgentStart, emitGateFinish, emitGateStart, emitNodeFinish, emitNodeOutputRecorded, emitNodeStart, emitWorkflowFinish, emitWorkflowPlanned, emitWorkflowStarted, runtimeNodeActorDescriptor, runtimeSystemId } from "./events.js";
|
|
2
|
+
export { agentFinishEvent, agentStartEvent, childReporter, createPublicRuntimeObservabilityEmitter, emit, emitAgentFinish, emitAgentStart, emitGateFinish, emitGateStart, emitNodeFinish, emitNodeOutputRecorded, emitNodeStart, emitWorkflowFinish, emitWorkflowPlanned, emitWorkflowStarted, runtimeNodeActorDescriptor, runtimeSystemId };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { nonNegativeInteger, parseStrictWithSchema, positiveInteger, requiredString, struct } from "../schema-boundary.js";
|
|
2
|
+
//#region src/runtime/execution-envelope.ts
|
|
3
|
+
/**
|
|
4
|
+
* ENG-57.6: the Engine-owned execution envelope that wraps — never modifies —
|
|
5
|
+
* the yeet `Event`. yeet owns the runtime `Event` wire type (`@oisin-ee/yeet-api`)
|
|
6
|
+
* and must not learn about Engine scheduling; the Engine owns the two identity
|
|
7
|
+
* axes that name WHICH execution produced an event:
|
|
8
|
+
*
|
|
9
|
+
* - `generation`: the semantic-execution counter (a coordinator-decided node
|
|
10
|
+
* retry increments it; it is baked into the Job identity/name).
|
|
11
|
+
* - `attempt`: the Kubernetes crash-retry index WITHIN one generation (a Job's
|
|
12
|
+
* `backoffLimit` re-runs the same execution's pod; each pod is a new attempt).
|
|
13
|
+
*
|
|
14
|
+
* Carrying both on every event is what makes a crash retry (same generation,
|
|
15
|
+
* higher attempt) distinguishable from a semantic node retry (higher
|
|
16
|
+
* generation) — see {@link classifyRetry}. The wrapped `event` is generic and
|
|
17
|
+
* opaque: the envelope validates only its own meta, so the yeet Event stays a
|
|
18
|
+
* verbatim, unchanged passthrough.
|
|
19
|
+
*/
|
|
20
|
+
const executionEnvelopeMetaSchema = struct({
|
|
21
|
+
attempt: positiveInteger,
|
|
22
|
+
generation: nonNegativeInteger,
|
|
23
|
+
nodeId: requiredString,
|
|
24
|
+
runId: requiredString
|
|
25
|
+
});
|
|
26
|
+
/** Validate untrusted envelope meta at a boundary (rejects attempt < 1, negative generation). */
|
|
27
|
+
const parseExecutionEnvelopeMeta = (input) => parseStrictWithSchema(executionEnvelopeMetaSchema, input);
|
|
28
|
+
/**
|
|
29
|
+
* Wrap a runtime/yeet event in its execution envelope. The event is carried by
|
|
30
|
+
* reference, unchanged; only the Engine-owned identity axes are added around it.
|
|
31
|
+
*/
|
|
32
|
+
const wrapExecutionEvent = (identity, attempt, event) => {
|
|
33
|
+
return {
|
|
34
|
+
...parseExecutionEnvelopeMeta({
|
|
35
|
+
attempt,
|
|
36
|
+
generation: identity.generation,
|
|
37
|
+
nodeId: identity.nodeId,
|
|
38
|
+
runId: identity.runId
|
|
39
|
+
}),
|
|
40
|
+
event
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
export { executionEnvelopeMetaSchema, parseExecutionEnvelopeMeta, wrapExecutionEvent };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Option } from "effect";
|
|
1
2
|
//#region src/runtime/gates/adjudication/llm-judge.ts
|
|
2
3
|
const HONOR_POLICIES = [
|
|
3
4
|
{
|
|
@@ -14,27 +15,13 @@ const HONOR_POLICIES = [
|
|
|
14
15
|
}
|
|
15
16
|
];
|
|
16
17
|
const TRIVIAL_REFUSAL = "trivial completion claim refused without consulting llm-judge: no non-blank claimed evidence";
|
|
17
|
-
|
|
18
|
-
* Adjudicates the residual acceptance criteria — the un-encodable residue the
|
|
19
|
-
* deterministic and structured-claim layers could not settle — against the
|
|
20
|
-
* agent's {@link CompletionClaim}, anchored to deterministic evidence. Returns
|
|
21
|
-
* one {@link UnmetCriterion} for every residue criterion that is refused for
|
|
22
|
-
* trivial input, fails anchoring, or the judge marks unsatisfied. Honored
|
|
23
|
-
* criteria are omitted. Pure given the injected `judge`.
|
|
24
|
-
*/
|
|
25
|
-
function llmJudgeUnmet(residue, claim, deterministicEvidence, judge) {
|
|
26
|
-
return residue.flatMap((criterion) => {
|
|
27
|
-
const failure = adjudicateCriterion(criterion, claimedEvidenceFor(criterion, claim), deterministicEvidence, judge);
|
|
28
|
-
return failure ? [failure] : [];
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
function adjudicateCriterion(criterion, claimedEvidence, deterministicEvidence, judge) {
|
|
18
|
+
const adjudicateCriterion = (criterion, claimedEvidence, deterministicEvidence, judge) => {
|
|
32
19
|
const meaningful = claimedEvidence.filter((item) => item.trim().length > 0);
|
|
33
|
-
if (meaningful.length === 0) return {
|
|
20
|
+
if (meaningful.length === 0) return Option.some({
|
|
34
21
|
criterion: criterion.id,
|
|
35
22
|
evidence: claimedEvidence,
|
|
36
23
|
reason: TRIVIAL_REFUSAL
|
|
37
|
-
};
|
|
24
|
+
});
|
|
38
25
|
const verdict = judge({
|
|
39
26
|
claimedEvidence: meaningful,
|
|
40
27
|
criterion,
|
|
@@ -42,15 +29,25 @@ function adjudicateCriterion(criterion, claimedEvidence, deterministicEvidence,
|
|
|
42
29
|
});
|
|
43
30
|
const anchored = verdict.citedEvidence.filter((item) => deterministicEvidence.includes(item));
|
|
44
31
|
const broken = HONOR_POLICIES.find((policy) => policy.failed(verdict, anchored));
|
|
45
|
-
if (
|
|
46
|
-
return {
|
|
32
|
+
if (broken === void 0) return Option.none();
|
|
33
|
+
return Option.some({
|
|
47
34
|
criterion: criterion.id,
|
|
48
35
|
evidence: verdict.citedEvidence.length > 0 ? verdict.citedEvidence : meaningful,
|
|
49
36
|
reason: broken.reason
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
const claimedEvidenceFor = (criterion, claim) => claim.criteria.find((entry) => entry.criterion === criterion.id)?.evidence ?? [];
|
|
40
|
+
/**
|
|
41
|
+
* Adjudicates the residual acceptance criteria — the un-encodable residue the
|
|
42
|
+
* deterministic and structured-claim layers could not settle — against the
|
|
43
|
+
* agent's {@link CompletionClaim}, anchored to deterministic evidence. Returns
|
|
44
|
+
* one {@link UnmetCriterion} for every residue criterion that is refused for
|
|
45
|
+
* trivial input, fails anchoring, or the judge marks unsatisfied. Honored
|
|
46
|
+
* criteria are omitted. Pure given the injected `judge`.
|
|
47
|
+
*/
|
|
48
|
+
const llmJudgeUnmet = (residue, claim, deterministicEvidence, judge) => residue.flatMap((criterion) => {
|
|
49
|
+
const failure = adjudicateCriterion(criterion, claimedEvidenceFor(criterion, claim), deterministicEvidence, judge);
|
|
50
|
+
return Option.isSome(failure) ? [failure.value] : [];
|
|
51
|
+
});
|
|
55
52
|
//#endregion
|
|
56
53
|
export { llmJudgeUnmet };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Option } from "effect";
|
|
1
2
|
//#region src/runtime/gates/adjudication/structured-claim.ts
|
|
2
3
|
/**
|
|
3
4
|
* Ordered completeness rules. Each rule is independently testable;
|
|
@@ -7,27 +8,27 @@
|
|
|
7
8
|
const COMPLETENESS_RULES = [
|
|
8
9
|
{
|
|
9
10
|
reason: (id) => `no claim entry for criterion '${id}'`,
|
|
10
|
-
test:
|
|
11
|
+
test: Option.isNone
|
|
11
12
|
},
|
|
12
13
|
{
|
|
13
14
|
reason: (id) => `empty evidence for criterion '${id}'`,
|
|
14
|
-
test: (entry) => entry
|
|
15
|
+
test: (entry) => Option.isSome(entry) && entry.value.evidence.length === 0
|
|
15
16
|
},
|
|
16
17
|
{
|
|
17
18
|
reason: (id) => `blank evidence for criterion '${id}'`,
|
|
18
|
-
test: (entry) => entry
|
|
19
|
+
test: (entry) => Option.isSome(entry) && entry.value.evidence.length > 0 && entry.value.evidence.every((s) => s.trim().length === 0)
|
|
19
20
|
}
|
|
20
21
|
];
|
|
21
|
-
|
|
22
|
-
const entry = claim.criteria.find((e) => e.criterion === criterion.id);
|
|
22
|
+
const criterionUnmet = (criterion, claim) => {
|
|
23
|
+
const entry = Option.fromUndefinedOr(claim.criteria.find((e) => e.criterion === criterion.id));
|
|
23
24
|
const rule = COMPLETENESS_RULES.find((r) => r.test(entry));
|
|
24
|
-
if (rule === void 0) return
|
|
25
|
-
return {
|
|
25
|
+
if (rule === void 0) return Option.none();
|
|
26
|
+
return Option.some({
|
|
26
27
|
criterion: criterion.id,
|
|
27
|
-
evidence: entry
|
|
28
|
+
evidence: Option.isSome(entry) ? entry.value.evidence : [],
|
|
28
29
|
reason: rule.reason(criterion.id)
|
|
29
|
-
};
|
|
30
|
-
}
|
|
30
|
+
});
|
|
31
|
+
};
|
|
31
32
|
/**
|
|
32
33
|
* Structured-claim adjudication layer (PIPE-90.7): deterministic, pure
|
|
33
34
|
* check below the LLM-judge in the completion pipeline.
|
|
@@ -42,11 +43,9 @@ function criterionUnmet(criterion, claim) {
|
|
|
42
43
|
* entry. Returns an empty array when the claim covers all declared
|
|
43
44
|
* criteria. Pure — no I/O, no LLM, deterministic.
|
|
44
45
|
*/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
});
|
|
50
|
-
}
|
|
46
|
+
const structuredClaimUnmet = (criteria, claim) => criteria.flatMap((criterion) => {
|
|
47
|
+
const unmet = criterionUnmet(criterion, claim);
|
|
48
|
+
return Option.isSome(unmet) ? [unmet.value] : [];
|
|
49
|
+
});
|
|
51
50
|
//#endregion
|
|
52
51
|
export { structuredClaimUnmet };
|
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
import { evaluateGate } from "../registry.js";
|
|
2
2
|
//#region src/runtime/gates/adjudicator/adjudicator.ts
|
|
3
3
|
/**
|
|
4
|
+
* Maps a failing deterministic gate to its structured refusal. A criterion-aware
|
|
5
|
+
* gate (acceptance) already reports `unmet`; a binary gate (command, artifact,
|
|
6
|
+
* ...) reports only pass/fail, so synthesize one entry per covered criterion —
|
|
7
|
+
* or, when the gate covers nothing, a single entry keyed by the gate id so the
|
|
8
|
+
* failure is never swallowed.
|
|
9
|
+
*/
|
|
10
|
+
const gateUnmet = (gate, result) => {
|
|
11
|
+
if (result.unmet && result.unmet.length > 0) return result.unmet;
|
|
12
|
+
const reason = result.reason ?? `deterministic gate '${result.gateId}' failed`;
|
|
13
|
+
if (gate.covers.length === 0) return [{
|
|
14
|
+
criterion: result.gateId,
|
|
15
|
+
evidence: result.evidence,
|
|
16
|
+
reason
|
|
17
|
+
}];
|
|
18
|
+
return gate.covers.map((id) => ({
|
|
19
|
+
criterion: id,
|
|
20
|
+
evidence: result.evidence,
|
|
21
|
+
reason
|
|
22
|
+
}));
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
4
25
|
* Runs the deterministic layer: every {@link DeterministicGate} through the
|
|
5
26
|
* registry's {@link evaluateGate}. Each gate's `covers` ids are settled
|
|
6
27
|
* (excluded from the residue) whether it passes or fails; passing gates feed the
|
|
7
28
|
* anchor evidence pool, failing gates feed the structured refusal.
|
|
8
29
|
*/
|
|
9
|
-
async
|
|
30
|
+
const runDeterministicLayer = async (gates) => {
|
|
10
31
|
const covered = /* @__PURE__ */ new Set();
|
|
11
32
|
const evidence = [];
|
|
12
33
|
const unmet = [];
|
|
@@ -21,49 +42,28 @@ async function runDeterministicLayer(gates) {
|
|
|
21
42
|
evidence,
|
|
22
43
|
unmet
|
|
23
44
|
};
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Maps a failing deterministic gate to its structured refusal. A criterion-aware
|
|
27
|
-
* gate (acceptance) already reports `unmet`; a binary gate (command, artifact,
|
|
28
|
-
* ...) reports only pass/fail, so synthesize one entry per covered criterion —
|
|
29
|
-
* or, when the gate covers nothing, a single entry keyed by the gate id so the
|
|
30
|
-
* failure is never swallowed.
|
|
31
|
-
*/
|
|
32
|
-
function gateUnmet(gate, result) {
|
|
33
|
-
if (result.unmet && result.unmet.length > 0) return result.unmet;
|
|
34
|
-
const reason = result.reason ?? `deterministic gate '${result.gateId}' failed`;
|
|
35
|
-
if (gate.covers.length === 0) return [{
|
|
36
|
-
criterion: result.gateId,
|
|
37
|
-
evidence: result.evidence,
|
|
38
|
-
reason
|
|
39
|
-
}];
|
|
40
|
-
return gate.covers.map((id) => ({
|
|
41
|
-
criterion: id,
|
|
42
|
-
evidence: result.evidence,
|
|
43
|
-
reason
|
|
44
|
-
}));
|
|
45
|
-
}
|
|
45
|
+
};
|
|
46
46
|
/**
|
|
47
47
|
* The llm-judge residue: criteria neither settled by a deterministic gate nor
|
|
48
48
|
* already flagged incomplete by the structured-claim layer. Only this residue
|
|
49
49
|
* reaches the judge, keeping it from re-adjudicating settled criteria.
|
|
50
50
|
*/
|
|
51
|
-
|
|
51
|
+
const residueCriteria = (criteria, covered, structured) => {
|
|
52
52
|
const structuredIds = new Set(structured.map((entry) => entry.criterion));
|
|
53
53
|
return criteria.filter((criterion) => !(covered.has(criterion.id) || structuredIds.has(criterion.id)));
|
|
54
|
-
}
|
|
54
|
+
};
|
|
55
55
|
/**
|
|
56
56
|
* Collapses the layer union to one entry per distinct failing criterion id,
|
|
57
57
|
* keeping the earliest (deterministic before structured-claim before llm-judge)
|
|
58
58
|
* — the most authoritative reason for that criterion.
|
|
59
59
|
*/
|
|
60
|
-
|
|
60
|
+
const dedupeByCriterion = (entries) => {
|
|
61
61
|
const seen = /* @__PURE__ */ new Set();
|
|
62
62
|
return entries.filter((entry) => {
|
|
63
63
|
if (seen.has(entry.criterion)) return false;
|
|
64
64
|
seen.add(entry.criterion);
|
|
65
65
|
return true;
|
|
66
66
|
});
|
|
67
|
-
}
|
|
67
|
+
};
|
|
68
68
|
//#endregion
|
|
69
69
|
export { dedupeByCriterion, residueCriteria, runDeterministicLayer };
|
|
@@ -21,7 +21,7 @@ import { dedupeByCriterion, residueCriteria, runDeterministicLayer } from "./adj
|
|
|
21
21
|
* The union is deduped by criterion id (earliest layer wins) so every distinct
|
|
22
22
|
* failing criterion appears exactly once. `passed` is true iff `unmet` is empty.
|
|
23
23
|
*/
|
|
24
|
-
async
|
|
24
|
+
const adjudicate = async (input) => {
|
|
25
25
|
const deterministic = await runDeterministicLayer(input.deterministicGates ?? []);
|
|
26
26
|
const structured = structuredClaimUnmet(input.criteria, input.claim);
|
|
27
27
|
const judged = llmJudgeUnmet(residueCriteria(input.criteria, deterministic.covered, structured), input.claim, deterministic.evidence, input.judge);
|
|
@@ -34,6 +34,6 @@ async function adjudicate(input) {
|
|
|
34
34
|
passed: unmet.length === 0,
|
|
35
35
|
unmet
|
|
36
36
|
};
|
|
37
|
-
}
|
|
37
|
+
};
|
|
38
38
|
//#endregion
|
|
39
39
|
export { adjudicate };
|
|
@@ -5,9 +5,7 @@
|
|
|
5
5
|
* union when `kind` is a type parameter, so this guard carries the narrowing
|
|
6
6
|
* without a type assertion.
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
return gate.kind === kind;
|
|
10
|
-
}
|
|
8
|
+
const hasKind = (gate, kind) => gate.kind === kind;
|
|
11
9
|
/**
|
|
12
10
|
* Binds one gate kind to its narrowly-typed evaluator and adapts it to the
|
|
13
11
|
* uniform {@link GateEvaluator} shape. The {@link hasKind} guard narrows the
|
|
@@ -15,11 +13,9 @@ function hasKind(gate, kind) {
|
|
|
15
13
|
* wired to the wrong key, surfacing the bug instead of silently evaluating the
|
|
16
14
|
* wrong gate.
|
|
17
15
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
};
|
|
23
|
-
}
|
|
16
|
+
const forKind = (kind, evaluate) => async (input) => {
|
|
17
|
+
if (!hasKind(input.gate, kind)) throw new Error(`gate registry mismatch: handler '${kind}' received '${input.gate.kind}'`);
|
|
18
|
+
return await evaluate(input.gate, input);
|
|
19
|
+
};
|
|
24
20
|
//#endregion
|
|
25
21
|
export { forKind };
|
|
@@ -2,29 +2,30 @@ import { parseJsonResult } from "../../safe-json.js";
|
|
|
2
2
|
import { readOptionalFile } from "../json-validation/json-validation.js";
|
|
3
3
|
import "../json-validation/index.js";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { Option } from "effect";
|
|
5
6
|
//#region src/runtime/gates/gates.ts
|
|
6
7
|
/**
|
|
7
8
|
* Resolves the raw JSON string for a gate that targets either the node's stdout
|
|
8
9
|
* output or an artifact file. Returns `{ evidence }` on read error so callers
|
|
9
10
|
* can short-circuit before parsing.
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
const gateJsonSource = (gate, context, attempt) => {
|
|
12
13
|
if (gate.target === "artifact") {
|
|
13
|
-
if (
|
|
14
|
+
if (gate.path === void 0 || gate.path.length === 0) return { evidence: "missing JSON artifact path" };
|
|
14
15
|
const source = readOptionalFile(join(context.worktreePath, gate.path));
|
|
15
|
-
return source
|
|
16
|
+
return Option.isNone(source) ? { evidence: `missing JSON artifact: ${gate.path}` } : { source: source.value };
|
|
16
17
|
}
|
|
17
18
|
return { source: attempt.output };
|
|
18
|
-
}
|
|
19
|
+
};
|
|
19
20
|
/**
|
|
20
21
|
* Parses the gate JSON source to an unknown value. Returns `{ evidence }` on
|
|
21
22
|
* parse error so callers can short-circuit with a failed gate result.
|
|
22
23
|
*/
|
|
23
|
-
|
|
24
|
+
const parseGateJson = (gate, context, attempt) => {
|
|
24
25
|
const src = gateJsonSource(gate, context, attempt);
|
|
25
|
-
if (src.evidence) return { evidence: src.evidence };
|
|
26
|
+
if (src.evidence !== void 0 && src.evidence.length > 0) return { evidence: src.evidence };
|
|
26
27
|
const parsed = parseJsonResult(src.source ?? "", "gate JSON");
|
|
27
|
-
return parsed.error ? { evidence: parsed.error } : { value: parsed.value };
|
|
28
|
-
}
|
|
28
|
+
return parsed.error !== void 0 && parsed.error.length > 0 ? { evidence: parsed.error } : { value: parsed.value };
|
|
29
|
+
};
|
|
29
30
|
//#endregion
|
|
30
31
|
export { parseGateJson };
|
|
@@ -1,13 +1,76 @@
|
|
|
1
1
|
import { isRecord } from "../../../../safe-json.js";
|
|
2
2
|
import { parseGateJson } from "../../gates.js";
|
|
3
3
|
//#region src/runtime/gates/kinds/acceptance/acceptance.ts
|
|
4
|
+
const effectiveTaskContext = (node, context) => node.taskContext ?? context.taskContext;
|
|
5
|
+
const acceptanceEntries = (value, key = "acceptance") => {
|
|
6
|
+
if (!isRecord(value)) return [];
|
|
7
|
+
const raw = value[key] ?? value.criteria ?? value.acceptanceCriteria;
|
|
8
|
+
return Array.isArray(raw) ? raw.filter((item) => isRecord(item)) : [];
|
|
9
|
+
};
|
|
10
|
+
const coverageCountUnmetCriteria = (id, count) => {
|
|
11
|
+
const unmet = [];
|
|
12
|
+
if (count === 0) unmet.push({
|
|
13
|
+
criterion: id,
|
|
14
|
+
evidence: [`criterion '${id}' absent from acceptance report`],
|
|
15
|
+
reason: `missing acceptance criterion '${id}'`
|
|
16
|
+
});
|
|
17
|
+
if (count > 1) unmet.push({
|
|
18
|
+
criterion: id,
|
|
19
|
+
evidence: [`criterion '${id}' reported ${count} times`],
|
|
20
|
+
reason: `duplicate acceptance criterion '${id}'`
|
|
21
|
+
});
|
|
22
|
+
return unmet;
|
|
23
|
+
};
|
|
24
|
+
const hasNonEmptyEvidence = (value) => Array.isArray(value) && value.some((item) => typeof item === "string" && item.trim().length > 0);
|
|
25
|
+
const entryUnmetCriteria = (entry, expectedIds, seen) => {
|
|
26
|
+
const id = typeof entry.id === "string" ? entry.id : "";
|
|
27
|
+
if (id.length === 0) return [{
|
|
28
|
+
criterion: "",
|
|
29
|
+
evidence: ["acceptance entry has no id field"],
|
|
30
|
+
reason: "acceptance entry missing id"
|
|
31
|
+
}];
|
|
32
|
+
seen.set(id, (seen.get(id) ?? 0) + 1);
|
|
33
|
+
const unmet = [];
|
|
34
|
+
if (!expectedIds.has(id)) unmet.push({
|
|
35
|
+
criterion: id,
|
|
36
|
+
evidence: [`id '${id}' not in task acceptance context`],
|
|
37
|
+
reason: `extra acceptance criterion '${id}'`
|
|
38
|
+
});
|
|
39
|
+
const { verdict } = entry;
|
|
40
|
+
if (verdict !== "PASS") unmet.push({
|
|
41
|
+
criterion: id,
|
|
42
|
+
evidence: [`reported verdict '${String(verdict)}'`],
|
|
43
|
+
reason: `acceptance criterion '${id}' verdict '${String(verdict)}'`
|
|
44
|
+
});
|
|
45
|
+
if (verdict === "PASS" && !hasNonEmptyEvidence(entry.evidence)) unmet.push({
|
|
46
|
+
criterion: id,
|
|
47
|
+
evidence: ["verdict 'PASS' reported without supporting evidence"],
|
|
48
|
+
reason: `acceptance criterion '${id}' has no evidence`
|
|
49
|
+
});
|
|
50
|
+
return unmet;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Structured refusal producer (PIPE-90.1): the single source of truth for which
|
|
54
|
+
* acceptance criteria a node failed to satisfy. Each {@link UnmetCriterion}
|
|
55
|
+
* names the criterion, a human-readable reason, and deterministic proof. The
|
|
56
|
+
* acceptance gate's flat `evidence` field is the `reason` projection of this,
|
|
57
|
+
* so refusal text and ordering stay in lockstep with the structured form.
|
|
58
|
+
*/
|
|
59
|
+
const acceptanceUnmetCriteria = (expected, entries) => {
|
|
60
|
+
const unmet = [];
|
|
61
|
+
const expectedIds = new Set(expected.map((criterion) => criterion.id));
|
|
62
|
+
const seen = /* @__PURE__ */ new Map();
|
|
63
|
+
for (const entry of entries) unmet.push(...entryUnmetCriteria(entry, expectedIds, seen));
|
|
64
|
+
for (const id of expectedIds) unmet.push(...coverageCountUnmetCriteria(id, seen.get(id) ?? 0));
|
|
65
|
+
return unmet;
|
|
66
|
+
};
|
|
4
67
|
/**
|
|
5
68
|
* Evaluates whether the node's JSON output (or artifact) covers all acceptance
|
|
6
69
|
* criteria declared in the task context. Uses
|
|
7
70
|
* {@link acceptanceUnmetCriteria} as the single source of truth for which
|
|
8
71
|
* criteria remain unmet, so refusal text and structured form stay in lockstep.
|
|
9
72
|
*/
|
|
10
|
-
|
|
73
|
+
const evaluateAcceptanceGate = (gate, gateId, nodeId, context, attempt, node) => {
|
|
11
74
|
const expected = (node ? effectiveTaskContext(node, context) : context.taskContext)?.acceptanceCriteria ?? [];
|
|
12
75
|
if (expected.length === 0) return {
|
|
13
76
|
evidence: ["no acceptance criteria in task context"],
|
|
@@ -18,7 +81,7 @@ function evaluateAcceptanceGate(gate, gateId, nodeId, context, attempt, node) {
|
|
|
18
81
|
reason: gate.required === false ? void 0 : "missing task acceptance context"
|
|
19
82
|
};
|
|
20
83
|
const parsed = parseGateJson(gate, context, attempt);
|
|
21
|
-
if (parsed.evidence) return {
|
|
84
|
+
if (parsed.evidence !== void 0 && parsed.evidence.length > 0) return {
|
|
22
85
|
evidence: [parsed.evidence],
|
|
23
86
|
gateId,
|
|
24
87
|
kind: gate.kind,
|
|
@@ -26,7 +89,8 @@ function evaluateAcceptanceGate(gate, gateId, nodeId, context, attempt, node) {
|
|
|
26
89
|
passed: false,
|
|
27
90
|
reason: "acceptance gate JSON parse failed"
|
|
28
91
|
};
|
|
29
|
-
const
|
|
92
|
+
const entries = acceptanceEntries(parsed.value, gate.acceptance_key);
|
|
93
|
+
const unmet = acceptanceUnmetCriteria(expected, entries);
|
|
30
94
|
const passed = unmet.length === 0;
|
|
31
95
|
return {
|
|
32
96
|
evidence: passed ? ["acceptance coverage passed"] : unmet.map((item) => item.reason),
|
|
@@ -37,73 +101,6 @@ function evaluateAcceptanceGate(gate, gateId, nodeId, context, attempt, node) {
|
|
|
37
101
|
reason: passed ? void 0 : "acceptance coverage failed",
|
|
38
102
|
unmet
|
|
39
103
|
};
|
|
40
|
-
}
|
|
41
|
-
function effectiveTaskContext(node, context) {
|
|
42
|
-
return node.taskContext ?? context.taskContext;
|
|
43
|
-
}
|
|
44
|
-
function acceptanceEntries(value, key = "acceptance") {
|
|
45
|
-
if (!isRecord(value)) return [];
|
|
46
|
-
const raw = value[key] ?? value.criteria ?? value.acceptanceCriteria;
|
|
47
|
-
return Array.isArray(raw) ? raw.filter((item) => isRecord(item)) : [];
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Structured refusal producer (PIPE-90.1): the single source of truth for which
|
|
51
|
-
* acceptance criteria a node failed to satisfy. Each {@link UnmetCriterion}
|
|
52
|
-
* names the criterion, a human-readable reason, and deterministic proof. The
|
|
53
|
-
* acceptance gate's flat `evidence` field is the `reason` projection of this,
|
|
54
|
-
* so refusal text and ordering stay in lockstep with the structured form.
|
|
55
|
-
*/
|
|
56
|
-
function acceptanceUnmetCriteria(expected, entries) {
|
|
57
|
-
const unmet = [];
|
|
58
|
-
const expectedIds = new Set(expected.map((criterion) => criterion.id));
|
|
59
|
-
const seen = /* @__PURE__ */ new Map();
|
|
60
|
-
for (const entry of entries) unmet.push(...entryUnmetCriteria(entry, expectedIds, seen));
|
|
61
|
-
for (const id of expectedIds) unmet.push(...coverageCountUnmetCriteria(id, seen.get(id) ?? 0));
|
|
62
|
-
return unmet;
|
|
63
|
-
}
|
|
64
|
-
function entryUnmetCriteria(entry, expectedIds, seen) {
|
|
65
|
-
const id = typeof entry.id === "string" ? entry.id : "";
|
|
66
|
-
if (!id) return [{
|
|
67
|
-
criterion: "",
|
|
68
|
-
evidence: ["acceptance entry has no id field"],
|
|
69
|
-
reason: "acceptance entry missing id"
|
|
70
|
-
}];
|
|
71
|
-
seen.set(id, (seen.get(id) ?? 0) + 1);
|
|
72
|
-
const unmet = [];
|
|
73
|
-
if (!expectedIds.has(id)) unmet.push({
|
|
74
|
-
criterion: id,
|
|
75
|
-
evidence: [`id '${id}' not in task acceptance context`],
|
|
76
|
-
reason: `extra acceptance criterion '${id}'`
|
|
77
|
-
});
|
|
78
|
-
const verdict = entry.verdict;
|
|
79
|
-
if (verdict !== "PASS") unmet.push({
|
|
80
|
-
criterion: id,
|
|
81
|
-
evidence: [`reported verdict '${String(verdict)}'`],
|
|
82
|
-
reason: `acceptance criterion '${id}' verdict '${String(verdict)}'`
|
|
83
|
-
});
|
|
84
|
-
if (verdict === "PASS" && !hasNonEmptyEvidence(entry.evidence)) unmet.push({
|
|
85
|
-
criterion: id,
|
|
86
|
-
evidence: ["verdict 'PASS' reported without supporting evidence"],
|
|
87
|
-
reason: `acceptance criterion '${id}' has no evidence`
|
|
88
|
-
});
|
|
89
|
-
return unmet;
|
|
90
|
-
}
|
|
91
|
-
function coverageCountUnmetCriteria(id, count) {
|
|
92
|
-
const unmet = [];
|
|
93
|
-
if (count === 0) unmet.push({
|
|
94
|
-
criterion: id,
|
|
95
|
-
evidence: [`criterion '${id}' absent from acceptance report`],
|
|
96
|
-
reason: `missing acceptance criterion '${id}'`
|
|
97
|
-
});
|
|
98
|
-
if (count > 1) unmet.push({
|
|
99
|
-
criterion: id,
|
|
100
|
-
evidence: [`criterion '${id}' reported ${count} times`],
|
|
101
|
-
reason: `duplicate acceptance criterion '${id}'`
|
|
102
|
-
});
|
|
103
|
-
return unmet;
|
|
104
|
-
}
|
|
105
|
-
function hasNonEmptyEvidence(value) {
|
|
106
|
-
return Array.isArray(value) && value.filter((item) => typeof item === "string" && item.trim()).length > 0;
|
|
107
|
-
}
|
|
104
|
+
};
|
|
108
105
|
//#endregion
|
|
109
106
|
export { acceptanceUnmetCriteria, evaluateAcceptanceGate };
|
|
@@ -2,8 +2,8 @@ import { forKind } from "../../contract.js";
|
|
|
2
2
|
import { evaluateAcceptanceGate } from "./acceptance.js";
|
|
3
3
|
//#region src/runtime/gates/kinds/acceptance/index.ts
|
|
4
4
|
const acceptanceModule = {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
evaluate: forKind("acceptance", (gate, input) => evaluateAcceptanceGate(gate, input.gateId, input.nodeId, input.context, input.attempt, input.node)),
|
|
6
|
+
kind: "acceptance"
|
|
7
7
|
};
|
|
8
8
|
//#endregion
|
|
9
9
|
export { acceptanceModule };
|
|
@@ -4,9 +4,9 @@ import { artifactExists } from "../../../../gates.js";
|
|
|
4
4
|
* Checks whether the configured artifact path exists in the node's worktree.
|
|
5
5
|
* A missing path or nonexistent file fails the gate.
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
const path = gate
|
|
9
|
-
const passed =
|
|
7
|
+
const evaluateArtifactGate = (gate, gateId, nodeId, context) => {
|
|
8
|
+
const { path } = gate;
|
|
9
|
+
const passed = path.length > 0 && artifactExists(context.worktreePath, path);
|
|
10
10
|
return {
|
|
11
11
|
evidence: [passed ? `artifact exists: ${path}` : `missing artifact: ${path}`],
|
|
12
12
|
gateId,
|
|
@@ -15,6 +15,6 @@ function evaluateArtifactGate(gate, gateId, nodeId, context) {
|
|
|
15
15
|
passed,
|
|
16
16
|
reason: passed ? void 0 : `missing artifact '${path}'`
|
|
17
17
|
};
|
|
18
|
-
}
|
|
18
|
+
};
|
|
19
19
|
//#endregion
|
|
20
20
|
export { evaluateArtifactGate };
|
|
@@ -2,8 +2,8 @@ import { forKind } from "../../contract.js";
|
|
|
2
2
|
import { evaluateArtifactGate } from "./artifact.js";
|
|
3
3
|
//#region src/runtime/gates/kinds/artifact/index.ts
|
|
4
4
|
const artifactModule = {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
evaluate: forKind("artifact", (gate, input) => evaluateArtifactGate(gate, input.gateId, input.nodeId, input.context)),
|
|
6
|
+
kind: "artifact"
|
|
7
7
|
};
|
|
8
8
|
//#endregion
|
|
9
9
|
export { artifactModule };
|
|
@@ -6,16 +6,16 @@ import "../../../builtins/index.js";
|
|
|
6
6
|
* Context must satisfy {@link RuntimeContext} since {@link executeBuiltin} uses
|
|
7
7
|
* the full runtime context (cwd, signal, store, etc.).
|
|
8
8
|
*/
|
|
9
|
-
async
|
|
10
|
-
const result = await executeBuiltin(gate.builtin
|
|
9
|
+
const evaluateBuiltinGate = async (gate, gateId, nodeId, context) => {
|
|
10
|
+
const result = await executeBuiltin(gate.builtin, context);
|
|
11
11
|
return {
|
|
12
12
|
evidence: result.evidence,
|
|
13
13
|
gateId,
|
|
14
14
|
kind: gate.kind,
|
|
15
15
|
nodeId,
|
|
16
16
|
passed: result.exitCode === 0,
|
|
17
|
-
reason: result.exitCode === 0 ? void 0 : `builtin '${gate.builtin
|
|
17
|
+
reason: result.exitCode === 0 ? void 0 : `builtin '${gate.builtin}' failed`
|
|
18
18
|
};
|
|
19
|
-
}
|
|
19
|
+
};
|
|
20
20
|
//#endregion
|
|
21
21
|
export { evaluateBuiltinGate };
|
|
@@ -2,8 +2,8 @@ import { forKind } from "../../contract.js";
|
|
|
2
2
|
import { evaluateBuiltinGate } from "./builtin.js";
|
|
3
3
|
//#region src/runtime/gates/kinds/builtin/index.ts
|
|
4
4
|
const builtinModule = {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
evaluate: forKind("builtin", async (gate, input) => await evaluateBuiltinGate(gate, input.gateId, input.nodeId, input.context)),
|
|
6
|
+
kind: "builtin"
|
|
7
7
|
};
|
|
8
8
|
//#endregion
|
|
9
9
|
export { builtinModule };
|