@akagilnc/pi-workflow-roles 0.1.2397 → 0.1.2402

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.
Files changed (37) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/doctor-contracts.js +2 -9
  4. package/dist/engine-detour-tool.js +10 -35
  5. package/dist/evidence-child-executor.js +11 -3
  6. package/dist/merger-contracts.js +2 -8
  7. package/dist/navigator-attendance.js +1 -9
  8. package/dist/notary-contracts.js +3 -8
  9. package/dist/package-contracts/judge-output.js +1 -4
  10. package/dist/package-contracts/reviewer-output.js +1 -7
  11. package/dist/package-contracts/terminating-tools.js +5 -10
  12. package/dist/public-cli/main.js +24 -101
  13. package/package.json +1 -1
  14. package/scripts/build-package.mjs +1 -2
  15. package/src/analyst-metric-families/acceptance-success-rework.ts +3 -6
  16. package/src/collector-role.ts +1 -8
  17. package/src/doctor-contracts.ts +3 -18
  18. package/src/doctor-role.ts +1 -5
  19. package/src/engine-detour-tool.ts +16 -51
  20. package/src/evidence-child-executor.ts +11 -3
  21. package/src/factory-board.ts +2 -4
  22. package/src/judge-role.ts +1 -7
  23. package/src/merger-contracts.ts +3 -17
  24. package/src/merger-role.ts +1 -8
  25. package/src/navigator-attendance.ts +1 -14
  26. package/src/notary-contracts.ts +4 -24
  27. package/src/notary-role.ts +1 -3
  28. package/src/package-contracts/judge-output.ts +2 -17
  29. package/src/package-contracts/reviewer-output.ts +2 -21
  30. package/src/package-contracts/terminating-tools.ts +4 -13
  31. package/src/package-contracts/worker-output.ts +2 -10
  32. package/src/public-cli/settlement.ts +8 -20
  33. package/src/reviewer-role.ts +2 -10
  34. package/src/role-runtime.ts +1 -28
  35. package/src/worker-role.ts +2 -14
  36. package/dist/engine-labor-fallback.js +0 -136
  37. package/src/engine-labor-fallback.ts +0 -270
package/README.md CHANGED
@@ -53,7 +53,7 @@ Receipts are typed, so callers compose roles without parsing prose; ordering and
53
53
 
54
54
  On completing-side submissions the package may spawn the Menxia province before the run settles: `gatekeeper` reads the subject and dispatches an officer (`inspector` or `notary`); existing auditor hooks stay where already wired. The gate runs inside the submission session; bounce means rewrite-and-resubmit in that same session — not role failure; the final receipt is the post-gate product. `planned` / `refused` / `unfinished` skip the province. Pointers only: [ADR 0067](docs/adr/0067-menxia-province-founding-jishizhong-fubaolang.md), [ADR 0072](docs/adr/0072-menxia-pre-pr-submission-hooks.md). Gate history is projected into the typed TerminalResult: an optional menxia section lists the seats that actually sat, each dispatch round (officer, optional reason verbatim), and each officer report (seat, status, findings). Absent when no gate ran. Do not scrape session prose for gate status — read the typed section.
55
55
 
56
- When a labor-engine detour fails and the seat continues the labor on the main road, the typed receipt may carry a mechanical `engineLaborFallback` field: `{ engine, failure, laborBy: "seat" }`. It appears only after a real detour failure that fell back to seat labor—not on detour success or caller cancel. First failure wins for the activation; model-forged `engineLaborFallback` keys are stripped unless the package latch recorded one. Sole producer: `src/engine-labor-fallback.ts`; decision record: [ADR 0071](docs/adr/0071-engine-detour-failure-seat-fallback-declaration.md). This README only projects that contract.
56
+ When a labor-engine detour process fails to spawn, exits nonzero, or produces no usable output, the role run stops through the existing infrastructure-failure path with the original cause visible. The seat does not continue the labor or produce a typed Receipt. Caller cancellation continues to propagate unchanged. See [ADR 0071](docs/adr/0071-engine-detour-failure-seat-fallback-declaration.md).
57
57
 
58
58
  ## Call the roles
59
59
 
package/README.zh-CN.md CHANGED
@@ -53,7 +53,7 @@ ak-role config set-auto-resume-limit 3
53
53
 
