@akagilnc/pi-workflow-roles 0.1.2135 → 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.
Files changed (39) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/compliance-transport.js +19 -24
  4. package/dist/doctor-contracts.js +10 -2
  5. package/dist/engine-detour-tool.js +5 -15
  6. package/dist/engine-detour.js +3 -11
  7. package/dist/engine-labor-fallback.js +129 -114
  8. package/dist/evidence-child-executor.js +3 -4
  9. package/dist/merger-contracts.js +9 -2
  10. package/dist/navigator-attendance.js +11 -4
  11. package/dist/package-contracts/judge-output.js +8 -1
  12. package/dist/package-contracts/reviewer-output.js +9 -1
  13. package/dist/package-contracts/terminating-tools.js +14 -5
  14. package/dist/public-cli/main.js +123 -125
  15. package/package.json +1 -1
  16. package/resources/engines/codex.md +3 -4
  17. package/resources/engines/grok-4.6.md +5 -6
  18. package/resources/engines/kimi.md +2 -2
  19. package/resources/engines/opus.md +2 -2
  20. package/scripts/build-package.mjs +2 -0
  21. package/src/collector-role.ts +5 -33
  22. package/src/compliance-transport.ts +19 -24
  23. package/src/doctor-contracts.ts +19 -3
  24. package/src/engine-detour-tool.ts +6 -21
  25. package/src/engine-detour.ts +3 -15
  26. package/src/engine-labor-fallback.ts +100 -2
  27. package/src/evidence-child-executor.ts +3 -4
  28. package/src/factory-board.ts +6 -2
  29. package/src/merger-contracts.ts +18 -3
  30. package/src/navigator-attendance.ts +18 -4
  31. package/src/package-contracts/judge-output.ts +25 -2
  32. package/src/package-contracts/reviewer-output.ts +23 -2
  33. package/src/package-contracts/terminating-tools.ts +22 -6
  34. package/src/package-contracts/worker-output.ts +10 -2
  35. package/src/public-cli/settlement.ts +51 -17
  36. package/src/role-runtime.ts +8 -4
  37. package/src/taishi-metric-families/acceptance-success-rework.ts +6 -3
  38. package/dist/package-owned-tool-idle.js +0 -223
  39. package/src/package-owned-tool-idle.ts +0 -257
package/README.md CHANGED
@@ -40,7 +40,7 @@ ak-role config unset-engine judge
40
40
 
41
41
  Receipts are typed, so callers compose roles without parsing prose; ordering and stopping stay caller-owned. Programmatic consumers derive contracts from the exported schemas in `src/package-contracts/`, not from this guide.
42
42
 
43
- 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 (including package-owned idle timeout on the detour tool)—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.
43
+ 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.
44
44
 
45
45
  ## Call the roles
46
46
 
package/README.zh-CN.md CHANGED
@@ -40,7 +40,7 @@ ak-role config unset-engine judge
40
40
 
41
41
  回执是 typed 的,调用者不必解析散文即可组合角色;顺序与停止归调用者。编程消费者从 `src/package-contracts/` 导出推导契约,不从本文。
42
42
 
43
- 当劳务引擎绕行失败、座席回到主路继续劳务时,typed 回执可带机械字段 `engineLaborFallback`:`{ engine, failure, laborBy: "seat" }`。仅在真实绕行失败并座席顶班后出现(含 detour 工具命中 package-owned idle 超时)——成功绕行或调用方 cancel 不出现。同一次 activation 内先到先得;无包内 latch 时剥离模型伪造的 `engineLaborFallback` 键。唯一构造点:`src/engine-labor-fallback.ts`;决策记录:[ADR 0071](docs/adr/0071-engine-detour-failure-seat-fallback-declaration.md)。本文只投影该契约。
43
+ 当劳务引擎绕行失败、座席回到主路继续劳务时,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)。本文只投影该契约。
44
44
 
45
45
  ## 调用百官
46
46
 
@@ -1,7 +1,6 @@
1
1
  import { Type } from "typebox";
2
2
  import { executeAuditorChild, } from "./evidence-child-executor.js";
3
3
  import { createAuditorDossierTool } from "./auditor-dossier-tool.js";
