@akagilnc/pi-workflow-roles 0.1.2139 → 0.1.2146
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/doctor-contracts.js +10 -2
- package/dist/engine-labor-fallback.js +129 -114
- package/dist/merger-contracts.js +9 -2
- package/dist/navigator-attendance.js +9 -1
- package/dist/package-contracts/judge-output.js +8 -1
- package/dist/package-contracts/reviewer-output.js +9 -1
- package/dist/package-contracts/terminating-tools.js +14 -5
- package/dist/public-cli/main.js +116 -64
- package/package.json +1 -1
- package/scripts/build-package.mjs +2 -0
- package/src/doctor-contracts.ts +19 -3
- package/src/engine-labor-fallback.ts +100 -2
- package/src/factory-board.ts +6 -2
- package/src/merger-contracts.ts +18 -3
- package/src/navigator-attendance.ts +16 -1
- package/src/package-contracts/judge-output.ts +25 -2
- package/src/package-contracts/reviewer-output.ts +23 -2
- package/src/package-contracts/terminating-tools.ts +22 -6
- package/src/package-contracts/worker-output.ts +10 -2
- package/src/public-cli/settlement.ts +51 -17
- package/src/role-runtime.ts +8 -1
- package/src/taishi-metric-families/acceptance-success-rework.ts +6 -3
package/dist/doctor-contracts.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { canonicalJson } from "./canonical-json.js";
|
|
3
|
+
import { seatFallbackBaseStatus, seatFallbackStatusHasLawfulEvidence, } from "./engine-labor-fallback.js";
|
|
3
4
|
import { openToolObjectFromUnion } from "./open-tool-schema.js";
|
|
4
5
|
export const DOCTOR_EVIDENCE_TOOL_NAME = "ak_doctor_evidence";
|
|
5
6
|
export const DOCTOR_OUTPUT_TOOL_NAME = "ak_doctor_output";
|
|
@@ -66,13 +67,20 @@ catch {
|
|
|
66
67
|
} }
|
|
67
68
|
export function validateDoctorSubmissionShape(value) {
|
|
68
69
|
const status = read(value, "status");
|
|
69
|
-
|
|
70
|
+
const base = typeof status === "string" ? seatFallbackBaseStatus(status) : status;
|
|
71
|
+
if (base !== "completed" && base !== "refused")
|
|
70
72
|
throw new DoctorSubmissionContractError("Doctor submission has no recognized execution status");
|
|
73
|
+
// ADR 0071: tainted status requires latch-shaped engineLaborFallback evidence.
|
|
74
|
+
if (typeof status === "string" && !seatFallbackStatusHasLawfulEvidence(status, value)) {
|
|
75
|
+
throw new DoctorSubmissionContractError("Doctor submission has no recognized execution status");
|
|
76
|
+
}
|
|
71
77
|
return value;
|
|
72
78
|
}
|
|
73
79
|
export function validateRecordedDoctorOutput(value) {
|
|
74
80
|
const output = validateDoctorSubmissionShape(value);
|
|
75
|
-
|
|
81
|
+
const status = read(output, "status");
|
|
82
|
+
const base = typeof status === "string" ? seatFallbackBaseStatus(status) : status;
|
|
83
|
+
if (base === "completed" && read(output, "cost") === undefined)
|
|
76
84
|
throw new Error("Completed Doctor receipt has no runtime-owned cost testimony");
|
|
77
85
|
return output;
|
|
78
86
|
}
|
|
@@ -1,121 +1,136 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* #380 — sole shared seat-fallback declaration after engine detour failure.
|
|
3
|
-
* Detour rejoins the main road (ADR 0069); silence is the only crime.
|
|
4
|
-
* Construction of the typed receipt field lives in exactly one site (S1).
|
|
5
|
-
*/
|
|
6
|
-
/** Activation-scoped latch holder (one parent seat activation at a time). */
|
|
7
1
|
let activationLatch;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
});
|
|
2
|
+
function buildEngineLaborFallbackField(input) {
|
|
3
|
+
return Object.freeze({
|
|
4
|
+
engineLaborFallback: Object.freeze({
|
|
5
|
+
engine: input.engine,
|
|
6
|
+
failure: input.failure,
|
|
7
|
+
laborBy: "seat"
|
|
8
|
+
})
|
|
9
|
+
});
|
|
17
10
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
export function withEngineLaborFallbackField(receipt, field) {
|
|
40
|
-
if (field !== undefined) {
|
|
41
|
-
return { ...receipt, ...field };
|
|
42
|
-
}
|
|
43
|
-
if (!Object.prototype.hasOwnProperty.call(receipt, "engineLaborFallback")) {
|
|
44
|
-
return receipt;
|
|
45
|
-
}
|
|
46
|
-
const { engineLaborFallback: _forged, ...rest } = receipt;
|
|
47
|
-
return rest;
|
|
48
|
-
}
|
|
49
|
-
/** Install the activation-scoped latch (any role session_start with engine). */
|
|
50
|
-
export function installActivationEngineLaborFallbackLatch(latch) {
|
|
51
|
-
activationLatch = latch;
|
|
52
|
-
}
|
|
53
|
-
/** Clear activation latch (session end / next activation). */
|
|
54
|
-
export function clearActivationEngineLaborFallbackLatch() {
|
|
55
|
-
activationLatch = undefined;
|
|
56
|
-
}
|
|
57
|
-
/** Active activation latch, if any (legs inherit parent seat activation). */
|
|
58
|
-
export function activationEngineLaborFallbackLatch() {
|
|
59
|
-
return activationLatch;
|
|
60
|
-
}
|
|
61
|
-
/** Read fallback field from the active activation latch. */
|
|
62
|
-
export function readActivationEngineLaborFallbackField() {
|
|
63
|
-
return readEngineLaborFallbackField(activationLatch);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Read a previously attached declaration from a typed receipt / details object.
|
|
67
|
-
* Rebuilds via the sole construction site — callers must spread, never re-key.
|
|
68
|
-
*/
|
|
69
|
-
export function readEngineLaborFallbackFieldFrom(source) {
|
|
70
|
-
if (typeof source !== "object" || source === null || Array.isArray(source)) {
|
|
71
|
-
return undefined;
|
|
72
|
-
}
|
|
73
|
-
let raw;
|
|
11
|
+
const SEAT_FALLBACK_STATUS_SUFFIX = "-by-fallback";
|
|
12
|
+
function isSeatFallbackTaintedStatus(status) {
|
|
13
|
+
return status.endsWith(SEAT_FALLBACK_STATUS_SUFFIX);
|
|
14
|
+
}
|
|
15
|
+
function seatFallbackBaseStatus(status) {
|
|
16
|
+
return isSeatFallbackTaintedStatus(status) ? status.slice(0, -SEAT_FALLBACK_STATUS_SUFFIX.length) : status;
|
|
17
|
+
}
|
|
18
|
+
function seatFallbackStatusHasLawfulEvidence(status, source) {
|
|
19
|
+
if (!isSeatFallbackTaintedStatus(status)) return true;
|
|
20
|
+
return readEngineLaborFallbackFieldFrom(source) !== void 0;
|
|
21
|
+
}
|
|
22
|
+
function taintStatusForSeatFallback(status) {
|
|
23
|
+
if (status.length === 0 || isSeatFallbackTaintedStatus(status)) return status;
|
|
24
|
+
return `${status}${SEAT_FALLBACK_STATUS_SUFFIX}`;
|
|
25
|
+
}
|
|
26
|
+
const STATUS_DISCRIMINATOR_KEYS = ["judgeStatus", "status"];
|
|
27
|
+
function taintReceiptStatusDiscriminators(receipt) {
|
|
28
|
+
let next;
|
|
29
|
+
for (const key of STATUS_DISCRIMINATOR_KEYS) {
|
|
30
|
+
if (!Object.prototype.hasOwnProperty.call(receipt, key)) continue;
|
|
31
|
+
let value;
|
|
74
32
|
try {
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return undefined;
|
|
33
|
+
value = receipt[key];
|
|
34
|
+
} catch {
|
|
35
|
+
continue;
|
|
79
36
|
}
|
|
80
|
-
if (typeof
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
rec.laborBy !== "seat") {
|
|
86
|
-
return undefined;
|
|
37
|
+
if (typeof value !== "string" || value.length === 0) continue;
|
|
38
|
+
const tainted = taintStatusForSeatFallback(value);
|
|
39
|
+
if (tainted === value) continue;
|
|
40
|
+
if (next === void 0) {
|
|
41
|
+
next = { ...receipt };
|
|
87
42
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
43
|
+
next[key] = tainted;
|
|
44
|
+
}
|
|
45
|
+
return next === void 0 ? receipt : next;
|
|
92
46
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
47
|
+
function createEngineLaborFallbackLatch() {
|
|
48
|
+
return { field: void 0 };
|
|
49
|
+
}
|
|
50
|
+
function recordEngineLaborFallback(latch, input) {
|
|
51
|
+
const field = buildEngineLaborFallbackField(input);
|
|
52
|
+
if (latch.field === void 0) latch.field = field;
|
|
53
|
+
return latch.field;
|
|
54
|
+
}
|
|
55
|
+
function readEngineLaborFallbackField(latch) {
|
|
56
|
+
return latch?.field;
|
|
57
|
+
}
|
|
58
|
+
function withEngineLaborFallbackField(receipt, field) {
|
|
59
|
+
if (field !== void 0) {
|
|
60
|
+
const taintedReceipt = taintReceiptStatusDiscriminators(receipt);
|
|
61
|
+
return { ...taintedReceipt, ...field };
|
|
62
|
+
}
|
|
63
|
+
if (!Object.prototype.hasOwnProperty.call(receipt, "engineLaborFallback")) {
|
|
64
|
+
return receipt;
|
|
65
|
+
}
|
|
66
|
+
const { engineLaborFallback: _forged, ...rest } = receipt;
|
|
67
|
+
return rest;
|
|
68
|
+
}
|
|
69
|
+
function installActivationEngineLaborFallbackLatch(latch) {
|
|
70
|
+
activationLatch = latch;
|
|
71
|
+
}
|
|
72
|
+
function clearActivationEngineLaborFallbackLatch() {
|
|
73
|
+
activationLatch = void 0;
|
|
74
|
+
}
|
|
75
|
+
function activationEngineLaborFallbackLatch() {
|
|
76
|
+
return activationLatch;
|
|
77
|
+
}
|
|
78
|
+
function readActivationEngineLaborFallbackField() {
|
|
79
|
+
return readEngineLaborFallbackField(activationLatch);
|
|
80
|
+
}
|
|
81
|
+
function readEngineLaborFallbackFieldFrom(source) {
|
|
82
|
+
if (typeof source !== "object" || source === null || Array.isArray(source)) {
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
let raw;
|
|
86
|
+
try {
|
|
87
|
+
raw = source.engineLaborFallback;
|
|
88
|
+
} catch {
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
|
|
92
|
+
const rec = raw;
|
|
93
|
+
if (typeof rec.engine !== "string" || typeof rec.failure !== "string" || rec.laborBy !== "seat") {
|
|
94
|
+
return void 0;
|
|
95
|
+
}
|
|
96
|
+
return buildEngineLaborFallbackField({
|
|
97
|
+
engine: rec.engine,
|
|
98
|
+
failure: rec.failure
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function restoreEngineLaborFallbackFromSessionEntries(latch, entries, toolName) {
|
|
102
|
+
for (const entry of entries) {
|
|
103
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
104
|
+
const row = entry;
|
|
105
|
+
if (row.type !== "message") continue;
|
|
106
|
+
const message = row.message;
|
|
107
|
+
if (typeof message !== "object" || message === null) continue;
|
|
108
|
+
const msg = message;
|
|
109
|
+
if (msg.role !== "toolResult") continue;
|
|
110
|
+
if (msg.toolName !== toolName) continue;
|
|
111
|
+
const field = readEngineLaborFallbackFieldFrom(msg.details);
|
|
112
|
+
if (field === void 0) continue;
|
|
113
|
+
recordEngineLaborFallback(latch, {
|
|
114
|
+
engine: field.engineLaborFallback.engine,
|
|
115
|
+
failure: field.engineLaborFallback.failure
|
|
116
|
+
});
|
|
117
|
+
}
|
|
121
118
|
}
|
|
119
|
+
export {
|
|
120
|
+
SEAT_FALLBACK_STATUS_SUFFIX,
|
|
121
|
+
activationEngineLaborFallbackLatch,
|
|
122
|
+
buildEngineLaborFallbackField,
|
|
123
|
+
clearActivationEngineLaborFallbackLatch,
|
|
124
|
+
createEngineLaborFallbackLatch,
|
|
125
|
+
installActivationEngineLaborFallbackLatch,
|
|
126
|
+
isSeatFallbackTaintedStatus,
|
|
127
|
+
readActivationEngineLaborFallbackField,
|
|
128
|
+
readEngineLaborFallbackField,
|
|
129
|
+
readEngineLaborFallbackFieldFrom,
|
|
130
|
+
recordEngineLaborFallback,
|
|
131
|
+
restoreEngineLaborFallbackFromSessionEntries,
|
|
132
|
+
seatFallbackBaseStatus,
|
|
133
|
+
seatFallbackStatusHasLawfulEvidence,
|
|
134
|
+
taintStatusForSeatFallback,
|
|
135
|
+
withEngineLaborFallbackField
|
|
136
|
+
};
|
package/dist/merger-contracts.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { isFullGitObjectId } from "./git-object-id.js";
|
|
3
3
|
import { exactUtf8 } from "./exact-utf8.js";
|
|
4
|
+
import { seatFallbackBaseStatus, seatFallbackStatusHasLawfulEvidence, } from "./engine-labor-fallback.js";
|
|
4
5
|
import { sha256Hex } from "./sha256.js";
|
|
5
6
|
import { openToolObjectFromUnion } from "./open-tool-schema.js";
|
|
6
7
|
const oidPattern = "^(?:[0-9a-f]{40}|[0-9a-f]{64})$";
|
|
@@ -68,9 +69,15 @@ export function validateMergerInput(value) {
|
|
|
68
69
|
export function validateMergerOutput(value, expectedAttemptId) {
|
|
69
70
|
if (!record(value) || (expectedAttemptId !== undefined && value.attemptId !== expectedAttemptId))
|
|
70
71
|
throw new Error("Merger output attempt mismatch");
|
|
71
|
-
|
|
72
|
+
const status = typeof value.status === "string" ? value.status : undefined;
|
|
73
|
+
const statusBase = status !== undefined ? seatFallbackBaseStatus(status) : undefined;
|
|
74
|
+
// ADR 0071: tainted status requires latch-shaped engineLaborFallback evidence.
|
|
75
|
+
if (status !== undefined && !seatFallbackStatusHasLawfulEvidence(status, value)) {
|
|
76
|
+
throw new Error("Merger output has no recognized execution discriminator");
|
|
77
|
+
}
|
|
78
|
+
if (statusBase === "completed" && isFullGitObjectId(value.mergeCommitId))
|
|
72
79
|
return structuredClone(value);
|
|
73
|
-
if (
|
|
80
|
+
if (statusBase === "escalate")
|
|
74
81
|
return structuredClone(value);
|
|
75
82
|
throw new Error("Merger output has no recognized execution discriminator");
|
|
76
83
|
}
|
|
@@ -6,6 +6,7 @@ import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
|
6
6
|
import { createAssistantMessageEventStream } from "@earendil-works/pi-ai";
|
|
7
7
|
import { Type } from "typebox";
|
|
8
8
|
import { Value } from "typebox/value";
|
|
9
|
+
import { seatFallbackBaseStatus } from "./engine-labor-fallback.js";
|
|
9
10
|
import {
|
|
10
11
|
NAVIGATOR_INVOCATION_ENTRY,
|
|
11
12
|
mintNavigatorInvocationId
|
|
@@ -257,6 +258,13 @@ function createNavigatorPrepareTool(onOutput) {
|
|
|
257
258
|
}
|
|
258
259
|
};
|
|
259
260
|
}
|
|
261
|
+
function statusListMatchesSettlement(candidateStatuses, settlementStatus) {
|
|
262
|
+
if (candidateStatuses.includes(settlementStatus)) return true;
|
|
263
|
+
const settlementBase = seatFallbackBaseStatus(settlementStatus);
|
|
264
|
+
return candidateStatuses.some(
|
|
265
|
+
(status) => seatFallbackBaseStatus(status) === settlementBase
|
|
266
|
+
);
|
|
267
|
+
}
|
|
260
268
|
function selectNavigatorCandidate(candidates, settlement) {
|
|
261
269
|
if (settlement.kind !== "accepted") return void 0;
|
|
262
270
|
const usable = candidates.filter((candidate) => candidate.next !== void 0);
|
|
@@ -267,7 +275,7 @@ function selectNavigatorCandidate(candidates, settlement) {
|
|
|
267
275
|
if (rolePhaseMatched.length > 0) {
|
|
268
276
|
if (settlement.status !== void 0) {
|
|
269
277
|
const statusSpecific = rolePhaseMatched.find(
|
|
270
|
-
(candidate) => candidate.matches?.statuses
|
|
278
|
+
(candidate) => candidate.matches?.statuses !== void 0 && statusListMatchesSettlement(candidate.matches.statuses, settlement.status)
|
|
271
279
|
);
|
|
272
280
|
if (statusSpecific !== void 0) {
|
|
273
281
|
return { candidate: statusSpecific, matchedToSettlement: true };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Package-owned Judge output leaf — no role registration surface. */
|
|
2
|
+
import { seatFallbackBaseStatus, seatFallbackStatusHasLawfulEvidence, } from "../engine-labor-fallback.js";
|
|
2
3
|
export const JUDGE_OUTPUT_TOOL_NAME = "ak_judge_output";
|
|
3
4
|
export const JUDGE_ACCEPTED_TEXT = "Judge verdict accepted";
|
|
4
5
|
export function validateAcceptedJudgeDetails(verdict) {
|
|
@@ -11,7 +12,13 @@ export function validateAcceptedJudgeDetails(verdict) {
|
|
|
11
12
|
catch {
|
|
12
13
|
throw new Error("Judge verdict has no execution discriminator");
|
|
13
14
|
}
|
|
14
|
-
if (
|
|
15
|
+
if (typeof judgeStatus !== "string") {
|
|
16
|
+
throw new Error("Judge verdict has no execution discriminator");
|
|
17
|
+
}
|
|
18
|
+
const base = seatFallbackBaseStatus(judgeStatus);
|
|
19
|
+
if (["converged", "continue", "escalate"].includes(base) &&
|
|
20
|
+
seatFallbackStatusHasLawfulEvidence(judgeStatus, verdict)) {
|
|
15
21
|
return verdict;
|
|
22
|
+
}
|
|
16
23
|
throw new Error("Judge verdict has no execution discriminator");
|
|
17
24
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Package-owned Reviewer intent and runtime-receipt leaves — no role registration surface. */
|
|
2
|
+
import { seatFallbackBaseStatus, seatFallbackStatusHasLawfulEvidence, } from "../engine-labor-fallback.js";
|
|
2
3
|
export const REVIEWER_OUTPUT_TOOL_NAME = "ak_reviewer_output";
|
|
3
4
|
export const REVIEWER_ACCEPTED_TEXT = "Reviewer report accepted";
|
|
4
5
|
function isRecord(value) {
|
|
@@ -24,6 +25,11 @@ export function validateReviewerIntent(output) {
|
|
|
24
25
|
}
|
|
25
26
|
/** Validate runtime-owned facts at their real identity seams (target pins + plain text). */
|
|
26
27
|
export function validateRuntimeReviewerReceipt(output) {
|
|
28
|
+
const status = read(output, "status");
|
|
29
|
+
// ADR 0071: tainted top-level status requires latch-shaped engineLaborFallback evidence.
|
|
30
|
+
if (typeof status === "string" && !seatFallbackStatusHasLawfulEvidence(status, output)) {
|
|
31
|
+
throw new Error("Reviewer receipt has no recognized execution discriminator");
|
|
32
|
+
}
|
|
27
33
|
const acceptedBatch = read(output, "acceptedBatch");
|
|
28
34
|
const identities = read(output, "identities");
|
|
29
35
|
const construction = read(identities, "construction");
|
|
@@ -91,7 +97,9 @@ export function validateRuntimeReviewerReceipt(output) {
|
|
|
91
97
|
export function projectReviewerIntentToReceipt(intentValue, receiptValue) {
|
|
92
98
|
const intent = validateReviewerIntent(intentValue);
|
|
93
99
|
const receipt = validateRuntimeReviewerReceipt(receiptValue);
|
|
94
|
-
|
|
100
|
+
const receiptStatus = String(receipt.status);
|
|
101
|
+
const receiptBase = seatFallbackBaseStatus(receiptStatus);
|
|
102
|
+
if (receiptBase !== intent.status || (intent.status === "completed" ? receipt.diagnostic !== undefined : receipt.diagnostic !== intent.diagnostic)) {
|
|
95
103
|
throw new Error("Reviewer intent and runtime receipt disagree");
|
|
96
104
|
}
|
|
97
105
|
return receipt;
|
|
@@ -6,6 +6,7 @@ import { COLLECTOR_ACCEPTED_TEXT, COLLECTOR_OUTPUT_TOOL, validateAcceptedCollect
|
|
|
6
6
|
import { JUDGE_ACCEPTED_TEXT, JUDGE_OUTPUT_TOOL_NAME, validateAcceptedJudgeDetails, } from "./judge-output.js";
|
|
7
7
|
import { REVIEWER_ACCEPTED_TEXT, REVIEWER_OUTPUT_TOOL_NAME, projectReviewerIntentToReceipt, validateReviewerIntent, validateRuntimeReviewerReceipt, } from "./reviewer-output.js";
|
|
8
8
|
import { isAuditEscalationResult } from "../audit-escalation.js";
|
|
9
|
+
import { seatFallbackBaseStatus, seatFallbackStatusHasLawfulEvidence, } from "../engine-labor-fallback.js";
|
|
9
10
|
import { DOCTOR_OUTPUT_TOOL_NAME, validateDoctorSubmissionShape, validateRecordedDoctorOutput } from "../doctor-contracts.js";
|
|
10
11
|
import { MERGER_ACCEPTED_TEXT, MERGER_OUTPUT_TOOL_NAME, validateMergerOutput } from "../merger-contracts.js";
|
|
11
12
|
import { CODER_ACCEPTED_TEXT, CODER_OUTPUT_TOOL_NAME, FIXER_ACCEPTED_TEXT, FIXER_OUTPUT_TOOL_NAME, validateAcceptedWorkerDetails, } from "./worker-output.js";
|
|
@@ -79,9 +80,15 @@ export function validateAcceptedDetails(toolName, details) {
|
|
|
79
80
|
[MERGER_OUTPUT_TOOL_NAME]: ["completed", "escalate"],
|
|
80
81
|
};
|
|
81
82
|
const collectorDiscriminator = toolName === COLLECTOR_OUTPUT_TOOL && Array.isArray(candidate?.groups);
|
|
82
|
-
const
|
|
83
|
+
const baseDiscriminator = typeof discriminator === "string" ? seatFallbackBaseStatus(discriminator) : discriminator;
|
|
84
|
+
// ADR 0071: `-by-fallback` is not independently lawful — require latch-shaped evidence.
|
|
85
|
+
const taintedWithoutEvidence = typeof discriminator === "string" &&
|
|
86
|
+
!seatFallbackStatusHasLawfulEvidence(discriminator, details);
|
|
87
|
+
const runtimeBindingMissing = (toolName === DOCTOR_OUTPUT_TOOL_NAME && baseDiscriminator === "completed" && !(candidate?.cost !== null && typeof candidate?.cost === "object")) ||
|
|
83
88
|
(toolName === REVIEWER_OUTPUT_TOOL_NAME && candidate?.version !== 2);
|
|
84
|
-
if (
|
|
89
|
+
if (taintedWithoutEvidence ||
|
|
90
|
+
runtimeBindingMissing ||
|
|
91
|
+
(!collectorDiscriminator && (typeof discriminator !== "string" || !lawfulStatuses[toolName].includes(baseDiscriminator)))) {
|
|
85
92
|
throw new AcceptedDetailsContractError("terminating receipt has no recognized execution discriminator");
|
|
86
93
|
}
|
|
87
94
|
try {
|
|
@@ -112,14 +119,15 @@ export function validateAcceptedLifecycle(toolName, argumentsValue, detailsValue
|
|
|
112
119
|
const details = validateAcceptedDetails(toolName, detailsValue);
|
|
113
120
|
if (toolName === DOCTOR_OUTPUT_TOOL_NAME) {
|
|
114
121
|
const testimony = validateDoctorSubmissionShape(argumentsValue);
|
|
115
|
-
if (testimony.status === "refused") {
|
|
122
|
+
if (seatFallbackBaseStatus(String(testimony.status)) === "refused") {
|
|
116
123
|
if (!deepEqual(testimony, details))
|
|
117
124
|
throw new Error("accepted tool lifecycle details mismatch");
|
|
118
125
|
return details;
|
|
119
126
|
}
|
|
120
127
|
const receipt = details;
|
|
121
|
-
if (receipt.status !== "completed")
|
|
128
|
+
if (seatFallbackBaseStatus(String(receipt.status)) !== "completed") {
|
|
122
129
|
throw new Error("accepted tool lifecycle details mismatch");
|
|
130
|
+
}
|
|
123
131
|
const { cost: _runtimeCost, ...projected } = receipt;
|
|
124
132
|
if (!deepEqual(testimony, projected))
|
|
125
133
|
throw new Error("accepted tool lifecycle details mismatch");
|
|
@@ -139,7 +147,8 @@ export function acceptedFacts(toolName, details) {
|
|
|
139
147
|
case JUDGE_OUTPUT_TOOL_NAME: return { status: details.judgeStatus };
|
|
140
148
|
case MERGER_OUTPUT_TOOL_NAME: {
|
|
141
149
|
const output = details;
|
|
142
|
-
|
|
150
|
+
const status = output.status;
|
|
151
|
+
return { status, ...(seatFallbackBaseStatus(status) === "completed" && typeof output.mergeCommitId === "string" ? { commit: output.mergeCommitId } : {}) };
|
|
143
152
|
}
|
|
144
153
|
case COLLECTOR_OUTPUT_TOOL:
|
|
145
154
|
return { status: "collected" };
|