54
54
  完成侧交卷时,包可能在本局结算前起门下省:`gatekeeper` 读受审物并派官(`inspector` 给事中或 `notary` 符宝郎);既有审刑院挂钩仍在原位。闸在交卷 session 内运行;封驳=当场重写重交,不是角色失败;最终回执即过闸产物。`planned`/`refused`/`unfinished` 不调省。指针:[ADR 0067](docs/adr/0067-menxia-province-founding-jishizhong-fubaolang.md)、[ADR 0072](docs/adr/0072-menxia-pre-pr-submission-hooks.md)。闸史已投影进 typed 回执:可选 menxia 段列出实际在场席位、每轮派官(officer 与逐字 reason)与各官报告(席位/判决/findings);无闸调用时该段缺席。勿刮 session 散文当闸状态——读 typed 段。
55
55
 
56
- 当劳务引擎绕行失败、座席回到主路继续劳务时,typed 回执可带机械字段 `engineLaborFallback`:`{ engine, failure, laborBy: "seat" }`。仅在真实绕行失败并座席顶班后出现——成功绕行或调用方 cancel 不出现。同一次 activation 内先到先得;无包内 latch 时剥离模型伪造的 `engineLaborFallback` 键。唯一构造点:`src/engine-labor-fallback.ts`;决策记录:[ADR 0071](docs/adr/0071-engine-detour-failure-seat-fallback-declaration.md)。本文只投影该契约。
56
+ 劳务引擎绕行进程无法启动、非零退出或未产生可用输出时,角色运行沿既有基础设施失败路径立即停止并保留可见真因;座席不继续顶班,也不产出 typed 回执。调用方 cancel 仍原样传播。见 [ADR 0071](docs/adr/0071-engine-detour-failure-seat-fallback-declaration.md)
57
57
 
58
58
  ## 调用百官
59
59
 
@@ -1,6 +1,5 @@
1
1
  import { Type } from "typebox";
2
2
  import { canonicalJson } from "./canonical-json.js";
3
- import { seatFallbackBaseStatus, seatFallbackStatusHasLawfulEvidence, } from "./engine-labor-fallback.js";
4
3
  import { openToolObjectFromUnion } from "./open-tool-schema.js";
5
4
  export const DOCTOR_EVIDENCE_TOOL_NAME = "ak_doctor_evidence";
6
5
  export const DOCTOR_OUTPUT_TOOL_NAME = "ak_doctor_output";
@@ -67,20 +66,14 @@ catch {
67
66
  } }
68
67
  export function validateDoctorSubmissionShape(value) {
69
68
  const status = read(value, "status");
70
- const base = typeof status === "string" ? seatFallbackBaseStatus(status) : status;
71
- if (base !== "completed" && base !== "refused")
69
+ if (status !== "completed" && status !== "refused")
72
70
  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
- }
77
71
  return value;
78
72
  }
79
73
  export function validateRecordedDoctorOutput(value) {
80
74
  const output = validateDoctorSubmissionShape(value);
81
75
  const status = read(output, "status");
82
- const base = typeof status === "string" ? seatFallbackBaseStatus(status) : status;
83
- if (base === "completed" && read(output, "cost") === undefined)
76
+ if (status === "completed" && read(output, "cost") === undefined)
84
77
  throw new Error("Completed Doctor receipt has no runtime-owned cost testimony");
85
78
  return output;
86
79
  }
@@ -1,28 +1,12 @@
1
1
  import { Type } from "typebox";
2
2
  import { ENGINE_DETOUR_ALREADY_USED_DIAGNOSTIC, ENGINE_DETOUR_TOOL_NAME, engineDetourFailureDiagnostic, engineNameFromEnv, isEngineDetourFailure, runEngineDetourOnce, } from "./engine-detour.js";
3
- import { activationEngineLaborFallbackLatch, recordEngineLaborFallback, } from "./engine-labor-fallback.js";
4
3
  const engineDetourArgsSchema = Type.Object({
5
4
  argv: Type.Array(Type.String({ minLength: 1 }), {
6
5
  minItems: 1,
7
6
  description: "Executable argv for one engine subprocess. First element is the command (PATH lookup); remaining elements are arguments. Build argv from the host CLI actual interface for the configured engine name; when optional packaged notes are present in the session prompt, follow those bytes. Do not invent package flags.",
8
7
  }),
9
8
  }, { additionalProperties: false });
