@revisium/orchestrator 0.2.0-alpha.2 → 0.2.0-alpha.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 +3 -3
- package/control-plane/bootstrap.config.json +104 -550
- package/control-plane/default-playbook/catalog/pipelines.json +405 -1065
- package/control-plane/default-playbook/catalog/run-profiles.json +208 -0
- package/control-plane/default-playbook/package.json +1 -1
- package/control-plane/default-playbook/playbook.json +2 -1
- package/control-plane/default-playbook/prompts/developer.md +6 -6
- package/dist/__generated__/client/browser.js +18 -0
- package/dist/__generated__/client/browser.js.map +1 -0
- package/dist/__generated__/client/client.js +32 -0
- package/dist/__generated__/client/client.js.map +1 -0
- package/dist/__generated__/client/commonInputTypes.js +11 -0
- package/dist/__generated__/client/commonInputTypes.js.map +1 -0
- package/dist/__generated__/client/enums.js +19 -0
- package/dist/__generated__/client/enums.js.map +1 -0
- package/dist/__generated__/client/internal/class.js +53 -0
- package/dist/__generated__/client/internal/class.js.map +1 -0
- package/dist/__generated__/client/internal/prismaNamespace.js +451 -0
- package/dist/__generated__/client/internal/prismaNamespace.js.map +1 -0
- package/dist/__generated__/client/internal/prismaNamespaceBrowser.js +422 -0
- package/dist/__generated__/client/internal/prismaNamespaceBrowser.js.map +1 -0
- package/dist/__generated__/client/models/Branch.js +2 -0
- package/dist/__generated__/client/models/Branch.js.map +1 -0
- package/dist/__generated__/client/models/CostLedgerEntry.js +2 -0
- package/dist/__generated__/client/models/CostLedgerEntry.js.map +1 -0
- package/dist/__generated__/client/models/FileBlob.js +2 -0
- package/dist/__generated__/client/models/FileBlob.js.map +1 -0
- package/dist/__generated__/client/models/InboxItem.js +2 -0
- package/dist/__generated__/client/models/InboxItem.js.map +1 -0
- package/dist/__generated__/client/models/ProjectFileUsage.js +2 -0
- package/dist/__generated__/client/models/ProjectFileUsage.js.map +1 -0
- package/dist/__generated__/client/models/Revision.js +2 -0
- package/dist/__generated__/client/models/Revision.js.map +1 -0
- package/dist/__generated__/client/models/RevoProject.js +2 -0
- package/dist/__generated__/client/models/RevoProject.js.map +1 -0
- package/dist/__generated__/client/models/Row.js +2 -0
- package/dist/__generated__/client/models/Row.js.map +1 -0
- package/dist/__generated__/client/models/RunAttempt.js +2 -0
- package/dist/__generated__/client/models/RunAttempt.js.map +1 -0
- package/dist/__generated__/client/models/RunEvent.js +2 -0
- package/dist/__generated__/client/models/RunEvent.js.map +1 -0
- package/dist/__generated__/client/models/RunOutput.js +2 -0
- package/dist/__generated__/client/models/RunOutput.js.map +1 -0
- package/dist/__generated__/client/models/RunTask.js +2 -0
- package/dist/__generated__/client/models/RunTask.js.map +1 -0
- package/dist/__generated__/client/models/Table.js +2 -0
- package/dist/__generated__/client/models/Table.js.map +1 -0
- package/dist/__generated__/client/models/TableMigration.js +2 -0
- package/dist/__generated__/client/models/TableMigration.js.map +1 -0
- package/dist/__generated__/client/models/TaskRun.js +2 -0
- package/dist/__generated__/client/models/TaskRun.js.map +1 -0
- package/dist/__generated__/client/models.js +2 -0
- package/dist/__generated__/client/models.js.map +1 -0
- package/dist/api/graphql-api/graphql-ws/pubsub.module.js +2 -1
- package/dist/api/graphql-api/graphql-ws/pubsub.module.js.map +1 -1
- package/dist/api/graphql-api/graphql-ws/subscription-bridge.service.js +110 -28
- package/dist/api/graphql-api/graphql-ws/subscription-bridge.service.js.map +1 -1
- package/dist/api/graphql-api/graphql-ws/subscription-mappers.js +3 -1
- package/dist/api/graphql-api/graphql-ws/subscription-mappers.js.map +1 -1
- package/dist/api/graphql-api/inbox/inputs/resolve-gate.input.js +46 -0
- package/dist/api/graphql-api/inbox/inputs/resolve-gate.input.js.map +1 -1
- package/dist/api/graphql-api/pr/inputs/pr-readiness.input.js +6 -0
- package/dist/api/graphql-api/pr/inputs/pr-readiness.input.js.map +1 -1
- package/dist/api/graphql-api/registerGraphqlEnums.js +2 -0
- package/dist/api/graphql-api/registerGraphqlEnums.js.map +1 -1
- package/dist/api/graphql-api/runs/inputs/create-run.input.js +17 -0
- package/dist/api/graphql-api/runs/inputs/create-run.input.js.map +1 -1
- package/dist/api/graphql-api/runs/inputs/execution-profile.input.js +85 -0
- package/dist/api/graphql-api/runs/inputs/execution-profile.input.js.map +1 -0
- package/dist/api/graphql-api/runs/inputs/simulate-route.input.js +11 -0
- package/dist/api/graphql-api/runs/inputs/simulate-route.input.js.map +1 -1
- package/dist/api/graphql-api/runs/model/run-workflow.model.js +5 -0
- package/dist/api/graphql-api/runs/model/run-workflow.model.js.map +1 -1
- package/dist/api/graphql-api/share/model/issue-action.model.js +7 -0
- package/dist/api/graphql-api/share/model/issue-action.model.js.map +1 -0
- package/dist/cli/commands/doctor-report.js +1 -13
- package/dist/cli/commands/doctor-report.js.map +1 -1
- package/dist/cli/commands/lifecycle.js +50 -65
- package/dist/cli/commands/lifecycle.js.map +1 -1
- package/dist/cli/commands/{revisium-helpers.js → process-helpers.js} +1 -24
- package/dist/cli/commands/process-helpers.js.map +1 -0
- package/dist/config.js +17 -55
- package/dist/config.js.map +1 -1
- package/dist/control-plane/audit-validation-helpers.js +31 -0
- package/dist/control-plane/audit-validation-helpers.js.map +1 -0
- package/dist/control-plane/bootstrap-json-validator.js +65 -0
- package/dist/control-plane/bootstrap-json-validator.js.map +1 -0
- package/dist/control-plane/bootstrap.js +25 -38
- package/dist/control-plane/bootstrap.js.map +1 -1
- package/dist/control-plane/change-notifications.js +45 -14
- package/dist/control-plane/change-notifications.js.map +1 -1
- package/dist/control-plane/data-access.js +1 -106
- package/dist/control-plane/data-access.js.map +1 -1
- package/dist/control-plane/default-playbook-policy.js +499 -78
- package/dist/control-plane/default-playbook-policy.js.map +1 -1
- package/dist/control-plane/definitions.js +4 -5
- package/dist/control-plane/definitions.js.map +1 -1
- package/dist/control-plane/engine-transport.js +381 -0
- package/dist/control-plane/engine-transport.js.map +1 -0
- package/dist/control-plane/inbox.js +15 -7
- package/dist/control-plane/inbox.js.map +1 -1
- package/dist/control-plane/index.js +0 -1
- package/dist/control-plane/index.js.map +1 -1
- package/dist/control-plane/manual-adoption-audit.js +3 -24
- package/dist/control-plane/manual-adoption-audit.js.map +1 -1
- package/dist/control-plane/merge-override-audit.js +44 -0
- package/dist/control-plane/merge-override-audit.js.map +1 -0
- package/dist/control-plane/pipeline-coverage-registry.js +478 -0
- package/dist/control-plane/pipeline-coverage-registry.js.map +1 -0
- package/dist/control-plane/query-types.js +2 -0
- package/dist/control-plane/query-types.js.map +1 -0
- package/dist/control-plane/run-profiles.js +89 -0
- package/dist/control-plane/run-profiles.js.map +1 -0
- package/dist/control-plane/schema-migration.js +0 -35
- package/dist/control-plane/schema-migration.js.map +1 -1
- package/dist/control-plane/seed-default-playbook.js +2 -31
- package/dist/control-plane/seed-default-playbook.js.map +1 -1
- package/dist/control-plane/tables.js +8 -0
- package/dist/control-plane/tables.js.map +1 -1
- package/dist/control-plane/transport.js +16 -0
- package/dist/control-plane/transport.js.map +1 -0
- package/dist/control-plane/versioned-meaning.js +56 -14
- package/dist/control-plane/versioned-meaning.js.map +1 -1
- package/dist/e2e/kit/agents.js +60 -6
- package/dist/e2e/kit/agents.js.map +1 -1
- package/dist/e2e/kit/assertions.js +13 -1
- package/dist/e2e/kit/assertions.js.map +1 -1
- package/dist/e2e/kit/drive.js +46 -11
- package/dist/e2e/kit/drive.js.map +1 -1
- package/dist/e2e/kit/env.js +17 -1
- package/dist/e2e/kit/env.js.map +1 -1
- package/dist/e2e/kit/fake-integrator.js +25 -5
- package/dist/e2e/kit/fake-integrator.js.map +1 -1
- package/dist/e2e/kit/gh-emulator.js +172 -40
- package/dist/e2e/kit/gh-emulator.js.map +1 -1
- package/dist/e2e/kit/harness.js +44 -10
- package/dist/e2e/kit/harness.js.map +1 -1
- package/dist/e2e/kit/index.js +1 -0
- package/dist/e2e/kit/index.js.map +1 -1
- package/dist/e2e/kit/scenario.js +165 -0
- package/dist/e2e/kit/scenario.js.map +1 -0
- package/dist/e2e/kit/scenarios.js +12 -18
- package/dist/e2e/kit/scenarios.js.map +1 -1
- package/dist/engine/dbos.service.js +8 -1
- package/dist/engine/dbos.service.js.map +1 -1
- package/dist/features/inbox/commands/impl/resolve-gate.command.js.map +1 -1
- package/dist/features/pr/queries/impl/get-pr-readiness.query.js.map +1 -1
- package/dist/features/runs/commands/impl/create-run.command.js.map +1 -1
- package/dist/host/daemon.js +32 -8
- package/dist/host/daemon.js.map +1 -1
- package/dist/host/ensure-host.js +3 -3
- package/dist/host/ensure-host.js.map +1 -1
- package/dist/host/host-runtime.js.map +1 -1
- package/dist/host/host.lifecycle.js +3 -16
- package/dist/host/host.lifecycle.js.map +1 -1
- package/dist/http/graphql-host.js +17 -8
- package/dist/http/graphql-host.js.map +1 -1
- package/dist/mcp/mcp-capabilities.js +12 -11
- package/dist/mcp/mcp-capabilities.js.map +1 -1
- package/dist/mcp/mcp-facade.service.js +45 -5
- package/dist/mcp/mcp-facade.service.js.map +1 -1
- package/dist/mcp/mcp-tools.js +59 -5
- package/dist/mcp/mcp-tools.js.map +1 -1
- package/dist/mcp/monitoring-directive.js +43 -0
- package/dist/mcp/monitoring-directive.js.map +1 -0
- package/dist/pipeline/data-driven-task.workflow.js +393 -182
- package/dist/pipeline/data-driven-task.workflow.js.map +1 -1
- package/dist/pipeline/pipeline.service.js +27 -5
- package/dist/pipeline/pipeline.service.js.map +1 -1
- package/dist/pipeline/route-contract.js +123 -3
- package/dist/pipeline/route-contract.js.map +1 -1
- package/dist/pipeline-core/condition-scopes.js +15 -0
- package/dist/pipeline-core/condition-scopes.js.map +1 -0
- package/dist/pipeline-core/index.js +2 -0
- package/dist/pipeline-core/index.js.map +1 -1
- package/dist/pipeline-core/kit/fixtures.js +81 -44
- package/dist/pipeline-core/kit/fixtures.js.map +1 -1
- package/dist/pipeline-core/materialize.js +221 -0
- package/dist/pipeline-core/materialize.js.map +1 -0
- package/dist/pipeline-core/types.js +5 -0
- package/dist/pipeline-core/types.js.map +1 -1
- package/dist/pipeline-core/validate-loops.js +2 -15
- package/dist/pipeline-core/validate-loops.js.map +1 -1
- package/dist/pipeline-core/validate-resilience.js +257 -0
- package/dist/pipeline-core/validate-resilience.js.map +1 -0
- package/dist/pipeline-core/validate.js +2 -0
- package/dist/pipeline-core/validate.js.map +1 -1
- package/dist/playbook/catalog-loader.js +39 -2
- package/dist/playbook/catalog-loader.js.map +1 -1
- package/dist/playbook/catalog-schema-validator.js +438 -0
- package/dist/playbook/catalog-schema-validator.js.map +1 -0
- package/dist/playbook/import-mapper.js +41 -0
- package/dist/playbook/import-mapper.js.map +1 -1
- package/dist/playbook/manifest.js +5 -0
- package/dist/playbook/manifest.js.map +1 -1
- package/dist/playbook/playbook-installer.js +17 -1
- package/dist/playbook/playbook-installer.js.map +1 -1
- package/dist/poller/pr-readiness-core.js +280 -94
- package/dist/poller/pr-readiness-core.js.map +1 -1
- package/dist/poller/pr-readiness.js +2 -2
- package/dist/poller/pr-readiness.js.map +1 -1
- package/dist/revisium/inbox.service.js +8 -8
- package/dist/revisium/inbox.service.js.map +1 -1
- package/dist/revisium/playbooks.service.js +116 -21
- package/dist/revisium/playbooks.service.js.map +1 -1
- package/dist/revisium/revisium.module.js +8 -8
- package/dist/revisium/revisium.module.js.map +1 -1
- package/dist/revisium/roles.service.js +3 -1
- package/dist/revisium/roles.service.js.map +1 -1
- package/dist/revisium/run.service.js +9 -8
- package/dist/revisium/run.service.js.map +1 -1
- package/dist/revisium/tokens.js +0 -1
- package/dist/revisium/tokens.js.map +1 -1
- package/dist/run/create-run.js +1 -1
- package/dist/run/create-run.js.map +1 -1
- package/dist/run/inspect-run.js +32 -28
- package/dist/run/inspect-run.js.map +1 -1
- package/dist/run/issue-ref.js +97 -2
- package/dist/run/issue-ref.js.map +1 -1
- package/dist/run/prisma-runtime-data-access.js +619 -0
- package/dist/run/prisma-runtime-data-access.js.map +1 -0
- package/dist/run/run-outputs.js +39 -14
- package/dist/run/run-outputs.js.map +1 -1
- package/dist/run/terminal-run-status.js.map +1 -1
- package/dist/runners/integrator.js +657 -56
- package/dist/runners/integrator.js.map +1 -1
- package/dist/runners/worktree.service.js +10 -8
- package/dist/runners/worktree.service.js.map +1 -1
- package/dist/schema/ajv-errors.js +15 -0
- package/dist/schema/ajv-errors.js.map +1 -0
- package/dist/smoke/isolation.js +7 -16
- package/dist/smoke/isolation.js.map +1 -1
- package/dist/storage/ensure-storage.js +117 -0
- package/dist/storage/ensure-storage.js.map +1 -0
- package/dist/storage/revo-database.js +39 -0
- package/dist/storage/revo-database.js.map +1 -0
- package/dist/storage/revo-prisma.service.js +31 -0
- package/dist/storage/revo-prisma.service.js.map +1 -0
- package/dist/storage/revo-storage.module.js +18 -0
- package/dist/storage/revo-storage.module.js.map +1 -0
- package/dist/task-control-plane/pr-readiness.service.js +5 -2
- package/dist/task-control-plane/pr-readiness.service.js.map +1 -1
- package/dist/task-control-plane/run-watch.service.js +3 -6
- package/dist/task-control-plane/run-watch.service.js.map +1 -1
- package/dist/task-control-plane/task-control-plane-api.service.js +234 -43
- package/dist/task-control-plane/task-control-plane-api.service.js.map +1 -1
- package/dist/testing/runtime-data-access.js +175 -0
- package/dist/testing/runtime-data-access.js.map +1 -0
- package/dist/worker/build-context.js +178 -2
- package/dist/worker/build-context.js.map +1 -1
- package/dist/worker/claude-code-runner.js +22 -3
- package/dist/worker/claude-code-runner.js.map +1 -1
- package/dist/worker/codex-runner.js +6 -1
- package/dist/worker/codex-runner.js.map +1 -1
- package/package.json +20 -7
- package/prisma/migrations/20260707080500_initial_schema/migration.sql +259 -0
- package/prisma/migrations/20260707103000_prisma_runtime_tables/migration.sql +235 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +339 -0
- package/prisma.config.ts +11 -0
- package/dist/cli/commands/revisium-helpers.js.map +0 -1
- package/dist/control-plane/client-transport.js +0 -168
- package/dist/control-plane/client-transport.js.map +0 -1
- package/dist/engine/ensure-postgres.js +0 -46
- package/dist/engine/ensure-postgres.js.map +0 -1
- package/dist/host/ensure-revisium.js +0 -135
- package/dist/host/ensure-revisium.js.map +0 -1
- package/revisium.config.json +0 -10
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { step as coreStep, initialState, validateTemplate, InterpretError, selectJoinWinner, reduceJoinVerdict, } from '../pipeline-core/index.js';
|
|
2
|
-
import { runnerNeedsLivePreflight, runnerUsesRealIntegrator } from './route-contract.js';
|
|
2
|
+
import { resolveLaunchOverrides, runnerNeedsLivePreflight, runnerUsesRealIntegrator } from './route-contract.js';
|
|
3
3
|
import { redactEventPayload } from '../run/append-event.js';
|
|
4
4
|
import { redactSecrets } from '../control-plane/inbox.js';
|
|
5
5
|
import { fnv1a64Hex } from '../control-plane/steps.js';
|
|
6
|
-
import { normalizeIssueRef } from '../run/issue-ref.js';
|
|
6
|
+
import { normalizeIssueAction, normalizeIssueRef } from '../run/issue-ref.js';
|
|
7
7
|
import { RUNNER_IDLE_TIMEOUT_KIND, RUNNER_WALL_CLOCK_LIMIT_KIND, } from '../worker/process-executor.js';
|
|
8
8
|
export const RUN_PROGRESS_EVENT_KEY = 'run-progress';
|
|
9
|
+
export const INTEGRATOR_PROGRESS_EVENT_TYPES = ['integrate_succeeded', 'foreign_pr_adopted'];
|
|
9
10
|
const MAX_STEPS = 1_000;
|
|
10
11
|
const DEFAULT_RUNNER_TRANSIENT_MAX_ATTEMPTS = 2;
|
|
11
12
|
const DEFAULT_RUNNER_TRANSIENT_RETRY_BACKOFF_MS = 2_000;
|
|
@@ -108,10 +109,12 @@ function producedChangeArtifact(value) {
|
|
|
108
109
|
if (typeof headSha !== 'string' || headSha.trim().length === 0)
|
|
109
110
|
return undefined;
|
|
110
111
|
const issueRef = normalizeArtifactIssueRef(candidate.issueRef);
|
|
112
|
+
const issueAction = normalizeArtifactIssueAction(candidate.issueAction);
|
|
111
113
|
return {
|
|
112
114
|
branch,
|
|
113
115
|
headSha,
|
|
114
116
|
...(issueRef ? { issueRef } : {}),
|
|
117
|
+
...(issueAction ? { issueAction } : {}),
|
|
115
118
|
...(typeof candidate.worktreePath === 'string' && candidate.worktreePath.trim() ? { worktreePath: candidate.worktreePath } : {}),
|
|
116
119
|
...(typeof candidate.artifactRef === 'string' && candidate.artifactRef.trim() ? { artifactRef: candidate.artifactRef } : {}),
|
|
117
120
|
...(typeof candidate.prNumber === 'number' && Number.isSafeInteger(candidate.prNumber) ? { prNumber: candidate.prNumber } : {}),
|
|
@@ -125,7 +128,15 @@ function normalizeArtifactIssueRef(value) {
|
|
|
125
128
|
return undefined;
|
|
126
129
|
}
|
|
127
130
|
}
|
|
128
|
-
function
|
|
131
|
+
function normalizeArtifactIssueAction(value) {
|
|
132
|
+
try {
|
|
133
|
+
return normalizeIssueAction(value, 'change.issueAction');
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function changeWithRunIssueContract(change, issueRef, issueAction) {
|
|
129
140
|
const normalized = { ...change };
|
|
130
141
|
if (issueRef) {
|
|
131
142
|
normalized.issueRef = issueRef;
|
|
@@ -133,6 +144,12 @@ function changeWithRunIssueRef(change, issueRef) {
|
|
|
133
144
|
else {
|
|
134
145
|
delete normalized.issueRef;
|
|
135
146
|
}
|
|
147
|
+
if (issueAction) {
|
|
148
|
+
normalized.issueAction = issueAction;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
delete normalized.issueAction;
|
|
152
|
+
}
|
|
136
153
|
return normalized;
|
|
137
154
|
}
|
|
138
155
|
function producedChangeFromInputs(inputs) {
|
|
@@ -150,7 +167,16 @@ function mergeReadinessFromInputs(inputs) {
|
|
|
150
167
|
const headSha = value.headSha;
|
|
151
168
|
if (typeof headSha !== 'string' || headSha.trim().length === 0)
|
|
152
169
|
return undefined;
|
|
153
|
-
|
|
170
|
+
const override = isRecord(value.override) && value.override.accepted === true
|
|
171
|
+
? { accepted: true }
|
|
172
|
+
: undefined;
|
|
173
|
+
return { headSha, ...(override ? { override } : {}) };
|
|
174
|
+
}
|
|
175
|
+
function headShaFromPayload(value) {
|
|
176
|
+
if (!isRecord(value))
|
|
177
|
+
return undefined;
|
|
178
|
+
const headSha = value.headSha;
|
|
179
|
+
return typeof headSha === 'string' && headSha.trim().length > 0 ? headSha.trim() : undefined;
|
|
154
180
|
}
|
|
155
181
|
function attachProducedChange(output, change) {
|
|
156
182
|
if (isRecord(output))
|
|
@@ -275,13 +301,24 @@ function gateVerdict(decision, outcomes) {
|
|
|
275
301
|
return outcomes[0];
|
|
276
302
|
return outcomes.length > 1 ? outcomes.at(-1) : undefined;
|
|
277
303
|
}
|
|
278
|
-
function gateResolutionOutput(decision, verdict, fallbackInboxId) {
|
|
304
|
+
function gateResolutionOutput(decision, verdict, fallbackInboxId, trustedGateHeadSha) {
|
|
305
|
+
const decisionRecord = isRecord(decision) ? decision : {};
|
|
306
|
+
const answer = isRecord(decision.answer) ? decision.answer : {};
|
|
307
|
+
const note = decision.note ?? (typeof answer.note === 'string' ? answer.note : undefined);
|
|
308
|
+
const resolvedBy = decision.resolvedBy ?? (typeof answer.resolvedBy === 'string' ? answer.resolvedBy : '');
|
|
309
|
+
const resolvedAt = decision.resolvedAt ?? (typeof answer.resolvedAt === 'string' ? answer.resolvedAt : '');
|
|
310
|
+
const inboxId = decision.inboxId ?? (typeof answer.inboxId === 'string' ? answer.inboxId : fallbackInboxId);
|
|
311
|
+
const mergeOverrideAudit = answer.mergeOverrideAudit ?? decisionRecord['mergeOverrideAudit'];
|
|
312
|
+
const adoptionAudit = answer.adoptionAudit ?? decisionRecord['adoptionAudit'];
|
|
279
313
|
return {
|
|
280
314
|
outcome: decision.outcome ?? verdict ?? decision.decision,
|
|
281
|
-
...(
|
|
282
|
-
resolvedBy
|
|
283
|
-
resolvedAt
|
|
284
|
-
inboxId
|
|
315
|
+
...(note !== undefined ? { note } : {}),
|
|
316
|
+
resolvedBy,
|
|
317
|
+
resolvedAt,
|
|
318
|
+
inboxId,
|
|
319
|
+
...(trustedGateHeadSha !== undefined ? { trustedGateHeadSha } : {}),
|
|
320
|
+
...(mergeOverrideAudit !== undefined ? { mergeOverrideAudit } : {}),
|
|
321
|
+
...(adoptionAudit !== undefined ? { adoptionAudit } : {}),
|
|
285
322
|
...(decision.decision ? { decision: decision.decision } : {}),
|
|
286
323
|
};
|
|
287
324
|
}
|
|
@@ -317,9 +354,19 @@ function gateArtifactView(row, as) {
|
|
|
317
354
|
payloadRef: `attempt:${row.attemptId ?? ''}`,
|
|
318
355
|
};
|
|
319
356
|
}
|
|
320
|
-
|
|
357
|
+
function freshMergeGateArtifact(decision, lastVerdict, lastProducedOutput) {
|
|
358
|
+
if (decision.nodeId !== 'mergeGate')
|
|
359
|
+
return undefined;
|
|
360
|
+
if (lastProducedOutput?.nodeId === 'mergeRecheck' && lastVerdict === 'clean')
|
|
361
|
+
return lastProducedOutput;
|
|
362
|
+
if (lastProducedOutput?.nodeId === 'mergeApproveReverify' && lastVerdict === 'recheck')
|
|
363
|
+
return lastProducedOutput;
|
|
364
|
+
return undefined;
|
|
365
|
+
}
|
|
366
|
+
export function buildGateSummary(decision, outputsByNode, lastVerdict, lastProducedOutput) {
|
|
321
367
|
const summary = { nodeId: decision.nodeId, outcomes: decision.outcomes };
|
|
322
|
-
const artRow =
|
|
368
|
+
const artRow = freshMergeGateArtifact(decision, lastVerdict, lastProducedOutput)
|
|
369
|
+
?? resolveGateRow(decision.gatedArtifact, outputsByNode);
|
|
323
370
|
if (artRow)
|
|
324
371
|
summary.gatedArtifact = gateArtifactView(artRow, decision.gatedArtifact?.as);
|
|
325
372
|
const verdictRow = resolveGateRow(decision.verdictFrom, outputsByNode);
|
|
@@ -329,8 +376,279 @@ export function buildGateSummary(decision, outputsByNode, lastVerdict) {
|
|
|
329
376
|
summary.reviewerVerdict = { verdict: lastVerdict };
|
|
330
377
|
return summary;
|
|
331
378
|
}
|
|
379
|
+
function approvedMergeGateHeadSha(decision, verdict, summary) {
|
|
380
|
+
if (decision.nodeId !== 'mergeGate')
|
|
381
|
+
return undefined;
|
|
382
|
+
if (verdict === 'approved' || verdict === 'override_merge') {
|
|
383
|
+
return headShaFromPayload(summary.gatedArtifact?.payload) ?? null;
|
|
384
|
+
}
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
function approvedHeadMove(decision, approvedHeadSha, pointer, verdict) {
|
|
388
|
+
if (decision.nodeId !== 'mergeApproveReverify' || verdict !== 'clean' || approvedHeadSha === undefined)
|
|
389
|
+
return undefined;
|
|
390
|
+
const reverifyHeadSha = headShaFromPayload(pointer);
|
|
391
|
+
if (reverifyHeadSha === undefined || reverifyHeadSha === approvedHeadSha)
|
|
392
|
+
return undefined;
|
|
393
|
+
return { approvedHeadSha, reverifyHeadSha };
|
|
394
|
+
}
|
|
395
|
+
function reapprovalRequiredFeedback(pointer, approvedHeadSha, reverifyHeadSha) {
|
|
396
|
+
if (!isRecord(pointer))
|
|
397
|
+
return pointer;
|
|
398
|
+
const evidence = Array.isArray(pointer.evidence)
|
|
399
|
+
? pointer.evidence.filter((item) => typeof item === 'string')
|
|
400
|
+
: [];
|
|
401
|
+
return {
|
|
402
|
+
...pointer,
|
|
403
|
+
verdict: 'recheck',
|
|
404
|
+
evidence: [
|
|
405
|
+
...evidence,
|
|
406
|
+
`approved mergeGate headSha=${approvedHeadSha}; reverify headSha=${reverifyHeadSha}; reapproval required`,
|
|
407
|
+
],
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function reopenNodeState(state, nodeId) {
|
|
411
|
+
return { ...state, activeNodeIds: new Set([nodeId]), status: 'running' };
|
|
412
|
+
}
|
|
413
|
+
function integratorResultPointer(result) {
|
|
414
|
+
return {
|
|
415
|
+
prUrl: result.prUrl,
|
|
416
|
+
branch: result.branch,
|
|
417
|
+
prNumber: result.prNumber,
|
|
418
|
+
headSha: result.headSha,
|
|
419
|
+
status: result.status,
|
|
420
|
+
...(result.issueRef ? { issueRef: result.issueRef } : {}),
|
|
421
|
+
...(result.foreignPr ? { foreignPr: true } : {}),
|
|
422
|
+
...(result.prAuthor ? { prAuthor: result.prAuthor } : {}),
|
|
423
|
+
...(result.integratorAccount ? { integratorAccount: result.integratorAccount } : {}),
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
function integratorProgressEventType(result) {
|
|
427
|
+
return result.foreignPr ? 'foreign_pr_adopted' : 'integrate_succeeded';
|
|
428
|
+
}
|
|
429
|
+
function mergeOverrideEventPayload(result) {
|
|
430
|
+
const audit = result.override.audit;
|
|
431
|
+
return {
|
|
432
|
+
actor: result.override.actor,
|
|
433
|
+
note: result.override.note,
|
|
434
|
+
reason: audit?.reason ?? result.override.reason ?? '',
|
|
435
|
+
risk: audit?.risk ?? '',
|
|
436
|
+
verificationResponsibility: audit?.verificationResponsibility ?? '',
|
|
437
|
+
headSha: audit?.headSha ?? result.headSha,
|
|
438
|
+
freshHeadSha: result.headSha,
|
|
439
|
+
prNumber: result.prNumber,
|
|
440
|
+
source: result.override.source,
|
|
441
|
+
overriddenFacts: result.override.facts,
|
|
442
|
+
replied: result.override.replied,
|
|
443
|
+
resolved: result.override.resolved,
|
|
444
|
+
...(result.override.reason ? { refusalReason: result.override.reason } : {}),
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
export function buildSystemScriptRegistry(deps) {
|
|
448
|
+
const { appendEvent, releaseWorktreeFn, integrateFn, runStub, confirmMergeFn, runConfirmStub, pollPrFn, runPollStub, overrideMergeFn, runOverrideStub, respondThreadsFn, runRespondStub, } = deps;
|
|
449
|
+
function buildIntegratorInput(runId, ctx, inputs) {
|
|
450
|
+
const { taskId, title, base, issueRef, issueAction } = ctx;
|
|
451
|
+
const change = producedChangeFromInputs(inputs);
|
|
452
|
+
const mergeReadiness = mergeReadinessFromInputs(inputs);
|
|
453
|
+
const changeForIntegrator = change ? changeWithRunIssueContract(change, issueRef, issueAction) : undefined;
|
|
454
|
+
return {
|
|
455
|
+
runId,
|
|
456
|
+
taskId,
|
|
457
|
+
title,
|
|
458
|
+
base,
|
|
459
|
+
...(issueRef ? { issueRef } : {}),
|
|
460
|
+
...(issueAction ? { issueAction } : {}),
|
|
461
|
+
...(changeForIntegrator ? { change: changeForIntegrator } : {}),
|
|
462
|
+
...(inputs.triage === undefined ? {} : { triage: inputs.triage }),
|
|
463
|
+
...(inputs.gateResolution === undefined ? {} : { gateResolution: inputs.gateResolution }),
|
|
464
|
+
...(mergeReadiness ? { mergeReadiness } : {}),
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
function makeIntegratorScript(desc) {
|
|
468
|
+
return async ({ runId, decision, ctx, bindingByRef, stepKey, inputs }) => {
|
|
469
|
+
const integratorInput = buildIntegratorInput(runId, ctx, inputs);
|
|
470
|
+
const binding = bindingByRef.get(decision.scriptRef) ?? bindingByRef.get('script:integrator');
|
|
471
|
+
const useReal = !!binding && runnerUsesRealIntegrator(binding.resolvedRunnerId);
|
|
472
|
+
let result;
|
|
473
|
+
try {
|
|
474
|
+
result = useReal ? await desc.real(integratorInput) : desc.stub(integratorInput);
|
|
475
|
+
}
|
|
476
|
+
catch (err) {
|
|
477
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
478
|
+
await appendEvent({
|
|
479
|
+
runId, taskId: ctx.taskId, stepId: '', stepKey,
|
|
480
|
+
type: 'step_failed',
|
|
481
|
+
payload: { scriptRef: decision.scriptRef, error: reason },
|
|
482
|
+
});
|
|
483
|
+
return { outcome: 'failed', reason };
|
|
484
|
+
}
|
|
485
|
+
if ('needsHuman' in result) {
|
|
486
|
+
await appendEvent({
|
|
487
|
+
runId, taskId: ctx.taskId, stepId: '', stepKey: 'pipeline',
|
|
488
|
+
type: 'pipeline_blocked',
|
|
489
|
+
payload: { reason: desc.blockedReason, lesson: result.lesson, nodeId: decision.nodeId },
|
|
490
|
+
});
|
|
491
|
+
return { outcome: 'blocked' };
|
|
492
|
+
}
|
|
493
|
+
const { eventType, payload, pointer, verdict } = desc.mapSuccess(result);
|
|
494
|
+
await appendEvent({ runId, taskId: ctx.taskId, stepId: '', stepKey, type: eventType, payload });
|
|
495
|
+
return { outcome: 'ok', pointer, ...(verdict !== undefined ? { verdict } : {}) };
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
const cleanupWorktree = async ({ runId, decision, ctx, stepKey }) => {
|
|
499
|
+
let releaseResult;
|
|
500
|
+
try {
|
|
501
|
+
releaseResult = await releaseWorktreeFn(runId, ctx.taskId);
|
|
502
|
+
}
|
|
503
|
+
catch (err) {
|
|
504
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
505
|
+
await appendEvent({
|
|
506
|
+
runId,
|
|
507
|
+
taskId: ctx.taskId,
|
|
508
|
+
stepId: '',
|
|
509
|
+
stepKey,
|
|
510
|
+
type: 'cleanup_failed',
|
|
511
|
+
payload: { nodeId: decision.nodeId, error, released: false },
|
|
512
|
+
});
|
|
513
|
+
return { outcome: 'ok', pointer: { released: false, error } };
|
|
514
|
+
}
|
|
515
|
+
if (!releaseResult.released) {
|
|
516
|
+
const pointer = {
|
|
517
|
+
released: false,
|
|
518
|
+
reason: releaseResult.reason,
|
|
519
|
+
worktreePath: releaseResult.worktreePath,
|
|
520
|
+
};
|
|
521
|
+
await appendEvent({
|
|
522
|
+
runId,
|
|
523
|
+
taskId: ctx.taskId,
|
|
524
|
+
stepId: '',
|
|
525
|
+
stepKey,
|
|
526
|
+
type: 'cleanup_failed',
|
|
527
|
+
payload: { nodeId: decision.nodeId, ...pointer },
|
|
528
|
+
});
|
|
529
|
+
return { outcome: 'ok', pointer };
|
|
530
|
+
}
|
|
531
|
+
await appendEvent({ runId, taskId: ctx.taskId, stepId: '', stepKey, type: 'worktree_released', payload: { nodeId: decision.nodeId } });
|
|
532
|
+
return { outcome: 'ok', pointer: { released: true } };
|
|
533
|
+
};
|
|
534
|
+
const integratorScript = makeIntegratorScript({
|
|
535
|
+
real: integrateFn,
|
|
536
|
+
stub: runStub,
|
|
537
|
+
blockedReason: 'integrate',
|
|
538
|
+
mapSuccess: (result) => ({
|
|
539
|
+
eventType: integratorProgressEventType(result),
|
|
540
|
+
payload: integratorResultPointer(result),
|
|
541
|
+
pointer: integratorResultPointer(result),
|
|
542
|
+
}),
|
|
543
|
+
});
|
|
544
|
+
const confirmMergeScript = makeIntegratorScript({
|
|
545
|
+
real: confirmMergeFn,
|
|
546
|
+
stub: runConfirmStub,
|
|
547
|
+
blockedReason: 'confirm-merge',
|
|
548
|
+
mapSuccess: (result) => ({
|
|
549
|
+
eventType: 'merge_confirmed',
|
|
550
|
+
payload: {
|
|
551
|
+
prNumber: result.prNumber,
|
|
552
|
+
prUrl: result.prUrl,
|
|
553
|
+
...(result.issueRef ? { issueRef: result.issueRef } : {}),
|
|
554
|
+
},
|
|
555
|
+
pointer: {
|
|
556
|
+
merged: true,
|
|
557
|
+
prNumber: result.prNumber,
|
|
558
|
+
prUrl: result.prUrl,
|
|
559
|
+
...(result.issueRef ? { issueRef: result.issueRef } : {}),
|
|
560
|
+
},
|
|
561
|
+
}),
|
|
562
|
+
});
|
|
563
|
+
const pollPrScript = makeIntegratorScript({
|
|
564
|
+
real: pollPrFn,
|
|
565
|
+
stub: runPollStub,
|
|
566
|
+
blockedReason: 'poll-pr',
|
|
567
|
+
mapSuccess: (result) => ({
|
|
568
|
+
eventType: 'pr_polled',
|
|
569
|
+
payload: {
|
|
570
|
+
prNumber: result.prNumber,
|
|
571
|
+
headSha: result.headSha,
|
|
572
|
+
verdict: result.verdict,
|
|
573
|
+
evidence: result.evidence,
|
|
574
|
+
ciFailures: result.ciFailures.length,
|
|
575
|
+
reviewThreads: result.reviewThreads.length,
|
|
576
|
+
...(result.issueRef ? { issueRef: result.issueRef } : {}),
|
|
577
|
+
},
|
|
578
|
+
pointer: result,
|
|
579
|
+
verdict: result.verdict,
|
|
580
|
+
}),
|
|
581
|
+
});
|
|
582
|
+
const overrideMergeScript = async ({ runId, decision, ctx, bindingByRef, stepKey, inputs }) => {
|
|
583
|
+
const integratorInput = buildIntegratorInput(runId, ctx, inputs);
|
|
584
|
+
const binding = bindingByRef.get(decision.scriptRef) ?? bindingByRef.get('script:integrator');
|
|
585
|
+
const useReal = !!binding && runnerUsesRealIntegrator(binding.resolvedRunnerId);
|
|
586
|
+
let result;
|
|
587
|
+
try {
|
|
588
|
+
result = useReal ? await overrideMergeFn(integratorInput) : runOverrideStub(integratorInput);
|
|
589
|
+
}
|
|
590
|
+
catch (err) {
|
|
591
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
592
|
+
await appendEvent({
|
|
593
|
+
runId, taskId: ctx.taskId, stepId: '', stepKey,
|
|
594
|
+
type: 'step_failed',
|
|
595
|
+
payload: { scriptRef: decision.scriptRef, error: reason },
|
|
596
|
+
});
|
|
597
|
+
return { outcome: 'failed', reason };
|
|
598
|
+
}
|
|
599
|
+
if ('needsHuman' in result) {
|
|
600
|
+
await appendEvent({
|
|
601
|
+
runId, taskId: ctx.taskId, stepId: '', stepKey: 'pipeline',
|
|
602
|
+
type: 'pipeline_blocked',
|
|
603
|
+
payload: { reason: 'override-merge', lesson: result.lesson, nodeId: decision.nodeId },
|
|
604
|
+
});
|
|
605
|
+
return { outcome: 'blocked' };
|
|
606
|
+
}
|
|
607
|
+
if (result.override.replied > 0 || result.override.resolved > 0) {
|
|
608
|
+
await appendEvent({
|
|
609
|
+
runId,
|
|
610
|
+
taskId: ctx.taskId,
|
|
611
|
+
stepId: '',
|
|
612
|
+
stepKey,
|
|
613
|
+
type: 'threads_responded',
|
|
614
|
+
payload: { replied: result.override.replied, resolved: result.override.resolved },
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
if (result.override.accepted || result.verdict !== 'merged') {
|
|
618
|
+
await appendEvent({
|
|
619
|
+
runId,
|
|
620
|
+
taskId: ctx.taskId,
|
|
621
|
+
stepId: '',
|
|
622
|
+
stepKey,
|
|
623
|
+
type: result.override.accepted ? 'merge_overridden' : 'merge_override_refused',
|
|
624
|
+
payload: mergeOverrideEventPayload(result),
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
return { outcome: 'ok', pointer: result, verdict: result.verdict };
|
|
628
|
+
};
|
|
629
|
+
const respondThreadsScript = makeIntegratorScript({
|
|
630
|
+
real: respondThreadsFn,
|
|
631
|
+
stub: runRespondStub,
|
|
632
|
+
blockedReason: 'respond-threads',
|
|
633
|
+
mapSuccess: (result) => ({
|
|
634
|
+
eventType: 'threads_responded',
|
|
635
|
+
payload: { replied: result.replied, resolved: result.resolved },
|
|
636
|
+
pointer: result,
|
|
637
|
+
}),
|
|
638
|
+
});
|
|
639
|
+
return new Map([
|
|
640
|
+
['script:cleanupWorktree', cleanupWorktree],
|
|
641
|
+
['script:confirmMerge', confirmMergeScript],
|
|
642
|
+
['script:pollPr', pollPrScript],
|
|
643
|
+
['script:overrideMerge', overrideMergeScript],
|
|
644
|
+
['script:respondThreads', respondThreadsScript],
|
|
645
|
+
// Unknown refs fall through to this entry, matching the original else-branch behavior.
|
|
646
|
+
['script:integrator', integratorScript],
|
|
647
|
+
]);
|
|
648
|
+
}
|
|
332
649
|
export function makeDataDrivenTask(runStepFn, deps) {
|
|
333
|
-
const { appendEvent, appendRunOutput, awaitHuman, completeRun, failRun, blockRun, cancelRun, loadRunTaskContext,
|
|
650
|
+
const { appendEvent, appendRunOutput, awaitHuman, completeRun, failRun, blockRun, cancelRun, loadRunTaskContext, captureChangeFn, preflightFn, createWorktreeFn } = deps;
|
|
651
|
+
const scriptRegistry = buildSystemScriptRegistry(deps);
|
|
334
652
|
function resolveConsumes(node, outputsByNode) {
|
|
335
653
|
const refs = 'consumes' in node ? (node.consumes ?? []) : [];
|
|
336
654
|
const inputs = {};
|
|
@@ -363,7 +681,7 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
363
681
|
}
|
|
364
682
|
async function recordOutput(runId, node, ordinal, attemptId, output, outputsByNode) {
|
|
365
683
|
if (!('produces' in node) || !node.produces)
|
|
366
|
-
return;
|
|
684
|
+
return undefined;
|
|
367
685
|
const row = {
|
|
368
686
|
runId,
|
|
369
687
|
nodeId: node.id,
|
|
@@ -377,6 +695,7 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
377
695
|
list.push(row);
|
|
378
696
|
outputsByNode.set(node.id, list);
|
|
379
697
|
await appendRunOutput(row);
|
|
698
|
+
return row;
|
|
380
699
|
}
|
|
381
700
|
return async function dataDrivenTaskImpl(runId, opts) {
|
|
382
701
|
try {
|
|
@@ -399,7 +718,7 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
399
718
|
if (diagnostics.length > 0) {
|
|
400
719
|
throw new Error(`PINNED_TEMPLATE_INVALID: ${template.pipelineId} — ${diagnostics.map((d) => d.code).join(', ')}`);
|
|
401
720
|
}
|
|
402
|
-
const { taskId, title, base, issueRef } = await loadRunTaskContext(runId);
|
|
721
|
+
const { taskId, title, base, issueRef, issueAction } = await loadRunTaskContext(runId);
|
|
403
722
|
const live = route.roleBindings.some((b) => runnerNeedsLivePreflight(b.resolvedRunnerId));
|
|
404
723
|
if (live) {
|
|
405
724
|
const pf = await preflightFn(taskId, base);
|
|
@@ -410,9 +729,9 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
410
729
|
if (live) {
|
|
411
730
|
await createWorktreeFn(runId, taskId, title, base, issueRef);
|
|
412
731
|
}
|
|
413
|
-
return runGraph(runId, opts, taskId, title, base, issueRef, live);
|
|
732
|
+
return runGraph(runId, opts, taskId, title, base, issueRef, issueAction, live);
|
|
414
733
|
}
|
|
415
|
-
async function runGraph(runId, opts, taskId, title, base, issueRef, live) {
|
|
734
|
+
async function runGraph(runId, opts, taskId, title, base, issueRef, issueAction, live) {
|
|
416
735
|
const { route, template, runnerRetryPolicy } = opts;
|
|
417
736
|
const bindingByRef = new Map();
|
|
418
737
|
for (const binding of route.roleBindings) {
|
|
@@ -427,7 +746,9 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
427
746
|
let state = initialState(template);
|
|
428
747
|
let lastResult;
|
|
429
748
|
let lastVerdict = '';
|
|
749
|
+
let lastProducedOutput;
|
|
430
750
|
let lastFailureReason = '';
|
|
751
|
+
let approvedMergeGateHeadShaValue;
|
|
431
752
|
let stepCount = 0;
|
|
432
753
|
const effectOrdinalByNode = new Map();
|
|
433
754
|
const outputsByNode = new Map();
|
|
@@ -439,10 +760,12 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
439
760
|
return await finish(runId, decision.status, lastVerdict, stepCount, lastFailureReason);
|
|
440
761
|
}
|
|
441
762
|
const eff = await applyDecision(decision, {
|
|
442
|
-
runId, template, state, bindingByRef, executionProfile, taskId, title, base, issueRef,
|
|
763
|
+
runId, template, state, bindingByRef, executionProfile, taskId, title, base, issueRef, issueAction,
|
|
443
764
|
effectOrdinalByNode, outputsByNode, runnerRetryPolicy,
|
|
444
765
|
live,
|
|
445
766
|
lastVerdict,
|
|
767
|
+
lastProducedOutput,
|
|
768
|
+
approvedMergeGateHeadSha: approvedMergeGateHeadShaValue,
|
|
446
769
|
});
|
|
447
770
|
stepCount += eff.stepDelta;
|
|
448
771
|
if (eff.terminal) {
|
|
@@ -453,9 +776,12 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
453
776
|
await deps.setProgress?.(runId, progressCursor(state, eff.lastResult));
|
|
454
777
|
}
|
|
455
778
|
lastResult = eff.lastResult;
|
|
456
|
-
|
|
457
|
-
|
|
779
|
+
lastVerdict = eff.lastVerdict ?? lastVerdict;
|
|
780
|
+
lastProducedOutput = eff.producedOutput ?? lastProducedOutput;
|
|
458
781
|
lastFailureReason = eff.failureReason ?? '';
|
|
782
|
+
if (eff.approvedMergeGateHeadSha !== undefined) {
|
|
783
|
+
approvedMergeGateHeadShaValue = eff.approvedMergeGateHeadSha ?? undefined;
|
|
784
|
+
}
|
|
459
785
|
}
|
|
460
786
|
throw new InterpretError(`data-driven ${template.pipelineId} did not terminate within ${MAX_STEPS} steps (template loop bug)`);
|
|
461
787
|
}
|
|
@@ -510,6 +836,7 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
510
836
|
};
|
|
511
837
|
let lastResult;
|
|
512
838
|
let lastVerdict = '';
|
|
839
|
+
let lastProducedOutput;
|
|
513
840
|
let stepDelta = 0;
|
|
514
841
|
for (let i = 0; i < MAX_STEPS; i++) {
|
|
515
842
|
const next = coreStep(branchTemplate, state, lastResult);
|
|
@@ -532,6 +859,7 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
532
859
|
template: branchTemplate,
|
|
533
860
|
state,
|
|
534
861
|
lastVerdict,
|
|
862
|
+
lastProducedOutput,
|
|
535
863
|
});
|
|
536
864
|
stepDelta += eff.stepDelta;
|
|
537
865
|
if (eff.terminal)
|
|
@@ -541,6 +869,7 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
541
869
|
lastResult = eff.lastResult;
|
|
542
870
|
if (eff.lastVerdict !== undefined)
|
|
543
871
|
lastVerdict = eff.lastVerdict;
|
|
872
|
+
lastProducedOutput = eff.producedOutput ?? lastProducedOutput;
|
|
544
873
|
}
|
|
545
874
|
throw new InterpretError(`fork ${decision.nodeId} branch ${branch.id} did not reach join ${decision.joinId} within ${MAX_STEPS} steps`);
|
|
546
875
|
}
|
|
@@ -553,11 +882,12 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
553
882
|
const stepKey = stepKeyFor(node.id, ordinal);
|
|
554
883
|
const resolved = resolveConsumes(node, ctx.outputsByNode);
|
|
555
884
|
if ('missing' in resolved) {
|
|
885
|
+
const reason = `${REVO_INPUT_MISSING}: required input ${resolved.missing} was not produced`;
|
|
556
886
|
await appendEvent({
|
|
557
887
|
runId, taskId, stepId: '', stepKey, type: 'step_failed',
|
|
558
|
-
payload: { nodeId: node.id, error:
|
|
888
|
+
payload: { nodeId: node.id, error: reason },
|
|
559
889
|
});
|
|
560
|
-
return { lastResult: { outcome: 'failed', errorCode: REVO_INPUT_MISSING }, lastVerdict: 'failed', stepDelta: 1 };
|
|
890
|
+
return { lastResult: { outcome: 'failed', errorCode: REVO_INPUT_MISSING }, lastVerdict: 'failed', failureReason: reason, stepDelta: 1 };
|
|
561
891
|
}
|
|
562
892
|
const result = await invokeRole(runId, decision, node, ctx, resolved.inputs, stepKey);
|
|
563
893
|
if ('blocked' in result) {
|
|
@@ -588,15 +918,16 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
588
918
|
await appendEvent({
|
|
589
919
|
runId, taskId, stepId: '', stepKey, type: 'step_failed',
|
|
590
920
|
idempotencyKey: result.attemptId,
|
|
591
|
-
payload: { nodeId: node.id, error: result.errorCode },
|
|
921
|
+
payload: { nodeId: node.id, error: result.errorCode, reason: result.reason },
|
|
592
922
|
});
|
|
593
923
|
return { lastResult: { outcome: 'failed', errorCode: result.errorCode }, lastVerdict: 'failed', failureReason: result.reason, stepDelta: result.attemptsMade };
|
|
594
924
|
}
|
|
595
|
-
await recordOutput(runId, node, ordinal, result.attemptId, result.output, ctx.outputsByNode);
|
|
925
|
+
const producedOutput = await recordOutput(runId, node, ordinal, result.attemptId, result.output, ctx.outputsByNode);
|
|
596
926
|
const verdict = result.verdict;
|
|
597
927
|
return {
|
|
598
928
|
lastResult: { outcome: 'succeeded', ...(verdict ? { verdict } : {}) },
|
|
599
929
|
...(verdict ? { lastVerdict: verdict } : {}),
|
|
930
|
+
...(producedOutput ? { producedOutput } : {}),
|
|
600
931
|
stepDelta: result.attemptsMade,
|
|
601
932
|
};
|
|
602
933
|
}
|
|
@@ -605,30 +936,54 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
605
936
|
const ordinal = nextOrdinal(ctx.effectOrdinalByNode, node.id);
|
|
606
937
|
const resolved = resolveConsumes(node, ctx.outputsByNode);
|
|
607
938
|
if ('missing' in resolved) {
|
|
939
|
+
const reason = `${REVO_INPUT_MISSING}: required input ${resolved.missing} was not produced`;
|
|
608
940
|
await appendEvent({
|
|
609
941
|
runId, taskId, stepId: '', stepKey: stepKeyFor(node.id, ordinal), type: 'step_failed',
|
|
610
|
-
payload: { nodeId: node.id, error:
|
|
942
|
+
payload: { nodeId: node.id, error: reason },
|
|
611
943
|
});
|
|
612
|
-
return { lastResult: { outcome: 'failed', errorCode: REVO_INPUT_MISSING }, lastVerdict: 'failed', stepDelta: 1 };
|
|
944
|
+
return { lastResult: { outcome: 'failed', errorCode: REVO_INPUT_MISSING }, lastVerdict: 'failed', failureReason: reason, stepDelta: 1 };
|
|
613
945
|
}
|
|
614
|
-
const scriptResult = await invokeScript(runId, decision, { taskId, title, base, issueRef: ctx.issueRef }, bindingByRef, stepKeyFor(node.id, ordinal), resolved.inputs);
|
|
946
|
+
const scriptResult = await invokeScript(runId, decision, { taskId, title, base, issueRef: ctx.issueRef, issueAction: ctx.issueAction }, bindingByRef, stepKeyFor(node.id, ordinal), resolved.inputs);
|
|
615
947
|
if (scriptResult.outcome === 'blocked') {
|
|
616
948
|
return { lastResult: { outcome: 'failed', errorCode: REVO_SCRIPT_BLOCKED }, lastVerdict: 'blocked', stepDelta: 1 };
|
|
617
949
|
}
|
|
618
950
|
if (scriptResult.outcome === 'failed') {
|
|
619
|
-
|
|
951
|
+
const reason = scriptResult.reason ? `${REVO_SCRIPT_FAILED}: ${scriptResult.reason}` : REVO_SCRIPT_FAILED;
|
|
952
|
+
return { lastResult: { outcome: 'failed', errorCode: REVO_SCRIPT_FAILED }, lastVerdict: 'failed', failureReason: reason, stepDelta: 1 };
|
|
953
|
+
}
|
|
954
|
+
let pointer = scriptResult.pointer;
|
|
955
|
+
let sv = scriptResult.verdict;
|
|
956
|
+
let stateOverride;
|
|
957
|
+
const headMove = approvedHeadMove(decision, ctx.approvedMergeGateHeadSha, pointer, sv);
|
|
958
|
+
if (headMove) {
|
|
959
|
+
pointer = reapprovalRequiredFeedback(pointer, headMove.approvedHeadSha, headMove.reverifyHeadSha);
|
|
960
|
+
sv = 'recheck';
|
|
961
|
+
stateOverride = reopenNodeState(ctx.state, 'mergeGate');
|
|
620
962
|
}
|
|
621
|
-
await recordOutput(runId, node, ordinal, stepKeyFor(node.id, ordinal),
|
|
622
|
-
|
|
623
|
-
|
|
963
|
+
const producedOutput = await recordOutput(runId, node, ordinal, stepKeyFor(node.id, ordinal), pointer, ctx.outputsByNode);
|
|
964
|
+
return {
|
|
965
|
+
lastResult: stateOverride ? undefined : { outcome: 'succeeded', ...(sv ? { verdict: sv } : {}) },
|
|
966
|
+
...(sv ? { lastVerdict: sv } : {}),
|
|
967
|
+
...(producedOutput ? { producedOutput } : {}),
|
|
968
|
+
...(stateOverride ? { stateOverride } : {}),
|
|
969
|
+
stepDelta: 1,
|
|
970
|
+
};
|
|
624
971
|
}
|
|
625
972
|
case 'awaitGate': {
|
|
626
973
|
const topic = gateTopicFor(decision.reason);
|
|
627
974
|
const ordinal = nextOrdinal(ctx.effectOrdinalByNode, decision.nodeId);
|
|
628
|
-
const
|
|
975
|
+
const summary = buildGateSummary(decision, ctx.outputsByNode, ctx.lastVerdict, ctx.lastProducedOutput);
|
|
976
|
+
const human = await awaitHuman(runId, topic, stepKeyFor(decision.nodeId, ordinal), `${decision.reason} approval`, summary, decision.outcomes);
|
|
629
977
|
const verdict = gateVerdict(human, decision.outcomes);
|
|
630
|
-
|
|
631
|
-
|
|
978
|
+
const approvedHeadSha = approvedMergeGateHeadSha(decision, verdict, summary);
|
|
979
|
+
const producedOutput = await recordOutput(runId, resolveNode(template, decision.nodeId), ordinal, stepKeyFor(decision.nodeId, ordinal), gateResolutionOutput(human, verdict, stepKeyFor(decision.nodeId, ordinal), approvedHeadSha), ctx.outputsByNode);
|
|
980
|
+
return {
|
|
981
|
+
lastResult: verdict ? { verdict } : {},
|
|
982
|
+
...(verdict ? { lastVerdict: verdict } : {}),
|
|
983
|
+
...(producedOutput ? { producedOutput } : {}),
|
|
984
|
+
...(approvedHeadSha !== undefined ? { approvedMergeGateHeadSha: approvedHeadSha } : {}),
|
|
985
|
+
stepDelta: 0,
|
|
986
|
+
};
|
|
632
987
|
}
|
|
633
988
|
case 'fork': {
|
|
634
989
|
await appendEvent({
|
|
@@ -662,7 +1017,8 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
662
1017
|
for (let attemptNo = 1; attemptNo <= ctx.runnerRetryPolicy.maxAttempts; attemptNo++) {
|
|
663
1018
|
const physicalAttempt = physicalAttemptFor(runId, stepKey, attemptNo);
|
|
664
1019
|
attemptIds.push(physicalAttempt.attemptId);
|
|
665
|
-
const
|
|
1020
|
+
const launchOverrides = resolveLaunchOverrides(binding, decision.nodeId, ctx.executionProfile);
|
|
1021
|
+
const result = await runStepFn(runId, binding.rowId, stepKey, stepInputForAttempt(decision.nodeId, inputs, physicalAttempt), binding.resolvedRunnerId, ctx.executionProfile, physicalAttempt, ctx.template.verdicts.domain, launchOverrides);
|
|
666
1022
|
const needsHuman = await maybeHandleNeedsHumanRoleResult({
|
|
667
1023
|
...input,
|
|
668
1024
|
attemptIds,
|
|
@@ -692,6 +1048,7 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
692
1048
|
nodeId: node.id,
|
|
693
1049
|
attemptId: physicalAttempt.attemptId,
|
|
694
1050
|
...(ctx.issueRef ? { issueRef: ctx.issueRef } : {}),
|
|
1051
|
+
...(ctx.issueAction ? { issueAction: ctx.issueAction } : {}),
|
|
695
1052
|
...(artifactRef ? { artifactRef } : {}),
|
|
696
1053
|
});
|
|
697
1054
|
output = attachProducedChange(output, change);
|
|
@@ -884,154 +1241,8 @@ export function makeDataDrivenTask(runStepFn, deps) {
|
|
|
884
1241
|
};
|
|
885
1242
|
}
|
|
886
1243
|
async function invokeScript(runId, decision, ctx, bindingByRef, stepKey, inputs) {
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
await releaseWorktreeFn(runId, ctx.taskId);
|
|
890
|
-
}
|
|
891
|
-
catch { /* best-effort */ }
|
|
892
|
-
await appendEvent({ runId, taskId: ctx.taskId, stepId: '', stepKey, type: 'worktree_released', payload: { nodeId: decision.nodeId } });
|
|
893
|
-
return { outcome: 'ok', pointer: { released: true } };
|
|
894
|
-
}
|
|
895
|
-
const isConfirmMerge = decision.scriptRef === 'script:confirmMerge';
|
|
896
|
-
const isPollPr = decision.scriptRef === 'script:pollPr';
|
|
897
|
-
const isRespondThreads = decision.scriptRef === 'script:respondThreads';
|
|
898
|
-
const binding = bindingByRef.get(decision.scriptRef) ?? bindingByRef.get('script:integrator');
|
|
899
|
-
const change = producedChangeFromInputs(inputs);
|
|
900
|
-
const mergeReadiness = mergeReadinessFromInputs(inputs);
|
|
901
|
-
const issueRef = ctx.issueRef;
|
|
902
|
-
const changeForIntegrator = change ? changeWithRunIssueRef(change, issueRef) : undefined;
|
|
903
|
-
const integratorInput = {
|
|
904
|
-
runId,
|
|
905
|
-
taskId: ctx.taskId,
|
|
906
|
-
title: ctx.title,
|
|
907
|
-
base: ctx.base,
|
|
908
|
-
...(issueRef ? { issueRef } : {}),
|
|
909
|
-
...(changeForIntegrator ? { change: changeForIntegrator } : {}),
|
|
910
|
-
...(inputs.triage === undefined ? {} : { triage: inputs.triage }),
|
|
911
|
-
...(mergeReadiness ? { mergeReadiness } : {}),
|
|
912
|
-
};
|
|
913
|
-
const useReal = !!binding && runnerUsesRealIntegrator(binding.resolvedRunnerId);
|
|
914
|
-
let result;
|
|
915
|
-
try {
|
|
916
|
-
if (isConfirmMerge) {
|
|
917
|
-
result = useReal ? await confirmMergeFn(integratorInput) : runConfirmStub(integratorInput);
|
|
918
|
-
}
|
|
919
|
-
else if (isPollPr) {
|
|
920
|
-
result = useReal ? await pollPrFn(integratorInput) : runPollStub(integratorInput);
|
|
921
|
-
}
|
|
922
|
-
else if (isRespondThreads) {
|
|
923
|
-
result = useReal ? await respondThreadsFn(integratorInput) : runRespondStub(integratorInput);
|
|
924
|
-
}
|
|
925
|
-
else {
|
|
926
|
-
result = useReal ? await integrateFn(integratorInput) : runStub(integratorInput);
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
catch (err) {
|
|
930
|
-
await appendEvent({
|
|
931
|
-
runId,
|
|
932
|
-
taskId: ctx.taskId,
|
|
933
|
-
stepId: '',
|
|
934
|
-
stepKey,
|
|
935
|
-
type: 'step_failed',
|
|
936
|
-
payload: { scriptRef: decision.scriptRef, error: err instanceof Error ? err.message : String(err) },
|
|
937
|
-
});
|
|
938
|
-
return { outcome: 'failed' };
|
|
939
|
-
}
|
|
940
|
-
if ('needsHuman' in result) {
|
|
941
|
-
const reason = isConfirmMerge ? 'confirm-merge' : isPollPr ? 'poll-pr' : isRespondThreads ? 'respond-threads' : 'integrate';
|
|
942
|
-
await appendEvent({
|
|
943
|
-
runId,
|
|
944
|
-
taskId: ctx.taskId,
|
|
945
|
-
stepId: '',
|
|
946
|
-
stepKey: 'pipeline',
|
|
947
|
-
type: 'pipeline_blocked',
|
|
948
|
-
payload: { reason, lesson: result.lesson, nodeId: decision.nodeId },
|
|
949
|
-
});
|
|
950
|
-
return { outcome: 'blocked' };
|
|
951
|
-
}
|
|
952
|
-
if (isConfirmMerge) {
|
|
953
|
-
const merged = result;
|
|
954
|
-
await appendEvent({
|
|
955
|
-
runId,
|
|
956
|
-
taskId: ctx.taskId,
|
|
957
|
-
stepId: '',
|
|
958
|
-
stepKey,
|
|
959
|
-
type: 'merge_confirmed',
|
|
960
|
-
payload: {
|
|
961
|
-
prNumber: merged.prNumber,
|
|
962
|
-
prUrl: merged.prUrl,
|
|
963
|
-
...(merged.issueRef ? { issueRef: merged.issueRef } : {}),
|
|
964
|
-
},
|
|
965
|
-
});
|
|
966
|
-
return {
|
|
967
|
-
outcome: 'ok',
|
|
968
|
-
pointer: {
|
|
969
|
-
merged: true,
|
|
970
|
-
prNumber: merged.prNumber,
|
|
971
|
-
prUrl: merged.prUrl,
|
|
972
|
-
...(merged.issueRef ? { issueRef: merged.issueRef } : {}),
|
|
973
|
-
},
|
|
974
|
-
};
|
|
975
|
-
}
|
|
976
|
-
if (isPollPr) {
|
|
977
|
-
const feedback = result;
|
|
978
|
-
await appendEvent({
|
|
979
|
-
runId,
|
|
980
|
-
taskId: ctx.taskId,
|
|
981
|
-
stepId: '',
|
|
982
|
-
stepKey,
|
|
983
|
-
type: 'pr_polled',
|
|
984
|
-
payload: {
|
|
985
|
-
prNumber: feedback.prNumber,
|
|
986
|
-
headSha: feedback.headSha,
|
|
987
|
-
verdict: feedback.verdict,
|
|
988
|
-
evidence: feedback.evidence,
|
|
989
|
-
ciFailures: feedback.ciFailures.length,
|
|
990
|
-
reviewThreads: feedback.reviewThreads.length,
|
|
991
|
-
...(feedback.issueRef ? { issueRef: feedback.issueRef } : {}),
|
|
992
|
-
},
|
|
993
|
-
});
|
|
994
|
-
return { outcome: 'ok', pointer: feedback, verdict: feedback.verdict };
|
|
995
|
-
}
|
|
996
|
-
if (isRespondThreads) {
|
|
997
|
-
const responded = result;
|
|
998
|
-
await appendEvent({
|
|
999
|
-
runId,
|
|
1000
|
-
taskId: ctx.taskId,
|
|
1001
|
-
stepId: '',
|
|
1002
|
-
stepKey,
|
|
1003
|
-
type: 'threads_responded',
|
|
1004
|
-
payload: { replied: responded.replied, resolved: responded.resolved },
|
|
1005
|
-
});
|
|
1006
|
-
return { outcome: 'ok', pointer: responded };
|
|
1007
|
-
}
|
|
1008
|
-
const integrated = result;
|
|
1009
|
-
await appendEvent({
|
|
1010
|
-
runId,
|
|
1011
|
-
taskId: ctx.taskId,
|
|
1012
|
-
stepId: '',
|
|
1013
|
-
stepKey,
|
|
1014
|
-
type: 'integrate_succeeded',
|
|
1015
|
-
payload: {
|
|
1016
|
-
prUrl: integrated.prUrl,
|
|
1017
|
-
branch: integrated.branch,
|
|
1018
|
-
prNumber: integrated.prNumber,
|
|
1019
|
-
headSha: integrated.headSha,
|
|
1020
|
-
status: integrated.status,
|
|
1021
|
-
...(integrated.issueRef ? { issueRef: integrated.issueRef } : {}),
|
|
1022
|
-
},
|
|
1023
|
-
});
|
|
1024
|
-
return {
|
|
1025
|
-
outcome: 'ok',
|
|
1026
|
-
pointer: {
|
|
1027
|
-
prUrl: integrated.prUrl,
|
|
1028
|
-
branch: integrated.branch,
|
|
1029
|
-
prNumber: integrated.prNumber,
|
|
1030
|
-
headSha: integrated.headSha,
|
|
1031
|
-
status: integrated.status,
|
|
1032
|
-
...(integrated.issueRef ? { issueRef: integrated.issueRef } : {}),
|
|
1033
|
-
},
|
|
1034
|
-
};
|
|
1244
|
+
const handler = scriptRegistry.get(decision.scriptRef) ?? scriptRegistry.get('script:integrator');
|
|
1245
|
+
return handler({ runId, decision, ctx, bindingByRef, stepKey, inputs });
|
|
1035
1246
|
}
|
|
1036
1247
|
async function finish(runId, status, verdict, steps, failureReason = '') {
|
|
1037
1248
|
if (status === 'succeeded') {
|