4
- import { withPackageOwnedToolIdleSuspended } from "./package-owned-tool-idle.js";
5
4
  /** Zero-projection kickoff — soul already carries dossier-fetch duty; no hand-delivered materials. */
6
5
  export const AUDITOR_DOSSIER_PROMPT = "Audit the current run dossier.";
7
6
  const nonblank = Type.String({ minLength: 1, pattern: "\\S" });
@@ -62,28 +61,24 @@ export function readComplianceCandidate(arguments_, usage) {
62
61
  return { status: "audit-incomplete", observation: { kind: "object-status-unreadable", status: status === undefined ? "missing" : "unknown" }, candidate: arguments_, ...(usage === undefined ? {} : { usage }) };
63
62
  }
64
63
  export async function runComplianceAudit(options) {
65
- // #339: only the real compliance-audit await leaves the outer package-owned
66
- // idle owner. Pre/post-audit work stays under the single outer backstop.
67
- return withPackageOwnedToolIdleSuspended(async () => {
68
- const prompt = options.serializedInput ?? AUDITOR_DOSSIER_PROMPT;
69
- const receipt = await executeAuditorChild({
70
- tool: options.tool,
71
- dossierTool: createAuditorDossierTool(options.runDirectory),
72
- systemPrompt: options.systemPrompt,
73
- prompt,
74
- roleLabel: options.roleLabel,
75
- context: options.context,
76
- retainResponse: (response) => retainComplianceResponse(options.context, response),
77
- ...(options.runCompletion === undefined ? {} : { runCompletion: options.runCompletion }),
78
- ...(options.signal === undefined ? {} : { signal: options.signal }),
79
- });
80
- if (receipt.noReceiptLifecycle !== undefined) {
81
- return {
82
- status: "no-receipt",
83
- ...receipt.noReceiptLifecycle,
84
- ...(receipt.response.usage === undefined ? {} : { usage: receipt.response.usage }),
85
- };
86
- }
87
- return readComplianceCandidate(receipt.decision, receipt.response.usage);
64
+ const prompt = options.serializedInput ?? AUDITOR_DOSSIER_PROMPT;
65
+ const receipt = await executeAuditorChild({
66
+ tool: options.tool,
67
+ dossierTool: createAuditorDossierTool(options.runDirectory),
68
+ systemPrompt: options.systemPrompt,
69
+ prompt,
70
+ roleLabel: options.roleLabel,
71
+ context: options.context,
72
+ retainResponse: (response) => retainComplianceResponse(options.context, response),
73
+ ...(options.runCompletion === undefined ? {} : { runCompletion: options.runCompletion }),
74
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
88
75
  });
76
+ if (receipt.noReceiptLifecycle !== undefined) {
77
+ return {
78
+ status: "no-receipt",
79
+ ...receipt.noReceiptLifecycle,
80
+ ...(receipt.response.usage === undefined ? {} : { usage: receipt.response.usage }),
81
+ };
82
+ }
83
+ return readComplianceCandidate(receipt.decision, receipt.response.usage);
89
84
  }
@@ -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
- if (status !== "completed" && status !== "refused")
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
- if (read(output, "status") === "completed" && read(output, "cost") === undefined)
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,7 +1,6 @@
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
3
  import { activationEngineLaborFallbackLatch, recordEngineLaborFallback, } from "./engine-labor-fallback.js";
4
- import { isPackageOwnedToolIdleTimeoutError, pokePackageOwnedToolIdle, wrapPackageOwnedToolDefinition, } from "./package-owned-tool-idle.js";
5
4
  const engineDetourArgsSchema = Type.Object({
6
5
  argv: Type.Array(Type.String({ minLength: 1 }), {
7
6
  minItems: 1,
@@ -23,14 +22,8 @@ function seatFallbackToolResult(field, failure) {
23
22
  },
24
23
  };
25
24
  }
26
- /** Caller/upper-layer cancel must propagate; idle backstop is seat-fallback, not cancel. */
25
+ /** Caller/upper-layer cancel must propagate; process failure is seat-fallback, not cancel. */
27
26
  function isCallerCancellation(error, signal) {
28
- if (isPackageOwnedToolIdleTimeoutError(error))
29
- return false;
30
- if (signal !== undefined &&
31
- isPackageOwnedToolIdleTimeoutError(signal.reason)) {
32
- return false;
33
- }
34
27
  if (signal?.aborted === true)
35
28
  return true;
36
29
  if (typeof error === "object" &&
@@ -44,13 +37,13 @@ function isCallerCancellation(error, signal) {
44
37
  * Build one once-latch detour tool definition for a configured engine name.
45
38
  * `latch` is shared so parent registration can reset between activations.
46
39
  * `fail` owns host abort (parent) vs throw (evidence child) for tool misuse only.
47
- * Engine process failure (nonzero/empty/spawn/idle-timeout) soft-returns seat fallback (#380).
40
+ * Engine process failure (nonzero/empty/spawn) soft-returns seat fallback (#380).
48
41
  * Caller AbortSignal cancel propagates without writing fallback.
49
42
  */
50
43
  export function createEngineDetourToolDefinition(input) {
51
44
  const latch = input.latch ?? { used: false };
52
45
  const engineName = input.engineName;
53
- return wrapPackageOwnedToolDefinition({
46
+ return {
54
47
  name: ENGINE_DETOUR_TOOL_NAME,
55
48
  label: "Engine Detour",
56
49
  description: `Run one labor-engine subprocess (engine=${engineName}) and return its stdout to this session. Call at most once per activation. Build argv from the host CLI actual interface for this engine name; when optional packaged notes are present in the session prompt, follow those bytes too.`,
@@ -83,17 +76,14 @@ export function createEngineDetourToolDefinition(input) {
83
76
  };
84
77
  let result;
85
78
  try {
86
- // Byte activity on stdout/stderr touches the outer package-owned idle clock
87
- // (183s silence law unchanged). True hangs still die; slow streaming engines live.
88
79
  result = await runEngineDetourOnce({
89
80
  argv,
90
81
  cwd: ctx.cwd,
91
82
  ...(signal === undefined ? {} : { signal }),
92
- onOutputActivity: pokePackageOwnedToolIdle,
93
83
  });
94
84
  }
95
85
  catch (error) {
96
- // Caller cancel: propagate. Idle backstop + spawn/engine failure: seat fallback.
86
+ // Caller cancel: propagate. Spawn/engine failure: seat fallback.
97
87
  if (isCallerCancellation(error, signal)) {
98
88
  throw error;
99
89
  }
@@ -111,7 +101,7 @@ export function createEngineDetourToolDefinition(input) {
111
101
  },
112
102
  };
113
103
  },
114
- });
104
+ };
115
105
  }
116
106
  /**
117
107
  * Register the engine-generic detour tool once for this process when any role has
@@ -48,8 +48,8 @@ export async function runEngineDetourOnce(input) {
48
48
  return await new Promise((resolve, reject) => {
49
49
  let settled = false;
50
50
  const signal = input.signal;
51
- // Own abort→kill explicitly so rejection preserves signal.reason (caller cancel
52
- // vs package-owned idle). Do not pass `signal` to spawn (Node replaces reason).
51
+ // Own abort→kill explicitly so rejection preserves signal.reason (caller cancel).
52
+ // Do not pass `signal` to spawn (Node replaces reason).
53
53
  const child = spawn(command, args, {
54
54
  cwd: input.cwd,
55
55
  env: input.env ?? process.env,
@@ -57,18 +57,11 @@ export async function runEngineDetourOnce(input) {
57
57
  });
58
58
  let stdout = "";
59
59
  let stderr = "";
60
- const noteActivity = (chunk) => {
61
- if (chunk.length === 0)
62
- return;
63
- input.onOutputActivity?.();
64
- };
65
60
  child.stdout.setEncoding("utf8").on("data", (chunk) => {
66
61
  stdout += chunk;
67
- noteActivity(chunk);
68
62
  });
69
63
  child.stderr.setEncoding("utf8").on("data", (chunk) => {
70
64
  stderr += chunk;
71
- noteActivity(chunk);
72
65
  });
73
66
  const fail = (error) => {
74
67
  if (settled)
@@ -89,8 +82,7 @@ export async function runEngineDetourOnce(input) {
89
82
  resolve(result);
90
83
  };
91
84
  const onAbort = () => {
92
- // Fail synchronously so cooperative idle/cancel paths can soft-settle
93
- // before the outer package-owned idle hard-reject drain.
85
+ // Fail synchronously so caller-cancel soft-settle preserves signal.reason.
94
86
  fail(signal !== undefined ? abortReasonError(signal) : new Error("aborted"));
95
87
  try {
96
88
  child.kill("SIGTERM");
@@ -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
- /** Sole construction site for typed receipt field `engineLaborFallback` (#380 S1). */
9
- export function buildEngineLaborFallbackField(input) {
10
- return Object.freeze({
11
- engineLaborFallback: Object.freeze({
12
- engine: input.engine,
13
- failure: input.failure,
14
- laborBy: "seat",
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
- export function createEngineLaborFallbackLatch() {
19
- return { field: undefined };
20
- }
21
- /**
22
- * Record first detour failure for this latch (first wins; parallel legs share one field).
23
- * Always returns the latched first-wins value so tool details and receipt projection match.
24
- */
25
- export function recordEngineLaborFallback(latch, input) {
26
- const field = buildEngineLaborFallbackField(input);
27
- if (latch.field === undefined)
28
- latch.field = field;
29
- return latch.field;
30
- }
31
- export function readEngineLaborFallbackField(latch) {
32
- return latch?.field;
33
- }
34
- /**
35
- * Merge sole-built field into typed receipt details.
36
- * Spread only — must not construct the field key again (S1).
37
- * Without a mechanical latch, strip any model-injected reserved key (no forged declaration).
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
- raw = source.engineLaborFallback;
76
- }
77
- catch {
78
- return undefined;
33
+ value = receipt[key];
34
+ } catch {
35
+ continue;
79
36
  }
80
- if (typeof raw !== "object" || raw === null || Array.isArray(raw))
81
- return undefined;
82
- const rec = raw;
83
- if (typeof rec.engine !== "string" ||
84
- typeof rec.failure !== "string" ||
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
- return buildEngineLaborFallbackField({
89
- engine: rec.engine,
90
- failure: rec.failure,
91
- });
43
+ next[key] = tainted;
44
+ }
45
+ return next === void 0 ? receipt : next;
92
46
  }
93
- /**
94
- * Restore activation latch from durable session tool results (#380 resume).
95
- * Scans existing same-session detour tool results only — no sidecar / new entry type.
96
- * Replays through recordEngineLaborFallback so first-wins + sole producer stay intact.
97
- */
98
- export function restoreEngineLaborFallbackFromSessionEntries(latch, entries, toolName) {
99
- for (const entry of entries) {
100
- if (typeof entry !== "object" || entry === null)
101
- continue;
102
- const row = entry;
103
- if (row.type !== "message")
104
- continue;
105
- const message = row.message;
106
- if (typeof message !== "object" || message === null)
107
- continue;
108
- const msg = message;
109
- if (msg.role !== "toolResult")
110
- continue;
111
- if (msg.toolName !== toolName)
112
- continue;
113
- const field = readEngineLaborFallbackFieldFrom(msg.details);
114
- if (field === undefined)
115
- continue;
116
- recordEngineLaborFallback(latch, {
117
- engine: field.engineLaborFallback.engine,
118
- failure: field.engineLaborFallback.failure,
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
+ };
@@ -11,7 +11,6 @@ import { AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE, AUDITOR_PARENT_ATTEMPT_BINDING_E
11
11
  import { createEngineDetourToolDefinition } from "./engine-detour-tool.js";
12
12
  import { engineNameFromEnv } from "./engine-detour.js";
13
13
  import { appendEngineSessionMaterial, engineSessionMaterialFromOptions, } from "./package-resources/engine-material.js";
14
- import { wrapPackageOwnedToolDefinition } from "./package-owned-tool-idle.js";
15
14
  import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, RECEIPT_DELIVERY_PROMPT } from "./receipt-delivery-policy.js";
16
15
  import { REVIEWER_VERIFICATION_BOUNDARY } from "./reviewer-construction.js";
17
16
  import { createStreamIdleGuard, isStreamIdleTimeoutError } from "./stream-idle-guard.js";
@@ -599,7 +598,7 @@ export async function executeAuditorChild(options) {
599
598
  let decisionToolFailure;
600
599
  const decisionToolFailures = new Map();
601
600
  const delivery = createReceiptDeliveryPolicy();
602
- const tool = wrapPackageOwnedToolDefinition({
601
+ const tool = {
603
602
  ...options.tool,
604
603
  label: options.roleLabel,
605
604
  async execute(...args) {
@@ -625,7 +624,7 @@ export async function executeAuditorChild(options) {
625
624
  throw error;
626
625
  }
627
626
  },
628
- });
627
+ };
629
628
  const parentSessionManager = options.context.sessionManager;
630
629
  const parentHeader = parentSessionManager?.getHeader?.();
631
630
  const parentSessionFile = parentSessionManager?.getSessionFile?.();
@@ -644,7 +643,7 @@ export async function executeAuditorChild(options) {
644
643
  thinkingLevel: options.context.thinkingLevel ?? "off",
645
644
  modelRuntime: inherited.runtime,
646
645
  systemPrompt: options.systemPrompt,
647
- customTools: [wrapPackageOwnedToolDefinition({ ...options.dossierTool, label: options.roleLabel }), tool],
646
+ customTools: [{ ...options.dossierTool, label: options.roleLabel }, tool],
648
647
  sessionManager: auditorSessionManager,
649
648
  });
650
649
  const binding = {
@@ -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
- if (value.status === "completed" && isFullGitObjectId(value.mergeCommitId))
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 (value.status === "escalate")
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
@@ -14,7 +15,6 @@ import { PACKAGED_ROLE_REGISTRY, packagedRoleMetadata } from "./packaged-role-re
14
15
  import { openInProcessAgentSession } from "./in-process-session.js";
15
16
  import { renderPublicAkRoleCommand } from "./public-command-renderer.js";
16
17
  import { issueRoot, subjectPath } from "./work-subject-identity.js";
17
- import { wrapPackageOwnedToolDefinition } from "./package-owned-tool-idle.js";
18
18
  import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, RECEIPT_DELIVERY_PROMPT } from "./receipt-delivery-policy.js";
19
19
  import { recordTypedProviderHttpStatus } from "./typed-provider-http.js";
20
20
  import {
@@ -247,7 +247,7 @@ function parseNavigatorModelSetting(value) {
247
247
  return { provider, model, thinkingLevel: suffix === "max" ? "max" : "off" };
248
248
  }
249
249
  function createNavigatorPrepareTool(onOutput) {
250
- return wrapPackageOwnedToolDefinition({
250
+ return {
251
251
  name: NAVIGATOR_PREPARE_TOOL_NAME,
252
252
  label: "Navigator preparation",
253
253
  description: "Submit Navigator direction advice. Provide candidates with next.role (phase when meaningful). route/matches/reason/command are optional context, not acceptance gates.",
@@ -256,7 +256,14 @@ function createNavigatorPrepareTool(onOutput) {
256
256
  onOutput(value);
257
257
  return { content: [{ type: "text", text: "Navigator preparation accepted" }], details: value, terminate: true };
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
+ );
260
267
  }
261
268
  function selectNavigatorCandidate(candidates, settlement) {
262
269
  if (settlement.kind !== "accepted") return void 0;
@@ -268,7 +275,7 @@ function selectNavigatorCandidate(candidates, settlement) {
268
275
  if (rolePhaseMatched.length > 0) {
269
276
  if (settlement.status !== void 0) {
270
277
  const statusSpecific = rolePhaseMatched.find(
271
- (candidate) => candidate.matches?.statuses?.includes(settlement.status) === true
278
+ (candidate) => candidate.matches?.statuses !== void 0 && statusListMatchesSettlement(candidate.matches.statuses, settlement.status)
272
279
  );
273
280
  if (statusSpecific !== void 0) {
274
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 (["converged", "continue", "escalate"].includes(String(judgeStatus)))
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
- if (receipt.status !== intent.status || (intent.status === "completed" ? receipt.diagnostic !== undefined : receipt.diagnostic !== intent.diagnostic)) {
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;