harnery 0.45.0 → 0.45.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commander.js +0 -1
- package/dist/commands/files.d.ts +2 -2
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +4 -7
- package/dist/commands/servers.d.ts.map +1 -1
- package/dist/commands/servers.js +17 -11
- package/dist/commands/tunnel.d.ts +0 -15
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +153 -453
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +6 -45
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +0 -18
- package/dist/core/agents/live-authority-v3.d.ts +0 -1
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +0 -11
- package/dist/core/agents/live-lifecycle-v3.d.ts +2 -4
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
- package/dist/core/agents/live-lifecycle-v3.js +7 -28
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +14 -25
- package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-writer.js +0 -5
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +2 -7
- package/dist/core/agents/state/proc-start.d.ts +20 -31
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +37 -70
- package/dist/core/agents/state/service-process.d.ts +61 -0
- package/dist/core/agents/state/service-process.d.ts.map +1 -0
- package/dist/core/agents/state/service-process.js +143 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +70 -180
- package/dist/core/artifacts/activity.d.ts +4 -1
- package/dist/core/artifacts/activity.d.ts.map +1 -1
- package/dist/core/artifacts/activity.js +8 -2
- package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
- package/dist/core/artifacts/delivery-card.js +1 -2
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +38 -12
- package/dist/core/context/index.d.ts +6 -4
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +194 -256
- package/dist/core/context/observation.d.ts +9 -9
- package/dist/core/context/observation.d.ts.map +1 -1
- package/dist/core/context/observation.js +37 -6
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +0 -29
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +0 -1
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +0 -7
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +64 -77
- package/dist/core/events/v3/runtime-build.d.ts +14 -0
- package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
- package/dist/core/events/v3/runtime-build.js +44 -0
- package/dist/core/governor/index.d.ts +2 -1
- package/dist/core/governor/index.d.ts.map +1 -1
- package/dist/core/governor/index.js +2 -1
- package/dist/core/governor/service-read.d.ts +3 -10
- package/dist/core/governor/service-read.d.ts.map +1 -1
- package/dist/core/governor/service-read.js +10 -94
- package/dist/core/governor/service.d.ts +3 -7
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +17 -182
- package/dist/core/hooks/cli.js +78 -48
- package/dist/core/hooks/resolve/owner.js +2 -2
- package/dist/core/resources/sampler.js +2 -2
- package/dist/core/resources/service-status.d.ts +2 -14
- package/dist/core/resources/service-status.d.ts.map +1 -1
- package/dist/core/resources/service-status.js +3 -23
- package/dist/core/resources/service.d.ts +3 -7
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +12 -101
- package/dist/core/resources/status.d.ts.map +1 -1
- package/dist/core/resources/status.js +3 -2
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +9 -2
- package/dist/core/servers/index.d.ts +11 -10
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +82 -192
- package/dist/core/servers/tunnels.d.ts +3 -5
- package/dist/core/servers/tunnels.d.ts.map +1 -1
- package/dist/core/servers/tunnels.js +2 -45
- package/dist/core/storage/durable-history.js +56 -22
- package/dist/core/storage/segments.d.ts.map +1 -1
- package/dist/core/storage/segments.js +73 -17
- package/dist/core/supervisor/service.d.ts +4 -7
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +23 -122
- package/dist/core/supervisor/services.js +2 -2
- package/dist/core/supervisor/status.d.ts +3 -17
- package/dist/core/supervisor/status.d.ts.map +1 -1
- package/dist/core/supervisor/status.js +7 -70
- package/dist/core/work/state.js +7 -7
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +23 -66
- package/dist/core/workflow/index.d.ts +3 -4
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +2 -2
- package/dist/core/workflow/live-session-v3.d.ts +0 -6
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +5 -126
- package/dist/core/workflow/proof.d.ts +4 -4
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +34 -86
- package/dist/core/workflow/resume-lock.d.ts +17 -0
- package/dist/core/workflow/resume-lock.d.ts.map +1 -0
- package/dist/core/workflow/resume-lock.js +158 -0
- package/dist/core/workflow/run-state.d.ts +0 -21
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +3 -174
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +0 -19
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +0 -13
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +0 -19
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +0 -14
- package/dist/core/workflow/types.d.ts +11 -54
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.d.ts +4 -0
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +1 -1
- package/dist/core/workflow/workspaces/local-git.js +91 -210
- package/dist/core/workflow/workspaces/ownership.d.ts +0 -7
- package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/ownership.js +3 -130
- package/dist/core/workflow/workspaces/reclaim.d.ts +4 -7
- package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/reclaim.js +0 -4
- package/dist/core/workflow/workspaces/recovery.d.ts +1 -1
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +1 -1
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +1 -4
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +0 -4
- package/dist/lib/tunnel/state.d.ts +6 -13
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +12 -58
- package/package.json +1 -6
- package/src/commander.ts +0 -10
- package/src/commands/files.ts +5 -13
- package/src/commands/servers.ts +23 -12
- package/src/commands/tunnel.ts +166 -533
- package/src/commands/workflow.ts +8 -58
- package/src/core/agents/coord-client.ts +0 -20
- package/src/core/agents/live-authority-v3.ts +0 -17
- package/src/core/agents/live-lifecycle-v3.ts +14 -52
- package/src/core/agents/state/heartbeat-writer.ts +12 -29
- package/src/core/agents/state/live-coordination-writer.ts +0 -29
- package/src/core/agents/state/pidmap.ts +2 -15
- package/src/core/agents/state/proc-start.ts +41 -69
- package/src/core/agents/state/service-process.ts +217 -0
- package/src/core/agents/state/stale-sweep.ts +82 -247
- package/src/core/artifacts/activity.ts +9 -2
- package/src/core/artifacts/delivery-card.ts +1 -7
- package/src/core/artifacts/index.ts +45 -12
- package/src/core/context/index.ts +207 -272
- package/src/core/context/observation.ts +40 -9
- package/src/core/events/v3/authority-outbox.ts +0 -56
- package/src/core/events/v3/producers/coordination-recorder.ts +0 -15
- package/src/core/events/v3/producers/recorder.ts +68 -94
- package/src/core/events/v3/runtime-build.ts +62 -0
- package/src/core/governor/index.ts +1 -2
- package/src/core/governor/service-read.ts +15 -114
- package/src/core/governor/service.ts +36 -220
- package/src/core/hooks/cli.ts +85 -47
- package/src/core/hooks/resolve/owner.ts +2 -2
- package/src/core/resources/sampler.ts +3 -2
- package/src/core/resources/service-status.ts +6 -35
- package/src/core/resources/service.ts +23 -133
- package/src/core/resources/status.ts +3 -2
- package/src/core/semantic/service.ts +8 -2
- package/src/core/servers/index.ts +86 -228
- package/src/core/servers/tunnels.ts +3 -55
- package/src/core/storage/durable-history.ts +52 -20
- package/src/core/storage/segments.ts +79 -17
- package/src/core/supervisor/service.ts +33 -155
- package/src/core/supervisor/services.ts +2 -2
- package/src/core/supervisor/status.ts +10 -85
- package/src/core/work/state.ts +7 -7
- package/src/core/workflow/engine.ts +22 -74
- package/src/core/workflow/index.ts +2 -13
- package/src/core/workflow/live-session-v3.ts +5 -146
- package/src/core/workflow/proof.ts +40 -104
- package/src/core/workflow/resume-lock.ts +179 -0
- package/src/core/workflow/run-state.ts +3 -207
- package/src/core/workflow/spawn-claude.ts +0 -18
- package/src/core/workflow/spawn-codex.ts +0 -13
- package/src/core/workflow/spawn-cursor.ts +0 -18
- package/src/core/workflow/spawn-opencode.ts +0 -14
- package/src/core/workflow/types.ts +11 -41
- package/src/core/workflow/workspaces/leases.ts +1 -1
- package/src/core/workflow/workspaces/local-git.ts +120 -239
- package/src/core/workflow/workspaces/ownership.ts +3 -158
- package/src/core/workflow/workspaces/reclaim.ts +3 -9
- package/src/core/workflow/workspaces/recovery.ts +1 -1
- package/src/core/workflow/workspaces/state.ts +1 -5
- package/src/lib/identities/assume.ts +0 -4
- package/src/lib/tunnel/state.ts +15 -70
- package/dist/commands/turn-rollback.d.ts +0 -5
- package/dist/commands/turn-rollback.d.ts.map +0 -1
- package/dist/commands/turn-rollback.js +0 -76
- package/dist/core/agents/state/process-handle.d.ts +0 -26
- package/dist/core/agents/state/process-handle.d.ts.map +0 -1
- package/dist/core/agents/state/process-handle.js +0 -121
- package/dist/core/agents/state/unchanged-file.d.ts +0 -44
- package/dist/core/agents/state/unchanged-file.d.ts.map +0 -1
- package/dist/core/agents/state/unchanged-file.js +0 -390
- package/dist/core/events/v3/generation-custody.d.ts +0 -5
- package/dist/core/events/v3/generation-custody.d.ts.map +0 -1
- package/dist/core/events/v3/generation-custody.js +0 -50
- package/dist/core/turn-rollback/content.d.ts +0 -53
- package/dist/core/turn-rollback/content.d.ts.map +0 -1
- package/dist/core/turn-rollback/content.js +0 -396
- package/dist/core/turn-rollback/index.d.ts +0 -72
- package/dist/core/turn-rollback/index.d.ts.map +0 -1
- package/dist/core/turn-rollback/index.js +0 -443
- package/dist/core/workflow/failure-evidence.d.ts +0 -16
- package/dist/core/workflow/failure-evidence.d.ts.map +0 -1
- package/dist/core/workflow/failure-evidence.js +0 -114
- package/dist/core/workflow/recovery-audit.d.ts +0 -30
- package/dist/core/workflow/recovery-audit.d.ts.map +0 -1
- package/dist/core/workflow/recovery-audit.js +0 -116
- package/src/commands/turn-rollback.ts +0 -111
- package/src/core/agents/state/process-handle.ts +0 -165
- package/src/core/agents/state/unchanged-file.ts +0 -456
- package/src/core/events/v3/generation-custody.ts +0 -59
- package/src/core/turn-rollback/content.ts +0 -499
- package/src/core/turn-rollback/index.ts +0 -580
- package/src/core/workflow/failure-evidence.ts +0 -141
- package/src/core/workflow/recovery-audit.ts +0 -181
|
@@ -45,16 +45,7 @@ import { freezeWorkflowAttemptContext } from "./attempt-context.ts";
|
|
|
45
45
|
import { type BillingProbe, probeBilling } from "./billing.ts";
|
|
46
46
|
import { stableDigest } from "./durable-record.ts";
|
|
47
47
|
import { evidencePreflightError } from "./evidence-preflight.ts";
|
|
48
|
-
import {
|
|
49
|
-
canRetryFailedSpawn,
|
|
50
|
-
observeSpawnFailure,
|
|
51
|
-
resolveFailureEvidence,
|
|
52
|
-
} from "./failure-evidence.ts";
|
|
53
|
-
import {
|
|
54
|
-
endWorkflowChildSessionV3,
|
|
55
|
-
recordWorkflowChildTerminalFailureV3,
|
|
56
|
-
startWorkflowChildSessionV3,
|
|
57
|
-
} from "./live-session-v3.ts";
|
|
48
|
+
import { endWorkflowChildSessionV3, startWorkflowChildSessionV3 } from "./live-session-v3.ts";
|
|
58
49
|
import {
|
|
59
50
|
buildWorkflowProof,
|
|
60
51
|
createEvidenceRecord,
|
|
@@ -62,8 +53,8 @@ import {
|
|
|
62
53
|
normalizeWorkflowMeta,
|
|
63
54
|
writeWorkflowProof,
|
|
64
55
|
} from "./proof.ts";
|
|
56
|
+
import { acquireWorkflowResumeLease } from "./resume-lock.ts";
|
|
65
57
|
import {
|
|
66
|
-
acquireWorkflowResumeLease,
|
|
67
58
|
assertWorkflowRunResumable,
|
|
68
59
|
assertWorkflowScriptUnchanged,
|
|
69
60
|
WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION,
|
|
@@ -1038,38 +1029,22 @@ async function executeWorkflow(
|
|
|
1038
1029
|
});
|
|
1039
1030
|
agentProof.access = access;
|
|
1040
1031
|
|
|
1041
|
-
let childGenerationId: string | undefined;
|
|
1042
1032
|
let recordAgentEnd: (() => void) | undefined;
|
|
1043
1033
|
const finalizeChild = (): void => {
|
|
1044
1034
|
try {
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
// Result publication is the resume-cache boundary, after canonical custody.
|
|
1035
|
+
// Record the canonical terminal first. agent.end is the resume-cache
|
|
1036
|
+
// boundary, so a child whose end was not recorded never reports success.
|
|
1037
|
+
endWorkflowChildSessionV3({
|
|
1038
|
+
coordRoot: opts.coordRoot,
|
|
1039
|
+
instanceId: childInstanceId(runId, id),
|
|
1040
|
+
runId,
|
|
1041
|
+
agentId: id,
|
|
1042
|
+
adapter,
|
|
1043
|
+
cleanExit: agentProof.status === "succeeded",
|
|
1044
|
+
});
|
|
1056
1045
|
recordAgentEnd?.();
|
|
1057
1046
|
} catch (error) {
|
|
1058
|
-
|
|
1059
|
-
try {
|
|
1060
|
-
if (childGenerationId)
|
|
1061
|
-
recordWorkflowChildTerminalFailureV3({
|
|
1062
|
-
coordRoot: opts.coordRoot,
|
|
1063
|
-
instanceId: childInstanceId(runId, id),
|
|
1064
|
-
runId,
|
|
1065
|
-
agentId: id,
|
|
1066
|
-
adapter,
|
|
1067
|
-
expectedGenerationId: childGenerationId,
|
|
1068
|
-
reason,
|
|
1069
|
-
});
|
|
1070
|
-
} catch (visibilityError) {
|
|
1071
|
-
reason += `; blocked lifecycle recording failed: ${(visibilityError as Error).message}`;
|
|
1072
|
-
}
|
|
1047
|
+
const reason = `V3 terminal recording failed: ${(error as Error).message}`;
|
|
1073
1048
|
const failure = new Error(agentProof.error ? `${agentProof.error}; ${reason}` : reason, {
|
|
1074
1049
|
cause: error,
|
|
1075
1050
|
});
|
|
@@ -1102,7 +1077,7 @@ async function executeWorkflow(
|
|
|
1102
1077
|
// Headless children do not reliably fire adapter hooks. The engine owns
|
|
1103
1078
|
// their canonical V3 start so every dispatched child has an auditable
|
|
1104
1079
|
// generation before it begins work.
|
|
1105
|
-
|
|
1080
|
+
startWorkflowChildSessionV3({
|
|
1106
1081
|
coordRoot: opts.coordRoot,
|
|
1107
1082
|
instanceId: childInstanceId(runId, id),
|
|
1108
1083
|
runId,
|
|
@@ -1111,13 +1086,10 @@ async function executeWorkflow(
|
|
|
1111
1086
|
label,
|
|
1112
1087
|
model: agentOpts.model,
|
|
1113
1088
|
});
|
|
1114
|
-
if (!child.v3_generation_id) throw new Error("workflow_child_v3_generation_missing");
|
|
1115
|
-
childGenerationId = child.v3_generation_id;
|
|
1116
1089
|
|
|
1117
1090
|
let attemptPrompt = dispatchPrompt;
|
|
1118
1091
|
let last: SpawnResult | null = null;
|
|
1119
1092
|
let agentCostUsd = 0;
|
|
1120
|
-
let informativeAttempt: ReturnType<typeof observeSpawnFailure> | undefined;
|
|
1121
1093
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1122
1094
|
last = await spawner({
|
|
1123
1095
|
prompt: attemptPrompt,
|
|
@@ -1140,30 +1112,14 @@ async function executeWorkflow(
|
|
|
1140
1112
|
costUsd += last.costUsd ?? 0;
|
|
1141
1113
|
|
|
1142
1114
|
if (!last.ok) {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
effect: { state: "unknown" },
|
|
1151
|
-
partial_output: informativeAttempt.partial_output,
|
|
1152
|
-
}
|
|
1153
|
-
: observed;
|
|
1154
|
-
transcript("agent.attempt_failed", {
|
|
1155
|
-
id,
|
|
1156
|
-
attempt,
|
|
1157
|
-
error: last.error,
|
|
1158
|
-
failure_evidence: observed,
|
|
1159
|
-
result_digest: digestResult(last.text, "text"),
|
|
1160
|
-
text: last.text,
|
|
1161
|
-
});
|
|
1162
|
-
if (!canRetryFailedSpawn({ ...last, failureEvidence: agentProof.failure_evidence }))
|
|
1163
|
-
break;
|
|
1164
|
-
continue;
|
|
1115
|
+
transcript("agent.attempt_failed", { id, attempt, error: last.error });
|
|
1116
|
+
// ADR 0046: an environment failure (the binary was absent) cannot be
|
|
1117
|
+
// helped by retrying an unchanged environment, so stop the in-agent
|
|
1118
|
+
// retry too — not just the outer attempt/replan budget. An upstream
|
|
1119
|
+
// refusal keeps retrying here: the vendor may recover mid-loop.
|
|
1120
|
+
if (last.class === "environment") break;
|
|
1121
|
+
continue; // spawn-level failure: retry with the original prompt
|
|
1165
1122
|
}
|
|
1166
|
-
informativeAttempt = observeSpawnFailure({}, last.text, "final_message");
|
|
1167
1123
|
if (!agentOpts.schema) {
|
|
1168
1124
|
agentProof.status = "succeeded";
|
|
1169
1125
|
agentProof.cost_usd =
|
|
@@ -1217,14 +1173,7 @@ async function executeWorkflow(
|
|
|
1217
1173
|
return parsed.value;
|
|
1218
1174
|
}
|
|
1219
1175
|
|
|
1220
|
-
|
|
1221
|
-
transcript("agent.schema_retry", {
|
|
1222
|
-
id,
|
|
1223
|
-
attempt,
|
|
1224
|
-
problems,
|
|
1225
|
-
rejected_text: last.text,
|
|
1226
|
-
result_digest: digestResult(last.text, "text"),
|
|
1227
|
-
});
|
|
1176
|
+
transcript("agent.schema_retry", { id, attempt, problems });
|
|
1228
1177
|
// Keep the original cache identity while making the actual output
|
|
1229
1178
|
// contract explicit. A full bounded copy of the rejected value lets the
|
|
1230
1179
|
// child repair omissions instead of reconstructing an unseen object.
|
|
@@ -1260,7 +1209,6 @@ async function executeWorkflow(
|
|
|
1260
1209
|
try {
|
|
1261
1210
|
finalizeChild();
|
|
1262
1211
|
} finally {
|
|
1263
|
-
// A slot is disposable; unresolved custody remains visible and failed.
|
|
1264
1212
|
release();
|
|
1265
1213
|
}
|
|
1266
1214
|
}
|
|
@@ -39,23 +39,12 @@ export {
|
|
|
39
39
|
rollupAcceptance,
|
|
40
40
|
writeWorkflowProof,
|
|
41
41
|
} from "./proof.ts";
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
WorkflowRecoveryIntent,
|
|
45
|
-
WorkflowRecoveryRecord,
|
|
46
|
-
} from "./recovery-audit.ts";
|
|
47
|
-
export { beginWorkflowRecovery, readWorkflowRecoveryLedger } from "./recovery-audit.ts";
|
|
48
|
-
export type {
|
|
49
|
-
WorkflowResumeRecoveryOptions,
|
|
50
|
-
WorkflowResumeRecoveryResult,
|
|
51
|
-
WorkflowRunManifest,
|
|
52
|
-
} from "./run-state.ts";
|
|
42
|
+
export { acquireWorkflowResumeLease } from "./resume-lock.ts";
|
|
43
|
+
export type { WorkflowRunManifest } from "./run-state.ts";
|
|
53
44
|
export {
|
|
54
|
-
acquireWorkflowResumeLease,
|
|
55
45
|
assertWorkflowRunResumable,
|
|
56
46
|
assertWorkflowScriptUnchanged,
|
|
57
47
|
readWorkflowRunManifest,
|
|
58
|
-
recoverWorkflowResumeLease,
|
|
59
48
|
WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION,
|
|
60
49
|
workflowRunManifestPath,
|
|
61
50
|
workflowScriptDigest,
|
|
@@ -1,16 +1,9 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { existsSync, linkSync, readdirSync, readFileSync, renameSync, unlinkSync } from "node:fs";
|
|
3
|
-
import { basename, dirname } from "node:path";
|
|
4
1
|
import { type Adapter, adapterFromPlatform } from "../adapter.ts";
|
|
5
|
-
import {
|
|
6
|
-
recordLiveLifecycleChangeV3,
|
|
7
|
-
recordLiveTaskChangeV3,
|
|
8
|
-
} from "../agents/live-authority-v3.ts";
|
|
2
|
+
import { recordLiveTaskChangeV3 } from "../agents/live-authority-v3.ts";
|
|
9
3
|
import { endSessionExplicitV3 } from "../agents/session-finalizer-v3.ts";
|
|
10
|
-
import { heartbeatPath } from "../agents/state/heartbeat-reader.ts";
|
|
11
4
|
import {
|
|
5
|
+
clearCoordinationCache,
|
|
12
6
|
type Heartbeat,
|
|
13
|
-
readHeartbeat,
|
|
14
7
|
setIdentityCache,
|
|
15
8
|
} from "../agents/state/heartbeat-writer.ts";
|
|
16
9
|
import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.ts";
|
|
@@ -19,17 +12,9 @@ import { assignName } from "../agents/state/names.ts";
|
|
|
19
12
|
import { coordFreshnessSeconds } from "../config.ts";
|
|
20
13
|
import { ensureEventLedgerV3 } from "../events/v3/bootstrap.ts";
|
|
21
14
|
import {
|
|
22
|
-
readCoordinationViewV3,
|
|
23
|
-
requireAuthoritySafeCoordinationViewV3,
|
|
24
|
-
} from "../events/v3/coordination-view.ts";
|
|
25
|
-
import { withGenerationCustodyV3 } from "../events/v3/generation-custody.ts";
|
|
26
|
-
import {
|
|
27
|
-
liveInstanceIdV3,
|
|
28
15
|
recordLiveHookSignalV3,
|
|
29
16
|
resolveLiveEventLedgerRouteV3,
|
|
30
17
|
} from "../events/v3/live-routing.ts";
|
|
31
|
-
import { readHookProducerStateV3 } from "../events/v3/producers/recorder.ts";
|
|
32
|
-
import { fsyncParentDirectory } from "./durable-record.ts";
|
|
33
18
|
import { stableScopeId } from "./scope-id.ts";
|
|
34
19
|
|
|
35
20
|
export interface WorkflowChildSessionV3Input {
|
|
@@ -92,45 +77,16 @@ export function startWorkflowChildSessionV3(input: WorkflowChildSessionV3Input):
|
|
|
92
77
|
}
|
|
93
78
|
const projected = readLiveCoordinationRow(input.coordRoot, input.instanceId);
|
|
94
79
|
if (!projected) throw new Error("workflow_child_v3_projection_missing");
|
|
95
|
-
const own = readHookProducerStateV3(input.coordRoot, adapter, sessionId);
|
|
96
|
-
if (!own || own.terminal || own.generation_id !== projected.v3_generation_id) {
|
|
97
|
-
throw new Error("workflow_child_v3_start_generation_changed");
|
|
98
|
-
}
|
|
99
80
|
return projected;
|
|
100
81
|
}
|
|
101
82
|
|
|
102
83
|
/** Record the authoritative terminal before removing the disposable coordination cache. */
|
|
103
84
|
export function endWorkflowChildSessionV3(
|
|
104
|
-
input: WorkflowChildSessionV3Input & { cleanExit: boolean
|
|
105
|
-
): void {
|
|
106
|
-
withGenerationCustodyV3(input.coordRoot, [liveInstanceIdV3(input.instanceId)], () =>
|
|
107
|
-
endExpectedChild(input),
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function endExpectedChild(
|
|
112
|
-
input: WorkflowChildSessionV3Input & { cleanExit: boolean; expectedGenerationId: string },
|
|
85
|
+
input: WorkflowChildSessionV3Input & { cleanExit: boolean },
|
|
113
86
|
): void {
|
|
114
87
|
requireV3Route(input.coordRoot);
|
|
115
|
-
const view = requireAuthoritySafeCoordinationViewV3(readCoordinationViewV3(input.coordRoot));
|
|
116
|
-
const instanceId = liveInstanceIdV3(input.instanceId);
|
|
117
|
-
const current = view.instances[instanceId];
|
|
118
|
-
const terminal = view.terminal_generations[input.expectedGenerationId];
|
|
119
|
-
if (terminal?.instance_id === instanceId) recoverHeldCaches(input);
|
|
120
|
-
if (current && current.generation_id !== input.expectedGenerationId) {
|
|
121
|
-
throw new Error("workflow_child_v3_generation_changed");
|
|
122
|
-
}
|
|
123
|
-
if (!current) {
|
|
124
|
-
if (!terminal || terminal.instance_id !== instanceId)
|
|
125
|
-
throw new Error("workflow_child_v3_generation_missing");
|
|
126
|
-
if (terminal.terminal?.outcome !== (input.cleanExit ? "succeeded" : "failed")) {
|
|
127
|
-
throw new Error("workflow_child_v3_terminal_outcome_changed");
|
|
128
|
-
}
|
|
129
|
-
clearExpectedCache(input);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
88
|
const row = readLiveCoordinationRow(input.coordRoot, input.instanceId);
|
|
133
|
-
if (!row?.v3_instance_id || row.v3_generation_id
|
|
89
|
+
if (!row?.v3_instance_id || !row.v3_generation_id) {
|
|
134
90
|
throw new Error("workflow_child_v3_generation_missing");
|
|
135
91
|
}
|
|
136
92
|
const result = endSessionExplicitV3({
|
|
@@ -145,104 +101,7 @@ function endExpectedChild(
|
|
|
145
101
|
`workflow_child_v3_end_failed:${result.state}${"reason" in result ? `:${result.reason}` : ""}`,
|
|
146
102
|
);
|
|
147
103
|
}
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** Persist unresolved terminal custody only for the generation this dispatch owns. */
|
|
152
|
-
export function recordWorkflowChildTerminalFailureV3(
|
|
153
|
-
input: WorkflowChildSessionV3Input & { expectedGenerationId: string; reason: string },
|
|
154
|
-
): void {
|
|
155
|
-
withGenerationCustodyV3(input.coordRoot, [liveInstanceIdV3(input.instanceId)], () => {
|
|
156
|
-
const row = readLiveCoordinationRow(input.coordRoot, input.instanceId);
|
|
157
|
-
if (!row || row.v3_generation_id !== input.expectedGenerationId) return;
|
|
158
|
-
recordLiveLifecycleChangeV3({
|
|
159
|
-
coordRoot: input.coordRoot,
|
|
160
|
-
owner: input.instanceId,
|
|
161
|
-
nativeSessionId: input.sessionId ?? input.instanceId,
|
|
162
|
-
adapter: workflowAdapter(input.adapter),
|
|
163
|
-
expectedGenerationId: input.expectedGenerationId as `gen_${string}`,
|
|
164
|
-
state: "blocked",
|
|
165
|
-
reason: input.reason,
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function heldPrefix(input: WorkflowChildSessionV3Input & { expectedGenerationId: string }): string {
|
|
171
|
-
if (!/^gen_[a-zA-Z0-9._-]{1,128}$/.test(input.expectedGenerationId))
|
|
172
|
-
throw new Error("workflow_child_v3_generation_invalid");
|
|
173
|
-
return `${basename(heartbeatPath(input.coordRoot, input.instanceId))}.terminal-${input.expectedGenerationId}-`;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function recoverHeldCaches(
|
|
177
|
-
input: WorkflowChildSessionV3Input & { expectedGenerationId: string },
|
|
178
|
-
): void {
|
|
179
|
-
const path = heartbeatPath(input.coordRoot, input.instanceId);
|
|
180
|
-
if (!existsSync(dirname(path))) return;
|
|
181
|
-
const prefix = heldPrefix(input);
|
|
182
|
-
for (const name of readdirSync(dirname(path)).filter((value) => value.startsWith(prefix))) {
|
|
183
|
-
finishHeldCache(`${dirname(path)}/${name}`, path, input.expectedGenerationId);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function clearExpectedCache(
|
|
188
|
-
input: WorkflowChildSessionV3Input & { expectedGenerationId: string },
|
|
189
|
-
): void {
|
|
190
|
-
recoverHeldCaches(input);
|
|
191
|
-
if (
|
|
192
|
-
readHeartbeat(input.coordRoot, input.instanceId)?.v3_generation_id !==
|
|
193
|
-
input.expectedGenerationId
|
|
194
|
-
)
|
|
195
|
-
return;
|
|
196
|
-
const path = heartbeatPath(input.coordRoot, input.instanceId);
|
|
197
|
-
const held = `${dirname(path)}/${heldPrefix(input)}${randomUUID()}`;
|
|
198
|
-
try {
|
|
199
|
-
// A unique held name pins the actual bytes atomically. Checking before
|
|
200
|
-
// unlink alone could consume a replacement published at this boundary.
|
|
201
|
-
renameSync(path, held);
|
|
202
|
-
fsyncParentDirectory(path);
|
|
203
|
-
} catch (error) {
|
|
204
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT") return;
|
|
205
|
-
throw error;
|
|
206
|
-
}
|
|
207
|
-
finishHeldCache(held, path, input.expectedGenerationId);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function finishHeldCache(held: string, path: string, expectedGenerationId: string): void {
|
|
211
|
-
let removed = false;
|
|
212
|
-
let cleanupFailed = false;
|
|
213
|
-
let cleanupError: unknown;
|
|
214
|
-
try {
|
|
215
|
-
const cache = JSON.parse(readFileSync(held, "utf8")) as Heartbeat;
|
|
216
|
-
if (cache.v3_generation_id === expectedGenerationId) {
|
|
217
|
-
unlinkSync(held);
|
|
218
|
-
fsyncParentDirectory(held);
|
|
219
|
-
removed = true;
|
|
220
|
-
}
|
|
221
|
-
} catch (error) {
|
|
222
|
-
cleanupFailed = true;
|
|
223
|
-
cleanupError = error;
|
|
224
|
-
}
|
|
225
|
-
if (!removed) {
|
|
226
|
-
try {
|
|
227
|
-
// Never overwrite a later publication. Keep the held evidence when
|
|
228
|
-
// another cache already owns the live name.
|
|
229
|
-
linkSync(held, path);
|
|
230
|
-
fsyncParentDirectory(path);
|
|
231
|
-
unlinkSync(held);
|
|
232
|
-
fsyncParentDirectory(held);
|
|
233
|
-
} catch (error) {
|
|
234
|
-
if ((error as NodeJS.ErrnoException).code !== "EEXIST") {
|
|
235
|
-
if (cleanupFailed) {
|
|
236
|
-
throw new AggregateError(
|
|
237
|
-
[cleanupError, error],
|
|
238
|
-
`workflow_child_v3_cache_cleanup_and_restore_failed: ${String(cleanupError)}; restoration: ${String(error)}`,
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
throw error;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
if (cleanupFailed) throw cleanupError;
|
|
104
|
+
clearCoordinationCache(input.coordRoot, input.instanceId);
|
|
246
105
|
}
|
|
247
106
|
|
|
248
107
|
function requireV3Route(coordRoot: string) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { type RepoSnapshot, repoComparisonProblems } from "../context/observation.ts";
|
|
5
5
|
import { DIAGNOSTIC_ADVICE_SCHEMA_VERSION } from "../diagnostics/contract.ts";
|
|
6
6
|
import type {
|
|
7
7
|
NormalizedPolicy,
|
|
@@ -12,7 +12,6 @@ import type {
|
|
|
12
12
|
import { policyDigest } from "../policy/index.ts";
|
|
13
13
|
import { isCanonicalWorkflowAttemptContext } from "./attempt-context.ts";
|
|
14
14
|
import { stableDigest, writeImmutableJson } from "./durable-record.ts";
|
|
15
|
-
import { failureEvidenceAllowsUncharged, isSpawnFailureEvidence } from "./failure-evidence.ts";
|
|
16
15
|
import { readWorkflowRunManifest } from "./run-state.ts";
|
|
17
16
|
import type {
|
|
18
17
|
AcceptanceCriterion,
|
|
@@ -243,25 +242,17 @@ export function digestResult(value: unknown, kind?: "text" | "json"): ResultDige
|
|
|
243
242
|
* attempt was informative about the work — charge it. This also keeps a
|
|
244
243
|
* resumed run whose earlier segment did real work from being written off by
|
|
245
244
|
* a later environment failure.
|
|
246
|
-
* 3.
|
|
247
|
-
*
|
|
248
|
-
*
|
|
245
|
+
* 3. Otherwise, among the failed agents, environment wins over upstream: a
|
|
246
|
+
* missing binary means nothing ran at all, and it is the operator-chosen
|
|
247
|
+
* hard stop.
|
|
249
248
|
* 4. Anything else is undefined ⇒ a charged work failure, exactly as today.
|
|
250
249
|
*/
|
|
251
250
|
export function deriveRunFailureClass(
|
|
252
251
|
status: "succeeded" | "failed",
|
|
253
|
-
agents: readonly Pick<WorkflowAgentProof, "status" | "class"
|
|
252
|
+
agents: readonly Pick<WorkflowAgentProof, "status" | "class">[],
|
|
254
253
|
): SpawnFailureClass | undefined {
|
|
255
254
|
if (status !== "failed") return undefined;
|
|
256
|
-
if (
|
|
257
|
-
agents.length === 0 ||
|
|
258
|
-
agents.some(
|
|
259
|
-
(agent) =>
|
|
260
|
-
agent.status !== "failed" ||
|
|
261
|
-
(agent.class !== "environment" && agent.class !== "upstream") ||
|
|
262
|
-
!failureEvidenceAllowsUncharged(agent.failure_evidence),
|
|
263
|
-
)
|
|
264
|
-
) {
|
|
255
|
+
if (agents.some((agent) => agent.status === "succeeded" || agent.status === "cached")) {
|
|
265
256
|
return undefined;
|
|
266
257
|
}
|
|
267
258
|
const failed = agents.filter((agent) => agent.status === "failed");
|
|
@@ -394,13 +385,6 @@ export function readWorkflowProof(coordRoot: string, runId: string): WorkflowPro
|
|
|
394
385
|
proof.run.class !== "environment" &&
|
|
395
386
|
proof.run.class !== "upstream" &&
|
|
396
387
|
proof.run.class !== "decision") ||
|
|
397
|
-
!Array.isArray(proof.agents) ||
|
|
398
|
-
proof.agents.some(
|
|
399
|
-
(agent) =>
|
|
400
|
-
!agent ||
|
|
401
|
-
typeof agent !== "object" ||
|
|
402
|
-
(agent.failure_evidence !== undefined && !isSpawnFailureEvidence(agent.failure_evidence)),
|
|
403
|
-
) ||
|
|
404
388
|
(proof.run.work_context !== undefined &&
|
|
405
389
|
(!proof.run.work_item_id ||
|
|
406
390
|
proof.run.work_context.id !== proof.run.work_item_id ||
|
|
@@ -415,15 +399,8 @@ export function readWorkflowProof(coordRoot: string, runId: string): WorkflowPro
|
|
|
415
399
|
) {
|
|
416
400
|
throw new Error(`workflow proof at ${path} has an unsupported or mismatched schema`);
|
|
417
401
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
proof.repository.before.dirty_paths_truncated ||
|
|
421
|
-
proof.repository.after.dirty_paths_truncated ||
|
|
422
|
-
!proof.repository.before.branch?.trim() ||
|
|
423
|
-
!proof.repository.after.branch?.trim() ||
|
|
424
|
-
!proof.repository.before.head?.trim() ||
|
|
425
|
-
!proof.repository.after.head?.trim()
|
|
426
|
-
) {
|
|
402
|
+
// Packets written before observation checks may carry unproven drift.
|
|
403
|
+
if (repoProblems(proof.repository).length > 0) {
|
|
427
404
|
proof.repository = buildRepoEvidence(proof.repository.before, proof.repository.after);
|
|
428
405
|
proof.unknowns = proof.unknowns.filter((item) => item.code !== "repository_drift_incomplete");
|
|
429
406
|
proof.unknowns.push({
|
|
@@ -544,29 +521,16 @@ export function renderWorkflowProof(proof: WorkflowProof): string {
|
|
|
544
521
|
: "admission: shadow not needed; no real child dispatch",
|
|
545
522
|
);
|
|
546
523
|
}
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
comparable &&
|
|
551
|
-
!proof.repository.before.dirty_paths_truncated &&
|
|
552
|
-
!proof.repository.after.dirty_paths_truncated &&
|
|
553
|
-
proof.repository.before.branch?.trim() &&
|
|
554
|
-
proof.repository.after.branch?.trim() &&
|
|
555
|
-
proof.repository.before.head?.trim() &&
|
|
556
|
-
proof.repository.after.head?.trim()
|
|
557
|
-
? proof.repository
|
|
558
|
-
: buildRepoEvidence(proof.repository.before, proof.repository.after);
|
|
559
|
-
const drift = repo.drift;
|
|
560
|
-
if (!comparable) {
|
|
561
|
-
lines.push(`repository: drift unknown. ${drift.note}`);
|
|
524
|
+
const problems = repoProblems(proof.repository);
|
|
525
|
+
if (problems.length > 0) {
|
|
526
|
+
lines.push(`repository: drift unknown. ${unverifiedDrift(problems)}`);
|
|
562
527
|
} else {
|
|
563
|
-
const
|
|
564
|
-
drift.dirty_paths_added === null || drift.dirty_paths_cleared === null
|
|
565
|
-
? "dirty-path drift unknown"
|
|
566
|
-
: `${drift.dirty_paths_added.length} dirty added, ${drift.dirty_paths_cleared.length} cleared`;
|
|
528
|
+
const { before, after, drift } = proof.repository;
|
|
567
529
|
lines.push(
|
|
568
|
-
`repository: branch ${
|
|
569
|
-
`HEAD ${short(
|
|
530
|
+
`repository: branch ${before.branch} -> ${after.branch}; ` +
|
|
531
|
+
`HEAD ${short(before.head)} -> ${short(after.head)}; ` +
|
|
532
|
+
`${drift.dirty_paths_added?.length ?? "unknown"} dirty added, ` +
|
|
533
|
+
`${drift.dirty_paths_cleared?.length ?? "unknown"} cleared`,
|
|
570
534
|
);
|
|
571
535
|
}
|
|
572
536
|
if (proof.unknowns.length > 0) {
|
|
@@ -580,8 +544,8 @@ export function renderWorkflowProof(proof: WorkflowProof): string {
|
|
|
580
544
|
function buildRepoEvidence(beforeRaw: RepoSnapshot, afterRaw: RepoSnapshot): WorkflowRepoEvidence {
|
|
581
545
|
const before = normalizeRepoSnapshot(beforeRaw);
|
|
582
546
|
const after = normalizeRepoSnapshot(afterRaw);
|
|
583
|
-
const
|
|
584
|
-
if (
|
|
547
|
+
const problems = repoProblems({ before, after });
|
|
548
|
+
if (problems.length > 0) {
|
|
585
549
|
return {
|
|
586
550
|
source: "engine",
|
|
587
551
|
before,
|
|
@@ -593,47 +557,43 @@ function buildRepoEvidence(beforeRaw: RepoSnapshot, afterRaw: RepoSnapshot): Wor
|
|
|
593
557
|
dirty_paths_cleared: null,
|
|
594
558
|
dirty_paths_retained: null,
|
|
595
559
|
incomplete: true,
|
|
596
|
-
note:
|
|
560
|
+
note: unverifiedDrift(problems),
|
|
597
561
|
},
|
|
598
562
|
};
|
|
599
563
|
}
|
|
600
|
-
const pathsComparable = !before.dirty_paths_truncated && !after.dirty_paths_truncated;
|
|
601
|
-
const branchKnown = Boolean(before.branch && after.branch);
|
|
602
|
-
const headKnown = Boolean(before.head && after.head);
|
|
603
564
|
const beforeDirty = new Set(before.dirty_paths);
|
|
604
565
|
const afterDirty = new Set(after.dirty_paths);
|
|
605
566
|
const retained = after.dirty_paths.filter((path) => beforeDirty.has(path));
|
|
606
|
-
const
|
|
607
|
-
...(!pathsComparable
|
|
608
|
-
? ["Dirty-path drift is unknown because a snapshot list was truncated."]
|
|
609
|
-
: []),
|
|
610
|
-
...(!branchKnown ? ["Branch drift is unknown because a snapshot has no branch."] : []),
|
|
611
|
-
...(!headKnown ? ["HEAD drift is unknown because a snapshot has no HEAD."] : []),
|
|
612
|
-
...(pathsComparable && retained.length > 0
|
|
613
|
-
? ["Snapshots cannot prove whether retained dirty paths changed during the run."]
|
|
614
|
-
: []),
|
|
615
|
-
];
|
|
616
|
-
const incomplete = notes.length > 0;
|
|
567
|
+
const incomplete = retained.length > 0;
|
|
617
568
|
return {
|
|
618
569
|
source: "engine",
|
|
619
570
|
before,
|
|
620
571
|
after,
|
|
621
572
|
drift: {
|
|
622
|
-
branch_changed:
|
|
623
|
-
head_changed:
|
|
624
|
-
dirty_paths_added:
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
dirty_paths_cleared: pathsComparable
|
|
628
|
-
? before.dirty_paths.filter((path) => !afterDirty.has(path))
|
|
629
|
-
: null,
|
|
630
|
-
dirty_paths_retained: pathsComparable ? retained : null,
|
|
573
|
+
branch_changed: before.branch !== after.branch,
|
|
574
|
+
head_changed: before.head !== after.head,
|
|
575
|
+
dirty_paths_added: after.dirty_paths.filter((path) => !beforeDirty.has(path)),
|
|
576
|
+
dirty_paths_cleared: before.dirty_paths.filter((path) => !afterDirty.has(path)),
|
|
577
|
+
dirty_paths_retained: retained,
|
|
631
578
|
incomplete,
|
|
632
|
-
note: incomplete
|
|
579
|
+
note: incomplete
|
|
580
|
+
? "Snapshots cannot prove whether retained dirty paths changed during the run."
|
|
581
|
+
: undefined,
|
|
633
582
|
},
|
|
634
583
|
};
|
|
635
584
|
}
|
|
636
585
|
|
|
586
|
+
function repoProblems(repository: {
|
|
587
|
+
before: WorkflowRepoSnapshot;
|
|
588
|
+
after: WorkflowRepoSnapshot;
|
|
589
|
+
}): string[] {
|
|
590
|
+
return repoComparisonProblems(repository.before, repository.after, ["before", "after"]);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function unverifiedDrift(problems: string[]): string {
|
|
594
|
+
return `Repository drift could not be verified: ${problems.join("; ")}. Re-check before editing.`;
|
|
595
|
+
}
|
|
596
|
+
|
|
637
597
|
function normalizeRepoSnapshot(snapshot: RepoSnapshot): WorkflowRepoSnapshot {
|
|
638
598
|
return {
|
|
639
599
|
cwd: snapshot.cwd,
|
|
@@ -649,30 +609,6 @@ function normalizeRepoSnapshot(snapshot: RepoSnapshot): WorkflowRepoSnapshot {
|
|
|
649
609
|
};
|
|
650
610
|
}
|
|
651
611
|
|
|
652
|
-
function snapshotFailure(label: string, snapshot: WorkflowRepoSnapshot): string {
|
|
653
|
-
return `${label} snapshot is ${snapshot.observation}${snapshot.observation_detail ? ` (${snapshot.observation_detail})` : ""}`;
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
function repoComparisonProblem(
|
|
657
|
-
before: WorkflowRepoSnapshot,
|
|
658
|
-
after: WorkflowRepoSnapshot,
|
|
659
|
-
): string | undefined {
|
|
660
|
-
if (!observationsComparable(before, after)) {
|
|
661
|
-
return [
|
|
662
|
-
...(before.observation !== "ok" ? [snapshotFailure("before", before)] : []),
|
|
663
|
-
...(after.observation !== "ok" ? [snapshotFailure("after", after)] : []),
|
|
664
|
-
].join("; ");
|
|
665
|
-
}
|
|
666
|
-
const unknownRoots = [
|
|
667
|
-
...(!before.root || !isAbsolute(before.root) ? ["before"] : []),
|
|
668
|
-
...(!after.root || !isAbsolute(after.root) ? ["after"] : []),
|
|
669
|
-
];
|
|
670
|
-
if (unknownRoots.length > 0)
|
|
671
|
-
return `${unknownRoots.join(" and ")} snapshot repository root is unknown`;
|
|
672
|
-
if (before.root !== after.root) return "snapshots name different repository roots";
|
|
673
|
-
return undefined;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
612
|
function buildAdapterCoverage(
|
|
677
613
|
agents: WorkflowAgentProof[],
|
|
678
614
|
claims: Readonly<Record<string, AdapterEvidenceCapability | undefined>> | undefined,
|