10
- function seatFallbackToolResult(field, failure) {
11
- return {
12
- content: [
13
- {
14
- type: "text",
15
- text: `Engine detour failed: ${failure}. Perform the labor in this session (seat main road) and submit via the existing typed path. A mechanical fallback declaration will attach to the typed receipt.`,
16
- },
17
- ],
18
- details: {
19
- tool: ENGINE_DETOUR_TOOL_NAME,
20
- detourFailed: true,
21
- ...field,
22
- },
23
- };
24
- }
25
- /** Caller/upper-layer cancel must propagate; process failure is seat-fallback, not cancel. */
9
+ /** Caller/upper-layer cancellation must propagate unchanged. */
26
10
  function isCallerCancellation(error, signal) {
27
11
  if (signal?.aborted === true)
28
12
  return true;
@@ -37,8 +21,8 @@ function isCallerCancellation(error, signal) {
37
21
  * Build one once-latch detour tool definition for a configured engine name.
38
22
  * `latch` is shared so parent registration can reset between activations.
39
23
  * `fail` owns host abort (parent) vs throw (evidence child) for tool misuse only.
40
- * Engine process failure (nonzero/empty/spawn) soft-returns seat fallback (#380).
41
- * Caller AbortSignal cancel propagates without writing fallback.
24
+ * Engine process failures (nonzero/empty/spawn) stop via `fail` with their cause.
25
+ * Caller AbortSignal cancellation propagates unchanged.
42
26
  */
43
27
  export function createEngineDetourToolDefinition(input) {
44
28
  const latch = input.latch ?? { used: false };
@@ -52,7 +36,7 @@ export function createEngineDetourToolDefinition(input) {
52
36
  `Use ${ENGINE_DETOUR_TOOL_NAME} exactly once for the configured engine (${engineName}). Optional packaged notes are guidance when present; a bare engine name alone is also a valid call path.`,
53
37
  "Pass argv for the host CLI of this engine name — first element is the executable name on PATH. Follow optional packaged notes when delivered; otherwise act from the engine name and the host CLI actual interface. Do not invent package flags.",
54
38
  "On success, use the returned stdout as labor content for the existing typed submission / report path.",
55
- "On engine failure the tool returns a soft failure: continue labor in this session and submit via the existing typed path. Do not treat engine failure as a reason to withhold the typed receipt.",
39
+ "An engine process failure stops this activation; do not continue labor in this session.",
56
40
  ],
57
41
  parameters: engineDetourArgsSchema,
58
42
  async execute(toolCallId, params, signal, _onUpdate, ctx) {
@@ -65,15 +49,6 @@ export function createEngineDetourToolDefinition(input) {
65
49
  if (argv.length === 0 || argv.some((part) => typeof part !== "string" || part.length === 0)) {
66
50
  input.fail(new Error("engine detour argv must be a non-empty string array"), toolCallId, ctx);
67
51
  }
68
- const softFail = (failure) => {
69
- // Activation-scoped latch is the sole shared recorder (parent seat + legs).
70
- const fallbackLatch = activationEngineLaborFallbackLatch() ?? { field: undefined };
71
- const field = recordEngineLaborFallback(fallbackLatch, {
72
- engine: engineName,
73
- failure,
74
- });
75
- return seatFallbackToolResult(field, failure);
76
- };
77
52
  let result;
78
53
  try {
79
54
  result = await runEngineDetourOnce({
@@ -83,15 +58,15 @@ export function createEngineDetourToolDefinition(input) {
83
58
  });
84
59
  }
85
60
  catch (error) {
86
- // Caller cancel: propagate. Spawn/engine failure: seat fallback.
87
- if (isCallerCancellation(error, signal)) {
61
+ if (isCallerCancellation(error, signal))
88
62
  throw error;
89
- }
90
- const failure = error instanceof Error ? error.message : String(error);
91
- return softFail(failure.trim() === "" ? "engine detour spawn failed" : failure);
63
+ const cause = error instanceof Error
64
+ ? error
65
+ : new Error(String(error).trim() || "engine detour spawn failed");
66
+ input.fail(cause, toolCallId, ctx);
92
67
  }
93
68
  if (isEngineDetourFailure(result)) {
94
- return softFail(engineDetourFailureDiagnostic(result));
69
+ input.fail(new Error(engineDetourFailureDiagnostic(result)), toolCallId, ctx);
95
70
  }
96
71
  return {
97
72
  content: [{ type: "text", text: result.stdout }],
@@ -475,14 +475,16 @@ export async function executeEvidenceChild(workspace, prompt, context, options =
475
475
  engine: engineName,
476
476
  packageRoot: options.packageRoot,
477
477
  });
478
- // #378/#380: same detour tool as parent seat. Engine process failure soft-returns
479
- // seat main-road fallback (ADR 0069 detour-rejoins-main-road); tool misuse still throws.
478
+ // Evidence legs use the parent detour tool; retain any engine process cause
479
+ // so the enclosing child boundary, rather than a tool-error result, terminates.
480
+ let engineDetourFailure;
480
481
  const engineDetourTool = engineName === undefined
481
482
  ? undefined
482
483
  : createEngineDetourToolDefinition({
483
484
  engineName,
484
485
  fail(error) {
485
- throw error instanceof Error ? error : new Error(String(error));
486
+ engineDetourFailure ??= error instanceof Error ? error : new Error(String(error));
487
+ throw engineDetourFailure;
486
488
  },
487
489
  });
488
490
  // No tools allowlist — Pi defaults + unrestricted evidence surface (ADR 0064).
@@ -521,8 +523,14 @@ export async function executeEvidenceChild(workspace, prompt, context, options =
521
523
  await session.prompt(delivered);
522
524
  }
523
525
  catch (error) {
526
+ if (engineDetourFailure !== undefined) {
527
+ throw classifiedError(engineDetourFailure, "child");
528
+ }
524
529
  throw classifiedError(error, "provider");
525
530
  }
531
+ if (engineDetourFailure !== undefined) {
532
+ throw classifiedError(engineDetourFailure, "child");
533
+ }
526
534
  if (signal?.aborted)
527
535
  throw new Error("Evidence child was cancelled");
528
536
  const lastAssistant = [...session.messages]
@@ -1,7 +1,6 @@
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";
5
4
  import { sha256Hex } from "./sha256.js";
6
5
  import { openToolObjectFromUnion } from "./open-tool-schema.js";
7
6
  const oidPattern = "^(?:[0-9a-f]{40}|[0-9a-f]{64})$";
@@ -70,14 +69,9 @@ export function validateMergerOutput(value, expectedAttemptId) {
70
69
  if (!record(value) || (expectedAttemptId !== undefined && value.attemptId !== expectedAttemptId))
71
70
  throw new Error("Merger output attempt mismatch");
72
71
  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
+ if (status === "completed" && isFullGitObjectId(value.mergeCommitId))
79
73
  return structuredClone(value);
80
- if (statusBase === "escalate")
74
+ if (status === "escalate")
81
75
  return structuredClone(value);
82
76
  throw new Error("Merger output has no recognized execution discriminator");
83
77
  }
@@ -6,7 +6,6 @@ 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";
10
9
  import {
11
10
  NAVIGATOR_INVOCATION_ENTRY,
12
11
  mintNavigatorInvocationId
@@ -258,13 +257,6 @@ function createNavigatorPrepareTool(onOutput) {
258
257
  }
259
258
  };
260
259
  }
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
- }
268
260
  function selectNavigatorCandidate(candidates, settlement) {
269
261
  if (settlement.kind !== "accepted") return void 0;
270
262
  const usable = candidates.filter((candidate) => candidate.next !== void 0);
@@ -275,7 +267,7 @@ function selectNavigatorCandidate(candidates, settlement) {
275
267
  if (rolePhaseMatched.length > 0) {
276
268
  if (settlement.status !== void 0) {
277
269
  const statusSpecific = rolePhaseMatched.find(
278
- (candidate) => candidate.matches?.statuses !== void 0 && statusListMatchesSettlement(candidate.matches.statuses, settlement.status)
270
+ (candidate) => candidate.matches?.statuses !== void 0 && candidate.matches.statuses.includes(settlement.status)
279
271
  );
280
272
  if (statusSpecific !== void 0) {
281
273
  return { candidate: statusSpecific, matchedToSettlement: true };
@@ -4,7 +4,6 @@
4
4
  * No usable result is infrastructure failure via public settlement, not a judgment status (#475).
5
5
  */
6
6
  import { Type } from "typebox";
7
- import { readActivationEngineLaborFallbackField, seatFallbackBaseStatus, seatFallbackStatusHasLawfulEvidence, withEngineLaborFallbackField, } from "./engine-labor-fallback.js";
8
7
  import { openToolObject } from "./open-tool-schema.js";
9
8
  export const NOTARY_OUTPUT_TOOL_NAME = "ak_notary_output";
10
9
  export const NOTARY_ACCEPTED_TEXT = "Notary output accepted";
@@ -42,10 +41,10 @@ export function validateNotaryOutput(value) {
42
41
  throw new Error("Notary output has no recognized execution discriminator");
43
42
  }
44
43
  const statusRaw = typeof value.status === "string" ? value.status : undefined;
45
- if (statusRaw === undefined || !seatFallbackStatusHasLawfulEvidence(statusRaw, value)) {
44
+ if (statusRaw === undefined) {
46
45
  throw new Error("Notary output has no recognized execution discriminator");
47
46
  }
48
- const status = seatFallbackBaseStatus(statusRaw);
47
+ const status = statusRaw;
49
48
  if (status === "bounce") {
50
49
  const clone = structuredClone(value);
51
50
  if (clone.disposition === undefined)
@@ -62,15 +61,11 @@ export function validateNotaryOutput(value) {
62
61
  }
63
62
  throw new Error("Notary output has no recognized execution discriminator");
64
63
  }
65
- /** Shape check for recorded accepted details (may include engineLaborFallback). */
66
64
  export function validateRecordedNotaryOutput(value) {
67
65
  return validateNotaryOutput(value);
68
66
  }
69
- export function withNotaryAcceptedDetails(output) {
70
- return withEngineLaborFallbackField(output, readActivationEngineLaborFallbackField());
71
- }
72
67
  export function notaryDecisiveFacts(output) {
73
- const status = seatFallbackBaseStatus(String(output.status));
68
+ const status = String(output.status);
74
69
  const facts = { status, officer: "notary" };
75
70
  if (status === "pass" || status === "bounce") {
76
71
  const findings = output.findings;
@@ -1,5 +1,4 @@
1
1
  /** Package-owned Judge output leaf — no role registration surface. */
2
- import { seatFallbackBaseStatus, seatFallbackStatusHasLawfulEvidence, } from "../engine-labor-fallback.js";
3
2
  export const JUDGE_OUTPUT_TOOL_NAME = "ak_judge_output";
4
3
  export const JUDGE_ACCEPTED_TEXT = "Judge verdict accepted";
5
4
  export function validateAcceptedJudgeDetails(verdict) {
@@ -15,9 +14,7 @@ export function validateAcceptedJudgeDetails(verdict) {
15
14
  if (typeof judgeStatus !== "string") {
16
15
  throw new Error("Judge verdict has no execution discriminator");
17
16
  }
18
- const base = seatFallbackBaseStatus(judgeStatus);
19
- if (["converged", "continue", "escalate"].includes(base) &&
20
- seatFallbackStatusHasLawfulEvidence(judgeStatus, verdict)) {
17
+ if (["converged", "continue", "escalate"].includes(judgeStatus)) {
21
18
  return verdict;
22
19
  }
23
20
  throw new Error("Judge verdict has no execution discriminator");
@@ -1,5 +1,4 @@
1
1
  /** Package-owned Reviewer intent and runtime-receipt leaves — no role registration surface. */
2
- import { seatFallbackBaseStatus, seatFallbackStatusHasLawfulEvidence, } from "../engine-labor-fallback.js";
3
2
  export const REVIEWER_OUTPUT_TOOL_NAME = "ak_reviewer_output";
4
3
  export const REVIEWER_ACCEPTED_TEXT = "Reviewer report accepted";
5
4
  function isRecord(value) {
@@ -46,10 +45,6 @@ export function validateReviewerIntent(output) {
46
45
  /** Validate runtime-owned facts at their real identity seams (target pins + plain text). */
47
46
  export function validateRuntimeReviewerReceipt(output) {
48
47
  const status = read(output, "status");
49
- // ADR 0071: tainted top-level status requires latch-shaped engineLaborFallback evidence.
50
- if (typeof status === "string" && !seatFallbackStatusHasLawfulEvidence(status, output)) {
51
- throw new Error("Reviewer receipt has no recognized execution discriminator");
52
- }
53
48
  const acceptedBatch = read(output, "acceptedBatch");
54
49
  const identities = read(output, "identities");
55
50
  const construction = read(identities, "construction");
@@ -118,8 +113,7 @@ export function projectReviewerIntentToReceipt(intentValue, receiptValue) {
118
113
  const intent = validateReviewerIntent(intentValue);
119
114
  const receipt = validateRuntimeReviewerReceipt(receiptValue);
120
115
  const receiptStatus = String(receipt.status);
121
- const receiptBase = seatFallbackBaseStatus(receiptStatus);
122
- if (receiptBase !== intent.status || (intent.status === "completed" ? receipt.diagnostic !== undefined : receipt.diagnostic !== intent.diagnostic)) {
116
+ if (receiptStatus !== intent.status || (intent.status === "completed" ? receipt.diagnostic !== undefined : receipt.diagnostic !== intent.diagnostic)) {
123
117
  throw new Error("Reviewer intent and runtime receipt disagree");
124
118
  }
125
119
  return receipt;
@@ -6,7 +6,6 @@ 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";
10
9
  import { DOCTOR_OUTPUT_TOOL_NAME, validateDoctorSubmissionShape, validateRecordedDoctorOutput } from "../doctor-contracts.js";
11
10
  import { MERGER_ACCEPTED_TEXT, MERGER_OUTPUT_TOOL_NAME, validateMergerOutput } from "../merger-contracts.js";
12
11
  import { NOTARY_ACCEPTED_TEXT, NOTARY_OUTPUT_TOOL_NAME, validateRecordedNotaryOutput } from "../notary-contracts.js";
@@ -85,14 +84,10 @@ export function validateAcceptedDetails(toolName, details) {
85
84
  [NOTARY_OUTPUT_TOOL_NAME]: ["pass", "bounce"],
86
85
  };
87
86
  const collectorDiscriminator = toolName === COLLECTOR_OUTPUT_TOOL && Array.isArray(candidate?.groups);
88
- const baseDiscriminator = typeof discriminator === "string" ? seatFallbackBaseStatus(discriminator) : discriminator;
89
- // ADR 0071: `-by-fallback` is not independently lawful — require latch-shaped evidence.
90
- const taintedWithoutEvidence = typeof discriminator === "string" &&
91
- !seatFallbackStatusHasLawfulEvidence(discriminator, details);
87
+ const baseDiscriminator = discriminator;
92
88
  const runtimeBindingMissing = (toolName === DOCTOR_OUTPUT_TOOL_NAME && baseDiscriminator === "completed" && !(candidate?.cost !== null && typeof candidate?.cost === "object")) ||
93
89
  (toolName === REVIEWER_OUTPUT_TOOL_NAME && candidate?.version !== 2);
94
- if (taintedWithoutEvidence ||
95
- runtimeBindingMissing ||
90
+ if (runtimeBindingMissing ||
96
91
  (!collectorDiscriminator && (typeof discriminator !== "string" || !lawfulStatuses[toolName].includes(baseDiscriminator)))) {
97
92
  throw new AcceptedDetailsContractError("terminating receipt has no recognized execution discriminator");
98
93
  }
@@ -126,13 +121,13 @@ export function validateAcceptedLifecycle(toolName, argumentsValue, detailsValue
126
121
  const details = validateAcceptedDetails(toolName, detailsValue);
127
122
  if (toolName === DOCTOR_OUTPUT_TOOL_NAME) {
128
123
  const testimony = validateDoctorSubmissionShape(argumentsValue);
129
- if (seatFallbackBaseStatus(String(testimony.status)) === "refused") {
124
+ if ((String(testimony.status)) === "refused") {
130
125
  if (!deepEqual(testimony, details))
131
126
  throw new Error("accepted tool lifecycle details mismatch");
132
127
  return details;
133
128
  }
134
129
  const receipt = details;
135
- if (seatFallbackBaseStatus(String(receipt.status)) !== "completed") {
130
+ if ((String(receipt.status)) !== "completed") {
136
131
  throw new Error("accepted tool lifecycle details mismatch");
137
132
  }
138
133
  const { cost: _runtimeCost, ...projected } = receipt;
@@ -156,7 +151,7 @@ export function acceptedFacts(toolName, details) {
156
151
  case MERGER_OUTPUT_TOOL_NAME: {
157
152
  const output = details;
158
153
  const status = output.status;
159
- return { status, ...(seatFallbackBaseStatus(status) === "completed" && typeof output.mergeCommitId === "string" ? { commit: output.mergeCommitId } : {}) };
154
+ return { status, ...(status === "completed" && typeof output.mergeCommitId === "string" ? { commit: output.mergeCommitId } : {}) };
160
155
  }
161
156
  case COLLECTOR_OUTPUT_TOOL:
162
157
  return { status: "collected" };