@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
@@ -136,54 +136,6 @@ var init_collector_output = __esm({
136
136
  }
137
137
  });
138
138
 
139
- // src/engine-labor-fallback.ts
140
- function buildEngineLaborFallbackField(input) {
141
- return Object.freeze({
142
- engineLaborFallback: Object.freeze({
143
- engine: input.engine,
144
- failure: input.failure,
145
- laborBy: "seat"
146
- })
147
- });
148
- }
149
- function isSeatFallbackTaintedStatus(status) {
150
- return status.endsWith(SEAT_FALLBACK_STATUS_SUFFIX);
151
- }
152
- function seatFallbackBaseStatus(status) {
153
- return isSeatFallbackTaintedStatus(status) ? status.slice(0, -SEAT_FALLBACK_STATUS_SUFFIX.length) : status;
154
- }
155
- function seatFallbackStatusHasLawfulEvidence(status, source) {
156
- if (!isSeatFallbackTaintedStatus(status)) return true;
157
- return readEngineLaborFallbackFieldFrom(source) !== void 0;
158
- }
159
- function readEngineLaborFallbackFieldFrom(source) {
160
- if (typeof source !== "object" || source === null || Array.isArray(source)) {
161
- return void 0;
162
- }
163
- let raw;
164
- try {
165
- raw = source.engineLaborFallback;
166
- } catch {
167
- return void 0;
168
- }
169
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
170
- const rec = raw;
171
- if (typeof rec.engine !== "string" || typeof rec.failure !== "string" || rec.laborBy !== "seat") {
172
- return void 0;
173
- }
174
- return buildEngineLaborFallbackField({
175
- engine: rec.engine,
176
- failure: rec.failure
177
- });
178
- }
179
- var SEAT_FALLBACK_STATUS_SUFFIX;
180
- var init_engine_labor_fallback = __esm({
181
- "src/engine-labor-fallback.ts"() {
182
- "use strict";
183
- SEAT_FALLBACK_STATUS_SUFFIX = "-by-fallback";
184
- }
185
- });
186
-
187
139
  // src/package-contracts/judge-output.ts
188
140
  function validateAcceptedJudgeDetails(verdict) {
189
141
  if (verdict === null || typeof verdict !== "object" || Array.isArray(verdict)) throw new Error("Judge verdict has no execution discriminator");
@@ -196,8 +148,7 @@ function validateAcceptedJudgeDetails(verdict) {
196
148
  if (typeof judgeStatus !== "string") {
197
149
  throw new Error("Judge verdict has no execution discriminator");
198
150
  }
199
- const base = seatFallbackBaseStatus(judgeStatus);
200
- if (["converged", "continue", "escalate"].includes(base) && seatFallbackStatusHasLawfulEvidence(judgeStatus, verdict)) {
151
+ if (["converged", "continue", "escalate"].includes(judgeStatus)) {
201
152
  return verdict;
202
153
  }
203
154
  throw new Error("Judge verdict has no execution discriminator");
@@ -206,7 +157,6 @@ var JUDGE_OUTPUT_TOOL_NAME;
206
157
  var init_judge_output = __esm({
207
158
  "src/package-contracts/judge-output.ts"() {
208
159
  "use strict";
209
- init_engine_labor_fallback();
210
160
  JUDGE_OUTPUT_TOOL_NAME = "ak_judge_output";
211
161
  }
212
162
  });
@@ -225,9 +175,6 @@ function read(value, key) {
225
175
  }
226
176
  function validateRuntimeReviewerReceipt(output) {
227
177
  const status = read(output, "status");
228
- if (typeof status === "string" && !seatFallbackStatusHasLawfulEvidence(status, output)) {
229
- throw new Error("Reviewer receipt has no recognized execution discriminator");
230
- }
231
178
  const acceptedBatch = read(output, "acceptedBatch");
232
179
  const identities = read(output, "identities");
233
180
  const construction = read(identities, "construction");
@@ -283,7 +230,6 @@ var REVIEWER_OUTPUT_TOOL_NAME;
283
230
  var init_reviewer_output = __esm({
284
231
  "src/package-contracts/reviewer-output.ts"() {
285
232
  "use strict";
286
- init_engine_labor_fallback();
287
233
  REVIEWER_OUTPUT_TOOL_NAME = "ak_reviewer_output";
288
234
  }
289
235
  });
@@ -14154,18 +14100,13 @@ function read2(value, key) {
14154
14100
  }
14155
14101
  function validateDoctorSubmissionShape(value) {
14156
14102
  const status = read2(value, "status");
14157
- const base = typeof status === "string" ? seatFallbackBaseStatus(status) : status;
14158
- if (base !== "completed" && base !== "refused") throw new DoctorSubmissionContractError("Doctor submission has no recognized execution status");
14159
- if (typeof status === "string" && !seatFallbackStatusHasLawfulEvidence(status, value)) {
14160
- throw new DoctorSubmissionContractError("Doctor submission has no recognized execution status");
14161
- }
14103
+ if (status !== "completed" && status !== "refused") throw new DoctorSubmissionContractError("Doctor submission has no recognized execution status");
14162
14104
  return value;
14163
14105
  }
14164
14106
  function validateRecordedDoctorOutput(value) {
14165
14107
  const output = validateDoctorSubmissionShape(value);
14166
14108
  const status = read2(output, "status");
14167
- const base = typeof status === "string" ? seatFallbackBaseStatus(status) : status;
14168
- if (base === "completed" && read2(output, "cost") === void 0) throw new Error("Completed Doctor receipt has no runtime-owned cost testimony");
14109
+ if (status === "completed" && read2(output, "cost") === void 0) throw new Error("Completed Doctor receipt has no runtime-owned cost testimony");
14169
14110
  return output;
14170
14111
  }
14171
14112
  var DOCTOR_OUTPUT_TOOL_NAME, DOCTOR_TARGET_KINDS, nonblank, count, evidenceIds, guardrail, lastRealBite, assetKinds, findingBody, finding, caseIdentity, cost, doctorSubmissionVariants, doctorSubmissionSchema, doctorOutputSchema, doctorEvidenceReadSchema, DoctorSubmissionContractError;
@@ -14174,7 +14115,6 @@ var init_doctor_contracts = __esm({
14174
14115
  "use strict";
14175
14116
  init_build();
14176
14117
  init_canonical_json();
14177
- init_engine_labor_fallback();
14178
14118
  init_open_tool_schema();
14179
14119
  DOCTOR_OUTPUT_TOOL_NAME = "ak_doctor_output";
14180
14120
  DOCTOR_TARGET_KINDS = ["law", "gate", "template", "station", "seat"];
@@ -14319,12 +14259,8 @@ function validateMergerInput(value) {
14319
14259
  function validateMergerOutput(value, expectedAttemptId) {
14320
14260
  if (!record(value) || expectedAttemptId !== void 0 && value.attemptId !== expectedAttemptId) throw new Error("Merger output attempt mismatch");
14321
14261
  const status = typeof value.status === "string" ? value.status : void 0;
14322
- const statusBase = status !== void 0 ? seatFallbackBaseStatus(status) : void 0;
14323
- if (status !== void 0 && !seatFallbackStatusHasLawfulEvidence(status, value)) {
14324
- throw new Error("Merger output has no recognized execution discriminator");
14325
- }
14326
- if (statusBase === "completed" && isFullGitObjectId(value.mergeCommitId)) return structuredClone(value);
14327
- if (statusBase === "escalate") return structuredClone(value);
14262
+ if (status === "completed" && isFullGitObjectId(value.mergeCommitId)) return structuredClone(value);
14263
+ if (status === "escalate") return structuredClone(value);
14328
14264
  throw new Error("Merger output has no recognized execution discriminator");
14329
14265
  }
14330
14266
  var oidPattern, materialSchema, checkSchema, mergerInputSchema, mergerOutputVariants, mergerOutputSchema, MERGER_OUTPUT_TOOL_NAME, record, blank, MergerInputContractError;
@@ -14334,7 +14270,6 @@ var init_merger_contracts = __esm({
14334
14270
  init_build();
14335
14271
  init_git_object_id();
14336
14272
  init_exact_utf8();
14337
- init_engine_labor_fallback();
14338
14273
  init_sha256();
14339
14274
  init_open_tool_schema();
14340
14275
  oidPattern = "^(?:[0-9a-f]{40}|[0-9a-f]{64})$";
@@ -14380,10 +14315,10 @@ function validateNotaryOutput(value) {
14380
14315
  throw new Error("Notary output has no recognized execution discriminator");
14381
14316
  }
14382
14317
  const statusRaw = typeof value.status === "string" ? value.status : void 0;
14383
- if (statusRaw === void 0 || !seatFallbackStatusHasLawfulEvidence(statusRaw, value)) {
14318
+ if (statusRaw === void 0) {
14384
14319
  throw new Error("Notary output has no recognized execution discriminator");
14385
14320
  }
14386
- const status = seatFallbackBaseStatus(statusRaw);
14321
+ const status = statusRaw;
14387
14322
  if (status === "bounce") {
14388
14323
  const clone = structuredClone(value);
14389
14324
  if (clone.disposition === void 0) clone.disposition = "rewrite";
@@ -14401,7 +14336,7 @@ function validateRecordedNotaryOutput(value) {
14401
14336
  return validateNotaryOutput(value);
14402
14337
  }
14403
14338
  function notaryDecisiveFacts(output) {
14404
- const status = seatFallbackBaseStatus(String(output.status));
14339
+ const status = String(output.status);
14405
14340
  const facts = { status, officer: "notary" };
14406
14341
  if (status === "pass" || status === "bounce") {
14407
14342
  const findings = output.findings;
@@ -14417,7 +14352,6 @@ var init_notary_contracts = __esm({
14417
14352
  "src/notary-contracts.ts"() {
14418
14353
  "use strict";
14419
14354
  init_build();
14420
- init_engine_labor_fallback();
14421
14355
  init_open_tool_schema();
14422
14356
  NOTARY_OUTPUT_TOOL_NAME = "ak_notary_output";
14423
14357
  NOTARY_FIXED_KICKOFF = "Notary review. Bound source-run locator is on the session materials; fetch authoritative ticket, git, and dossier evidence yourself; submit one typed decision.";
@@ -15434,10 +15368,9 @@ function validateAcceptedDetails(toolName, details) {
15434
15368
  [NOTARY_OUTPUT_TOOL_NAME]: ["pass", "bounce"]
15435
15369
  };
15436
15370
  const collectorDiscriminator = toolName === COLLECTOR_OUTPUT_TOOL && Array.isArray(candidate?.groups);
15437
- const baseDiscriminator = typeof discriminator === "string" ? seatFallbackBaseStatus(discriminator) : discriminator;
15438
- const taintedWithoutEvidence = typeof discriminator === "string" && !seatFallbackStatusHasLawfulEvidence(discriminator, details);
15371
+ const baseDiscriminator = discriminator;
15439
15372
  const runtimeBindingMissing = toolName === DOCTOR_OUTPUT_TOOL_NAME && baseDiscriminator === "completed" && !(candidate?.cost !== null && typeof candidate?.cost === "object") || toolName === REVIEWER_OUTPUT_TOOL_NAME && candidate?.version !== 2;
15440
- if (taintedWithoutEvidence || runtimeBindingMissing || !collectorDiscriminator && (typeof discriminator !== "string" || !lawfulStatuses[toolName].includes(baseDiscriminator))) {
15373
+ if (runtimeBindingMissing || !collectorDiscriminator && (typeof discriminator !== "string" || !lawfulStatuses[toolName].includes(baseDiscriminator))) {
15441
15374
  throw new AcceptedDetailsContractError("terminating receipt has no recognized execution discriminator");
15442
15375
  }
15443
15376
  try {
@@ -15477,7 +15410,7 @@ function acceptedFacts(toolName, details) {
15477
15410
  case MERGER_OUTPUT_TOOL_NAME: {
15478
15411
  const output = details;
15479
15412
  const status = output.status;
15480
- return { status, ...seatFallbackBaseStatus(status) === "completed" && typeof output.mergeCommitId === "string" ? { commit: output.mergeCommitId } : {} };
15413
+ return { status, ...status === "completed" && typeof output.mergeCommitId === "string" ? { commit: output.mergeCommitId } : {} };
15481
15414
  }
15482
15415
  case COLLECTOR_OUTPUT_TOOL:
15483
15416
  return { status: "collected" };
@@ -15491,7 +15424,6 @@ var init_terminating_tools = __esm({
15491
15424
  init_judge_output();
15492
15425
  init_reviewer_output();
15493
15426
  init_audit_escalation();
15494
- init_engine_labor_fallback();
15495
15427
  init_doctor_contracts();
15496
15428
  init_merger_contracts();
15497
15429
  init_notary_contracts();
@@ -20391,7 +20323,6 @@ var init_engine_detour_tool = __esm({
20391
20323
  "use strict";
20392
20324
  init_build();
20393
20325
  init_engine_detour();
20394
- init_engine_labor_fallback();
20395
20326
  engineDetourArgsSchema = typebox_exports.Object(
20396
20327
  {
20397
20328
  argv: typebox_exports.Array(typebox_exports.String({ minLength: 1 }), {
@@ -21397,11 +21328,9 @@ function auditNoReceiptDecisiveFact(candidate) {
21397
21328
  function judgeDecisiveFacts(verdict, judgeStatus) {
21398
21329
  const facts = {
21399
21330
  judgeStatus,
21400
- ...auditNoReceiptDecisiveFact(verdict),
21401
- // #380: spread sole-built field; do not re-key here (S1).
21402
- ...readEngineLaborFallbackFieldFrom(verdict)
21331
+ ...auditNoReceiptDecisiveFact(verdict)
21403
21332
  };
21404
- const statusBase = seatFallbackBaseStatus(judgeStatus);
21333
+ const statusBase = judgeStatus;
21405
21334
  if (statusBase === "continue") {
21406
21335
  const fix = safelyRead(verdict, "fix");
21407
21336
  if (fix.readable && isRecord8(fix.value)) {
@@ -21451,7 +21380,7 @@ function coderDecisiveFacts(output) {
21451
21380
  const status = safelyRead(candidate, "status");
21452
21381
  const facts = {};
21453
21382
  if (status.readable && typeof status.value === "string") facts.coderStatus = status.value;
21454
- const statusBase = status.readable && typeof status.value === "string" ? seatFallbackBaseStatus(status.value) : void 0;
21383
+ const statusBase = status.readable && typeof status.value === "string" ? status.value : void 0;
21455
21384
  const remainingScope = safelyRead(candidate, "remainingScope");
21456
21385
  if (statusBase === "unfinished" && remainingScope.readable && typeof remainingScope.value === "string") facts.remainingScope = remainingScope.value;
21457
21386
  const reason = safelyRead(candidate, "reason");
@@ -21467,7 +21396,7 @@ function fixerDecisiveFacts(output) {
21467
21396
  const status = safelyRead(candidate, "status");
21468
21397
  const facts = {};
21469
21398
  if (status.readable && typeof status.value === "string") facts.fixerStatus = status.value;
21470
- const statusBase = status.readable && typeof status.value === "string" ? seatFallbackBaseStatus(status.value) : void 0;
21399
+ const statusBase = status.readable && typeof status.value === "string" ? status.value : void 0;
21471
21400
  const remainingScope = safelyRead(candidate, "remainingScope");
21472
21401
  if ((statusBase === "unfinished" || statusBase === "refused") && remainingScope.readable && typeof remainingScope.value === "string") facts.remainingScope = remainingScope.value;
21473
21402
  const reason = safelyRead(candidate, "reason");
@@ -21551,7 +21480,7 @@ function doctorDecisiveFacts(output) {
21551
21480
  const status = safelyRead(candidate, "status");
21552
21481
  const facts = { ...auditNoReceiptDecisiveFact(candidate) };
21553
21482
  if (status.readable && typeof status.value === "string") facts.doctorStatus = status.value;
21554
- const statusBase = status.readable && typeof status.value === "string" ? seatFallbackBaseStatus(status.value) : void 0;
21483
+ const statusBase = status.readable && typeof status.value === "string" ? status.value : void 0;
21555
21484
  if (statusBase === "refused") {
21556
21485
  const reason = safelyRead(candidate, "reason");
21557
21486
  if (reason.readable && reason.value !== void 0) facts.reason = reason.value;
@@ -21593,16 +21522,14 @@ function reviewerDecisiveFacts(output) {
21593
21522
  reportAxes,
21594
21523
  amendmentAxes,
21595
21524
  acceptedBatchPresent: acceptedBatch.readable && acceptedBatch.value !== void 0,
21596
- ...auditNoReceiptDecisiveFact(candidate),
21597
- // #380: spread sole-built field; do not re-key here (S1).
21598
- ...readEngineLaborFallbackFieldFrom(candidate)
21525
+ ...auditNoReceiptDecisiveFact(candidate)
21599
21526
  };
21600
21527
  if (status.readable && typeof status.value === "string") facts.reviewerStatus = status.value;
21601
21528
  if (specDisposition.readable && (specDisposition.value === "launched" || specDisposition.value === "skipped-missing")) {
21602
21529
  facts.specDisposition = specDisposition.value;
21603
21530
  }
21604
21531
  const diagnostic = safelyRead(candidate, "diagnostic");
21605
- const statusBase = status.readable && typeof status.value === "string" ? seatFallbackBaseStatus(status.value) : void 0;
21532
+ const statusBase = status.readable && typeof status.value === "string" ? status.value : void 0;
21606
21533
  if (statusBase === "refused" && diagnostic.readable) {
21607
21534
  facts.diagnosticPresent = typeof diagnostic.value === "string" && diagnostic.value.trim().length > 0;
21608
21535
  }
@@ -21868,9 +21795,8 @@ function extractJudgeRoleOutcome(entries) {
21868
21795
  const statusRead = safelyRead(details, "judgeStatus");
21869
21796
  if (!statusRead.readable || typeof statusRead.value !== "string") continue;
21870
21797
  const judgeStatus = statusRead.value;
21871
- const statusBase = seatFallbackBaseStatus(judgeStatus);
21798
+ const statusBase = judgeStatus;
21872
21799
  if (statusBase !== "converged" && statusBase !== "continue" && statusBase !== "escalate") continue;
21873
- if (!seatFallbackStatusHasLawfulEvidence(judgeStatus, details)) continue;
21874
21800
  return {
21875
21801
  kind: "accepted",
21876
21802
  role: "judge",
@@ -22614,7 +22540,7 @@ async function settleLawfulDoctorTerminalResult(admitted) {
22614
22540
  if (entries === void 0) return void 0;
22615
22541
  const extracted = extractDoctorRoleOutcome(entries);
22616
22542
  if (extracted === void 0) return void 0;
22617
- if (extracted.output !== void 0 && seatFallbackBaseStatus(String(extracted.output.status)) === "completed") {
22543
+ if (extracted.output !== void 0 && String(extracted.output.status) === "completed") {
22618
22544
  const completedCase = extracted.output.case;
22619
22545
  if (completedCase.issueNumber !== admitted.caseIdentity.issueNumber || completedCase.runsPath !== admitted.caseIdentity.runsPath) {
22620
22546
  const error = new Error(
@@ -22898,7 +22824,7 @@ function mergerDecisiveFacts(output) {
22898
22824
  const attemptId = safelyRead(candidate, "attemptId");
22899
22825
  if (status.readable && typeof status.value === "string") facts.mergerStatus = status.value;
22900
22826
  if (attemptId.readable && attemptId.value !== void 0) facts.attemptId = attemptId.value;
22901
- const statusBase = status.readable && typeof status.value === "string" ? seatFallbackBaseStatus(status.value) : void 0;
22827
+ const statusBase = status.readable && typeof status.value === "string" ? status.value : void 0;
22902
22828
  const decisiveKey = statusBase === "completed" ? "mergeCommitId" : "diagnosis";
22903
22829
  const decisive = safelyRead(candidate, decisiveKey);
22904
22830
  if (decisive.readable && decisive.value !== void 0) facts[decisiveKey] = decisive.value;
@@ -23375,7 +23301,6 @@ var init_settlement = __esm({
23375
23301
  init_compliance_transport();
23376
23302
  init_collector_ledger();
23377
23303
  init_engine_detour();
23378
- init_engine_labor_fallback();
23379
23304
  init_judge_output();
23380
23305
  init_collector_output();
23381
23306
  init_worker_output();
@@ -27151,9 +27076,8 @@ function mapTerminal(role, terminal) {
27151
27076
  noReceipt: false
27152
27077
  };
27153
27078
  }
27154
- const statusBase = seatFallbackBaseStatus(status);
27155
27079
  const acceptedSet = ACCEPTED_STATUS[role];
27156
- if (acceptedSet === void 0 || !acceptedSet.has(statusBase)) {
27080
+ if (acceptedSet === void 0 || !acceptedSet.has(status)) {
27157
27081
  return {
27158
27082
  terminalLabel: status,
27159
27083
  accepted: false,
@@ -27162,9 +27086,9 @@ function mapTerminal(role, terminal) {
27162
27086
  noReceipt: false
27163
27087
  };
27164
27088
  }
27165
- const plannedDuty = WORKER_ROLES.has(role) && statusBase === "planned";
27089
+ const plannedDuty = WORKER_ROLES.has(role) && status === "planned";
27166
27090
  const successSet = SUCCESS_STATUS[role] ?? /* @__PURE__ */ new Set();
27167
- const success = !plannedDuty && successSet.has(statusBase);
27091
+ const success = !plannedDuty && successSet.has(status);
27168
27092
  const successEligible = !plannedDuty;
27169
27093
  return {
27170
27094
  terminalLabel: status,
@@ -27286,7 +27210,6 @@ var WORKER_ROLES, ACCEPTED_STATUS, SUCCESS_STATUS, acceptanceSuccessReworkFamily
27286
27210
  var init_acceptance_success_rework = __esm({
27287
27211
  "src/analyst-metric-families/acceptance-success-rework.ts"() {
27288
27212
  "use strict";
27289
- init_engine_labor_fallback();
27290
27213
  init_analyst_median();
27291
27214
  WORKER_ROLES = /* @__PURE__ */ new Set(["coder", "fixer"]);
27292
27215
  ACCEPTED_STATUS = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.2397",
3
+ "version": "0.1.2402",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -9,8 +9,7 @@ const entries = [
9
9
  "work-subject-identity",
10
10
  "navigator-invocation-identity",
11
11
  "navigator-attendance",
12
- // Navigator package-graph dependency (seat-fallback base status / ADR 0071).
13
- "engine-labor-fallback",
12
+ // Navigator package-graph dependencies used by attendance settlement.
14
13
  "activation-ledger-git",
15
14
  "activation-ledger-topology",
16
15
  "activation-reconciliation",
@@ -9,7 +9,6 @@
9
9
  * - success rate den = success-eligible accepted legs (no-receipt out; planned out)
10
10
  * - planned = plan-duty acceptance; never success numerator or denominator
11
11
  */
12
- import { seatFallbackBaseStatus } from "../engine-labor-fallback.ts";
13
12
  import type { AnalystReadableRunFacts, AnalystRunTerminalFace } from "../analyst-ledger.ts";
14
13
  import { medianNumber } from "../analyst-median.ts";
15
14
  import type { AnalystMetricFamilyModule } from "../analyst-metric-family.ts";
@@ -186,10 +185,8 @@ function mapTerminal(
186
185
  };
187
186
  }
188
187
 
189
- // Seat-fallback taint is visible on the label; acceptance/success use base semantics.
190
- const statusBase = seatFallbackBaseStatus(status);
191
188
  const acceptedSet = ACCEPTED_STATUS[role];
192
- if (acceptedSet === undefined || !acceptedSet.has(statusBase)) {
189
+ if (acceptedSet === undefined || !acceptedSet.has(status)) {
193
190
  return {
194
191
  terminalLabel: status,
195
192
  accepted: false,
@@ -199,9 +196,9 @@ function mapTerminal(
199
196
  };
200
197
  }
201
198
 
202
- const plannedDuty = WORKER_ROLES.has(role) && statusBase === "planned";
199
+ const plannedDuty = WORKER_ROLES.has(role) && status === "planned";
203
200
  const successSet = SUCCESS_STATUS[role] ?? new Set<string>();
204
- const success = !plannedDuty && successSet.has(statusBase);
201
+ const success = !plannedDuty && successSet.has(status);
205
202
  const successEligible = !plannedDuty;
206
203
 
207
204
  return {
@@ -4,10 +4,6 @@ import type {
4
4
  } from "@earendil-works/pi-coding-agent";
5
5
  import type { Static } from "typebox";
6
6
 
7
- import {
8
- readActivationEngineLaborFallbackField,
9
- withEngineLaborFallbackField,
10
- } from "./engine-labor-fallback.ts";
11
7
  import {
12
8
  COLLECTOR_FIXED_KICKOFF,
13
9
  emptyCollectorManifest,
@@ -422,10 +418,7 @@ export function createCollectorRoleRuntime(
422
418
  );
423
419
  activation.ledger.markOutputAccepted();
424
420
  activation.ledger.completeOperational(toolCallId);
425
- const acceptedDetails = withEngineLaborFallbackField(
426
- receipt,
427
- readActivationEngineLaborFallbackField(),
428
- );
421
+ const acceptedDetails = receipt;
429
422
  return {
430
423
  content: [{
431
424
  type: "text" as const,
@@ -1,10 +1,5 @@
1
1
  import { Type } from "typebox";
2
2
  import { canonicalJson } from "./canonical-json.ts";
3
- import {
4
- seatFallbackBaseStatus,
5
- seatFallbackStatusHasLawfulEvidence,
6
- type WithEngineLaborFallback,
7
- } from "./engine-labor-fallback.ts";
8
3
  import { openToolObjectFromUnion } from "./open-tool-schema.ts";
9
4
 
10
5
  export const DOCTOR_EVIDENCE_TOOL_NAME = "ak_doctor_evidence";
@@ -42,13 +37,9 @@ export type DoctorFinding =
42
37
  export type DoctorSubmission =
43
38
  | { status: "completed"; case: DoctorCaseIdentity; findings: DoctorFinding[] }
44
39
  | { status: "refused"; reason: string; missingEvidence: Array<{ need: string; targetKeys: string[] }> };
45
- type DoctorOutputClean =
40
+ export type DoctorOutput =
46
41
  | { status: "completed"; case: DoctorCaseIdentity; findings: DoctorFinding[]; cost: DoctorCaseCost }
47
42
  | Extract<DoctorSubmission, { status: "refused" }>;
48
- /** Clean recorded shape or seat-fallback tainted accepted receipt (ADR 0071). */
49
- export type DoctorOutput =
50
- | DoctorOutputClean
51
- | WithEngineLaborFallback<DoctorOutputClean>;
52
43
  export type DoctorEvidenceEntry = { id: string; kind: "session" | "stderr"; byteLength: number; contentLength: number; sha256: string; content: string };
53
44
  export type DoctorCase = { version: 1; identity: DoctorCaseIdentity; evidence: DoctorEvidenceEntry[]; cost: DoctorCaseCost };
54
45
 
@@ -105,19 +96,13 @@ function isRecord(value: unknown): value is Record<string, unknown> { return typ
105
96
  function read(value: unknown, key: string): unknown { if (!isRecord(value)) return undefined; try { return value[key]; } catch { return undefined; } }
106
97
  export function validateDoctorSubmissionShape(value: unknown): DoctorSubmission {
107
98
  const status = read(value, "status");
108
- const base = typeof status === "string" ? seatFallbackBaseStatus(status) : status;
109
- if (base !== "completed" && base !== "refused") throw new DoctorSubmissionContractError("Doctor submission has no recognized execution status");
110
- // ADR 0071: tainted status requires latch-shaped engineLaborFallback evidence.
111
- if (typeof status === "string" && !seatFallbackStatusHasLawfulEvidence(status, value)) {
112
- throw new DoctorSubmissionContractError("Doctor submission has no recognized execution status");
113
- }
99
+ if (status !== "completed" && status !== "refused") throw new DoctorSubmissionContractError("Doctor submission has no recognized execution status");
114
100
  return value as DoctorSubmission;
115
101
  }
116
102
  export function validateRecordedDoctorOutput(value: unknown): DoctorOutput {
117
103
  const output = validateDoctorSubmissionShape(value);
118
104
  const status = read(output, "status");
119
- const base = typeof status === "string" ? seatFallbackBaseStatus(status) : status;
120
- if (base === "completed" && read(output, "cost") === undefined) throw new Error("Completed Doctor receipt has no runtime-owned cost testimony");
105
+ if (status === "completed" && read(output, "cost") === undefined) throw new Error("Completed Doctor receipt has no runtime-owned cost testimony");
121
106
  return output as DoctorOutput;
122
107
  }
123
108
 
@@ -2,10 +2,6 @@ import type { AgentToolResult, ExtensionAPI, ExtensionContext } from "@earendil-
2
2
  import { disposeComplianceDecision } from "./audit-escalation.ts";
3
3
  import { appendActiveSessionCustomEntry, type ComplianceDecision } from "./compliance-transport.ts";
4
4
  import { DOCTOR_CANDIDATE_ENTRY_TYPE } from "./dossier-resolution.ts";
5
- import {
6
- readActivationEngineLaborFallbackField,
7
- withEngineLaborFallbackField,
8
- } from "./engine-labor-fallback.ts";
9
5
  import { DOCTOR_EVIDENCE_TOOL_NAME, DOCTOR_OUTPUT_TOOL_DESCRIPTION, DOCTOR_OUTPUT_TOOL_NAME, DoctorEvidenceStore, doctorEvidenceReadSchema, doctorSubmissionSchema, validateDoctorOutput, type DoctorCase } from "./doctor-contracts.ts";
10
6
 
11
7
  export { DOCTOR_EVIDENCE_TOOL_NAME, DOCTOR_OUTPUT_TOOL_NAME };
@@ -16,7 +12,7 @@ export function createDoctorRoleRuntime(pi: ExtensionAPI, dependencies: DoctorRo
16
12
  let activation: { soul: string; patient: DoctorCase; store: DoctorEvidenceStore } | undefined; let registered = false; pi.registerFlag(DOCTOR_CASE_FLAG.name, DOCTOR_CASE_FLAG.definition);
17
13
  return { async activate() { const path = pi.getFlag(DOCTOR_CASE_FLAG.name); if (typeof path !== "string" || !path.trim()) throw new Error("Doctor requires --ak-doctor-case"); const soul = (await dependencies.loadSoul()).trim(); if (!soul) throw new Error("Doctor soul is empty"); const patient = await dependencies.loadCase(path); activation = { soul, patient, store: new DoctorEvidenceStore(patient) };
18
14
  if (!registered) { registered = true; pi.registerTool({ name: DOCTOR_EVIDENCE_TOOL_NAME, label: "Doctor Evidence", description: "Read retained Pi session bytes with bounded pagination.", parameters: doctorEvidenceReadSchema, async execute(_id, params: { evidenceId: string; offset?: number; limit?: number }) { if (!activation) throw new Error("Doctor is not activated"); const details = activation.store.read(params.evidenceId, params.offset, params.limit); return { content: [{ type: "text" as const, text: JSON.stringify(details) }], details }; } });
19
- pi.registerTool({ name: DOCTOR_OUTPUT_TOOL_NAME, label: "Doctor Output", description: DOCTOR_OUTPUT_TOOL_DESCRIPTION, parameters: doctorSubmissionSchema, async execute(id, params, signal, _update, ctx): Promise<AgentToolResult<unknown>> { if (!activation) throw new Error("Doctor is not activated"); singleton(id, ctx); const testimony = validateDoctorOutput(params, activation.patient, activation.store); try { appendActiveSessionCustomEntry(ctx, DOCTOR_CANDIDATE_ENTRY_TYPE, { version: 1, testimony, readRecord: activation.store.readRecord(), patientIdentity: activation.patient.identity }, { unavailable: "doctor candidate retention is unavailable", failed: "doctor candidate retention failed" }); } catch (error) { host.failInfrastructure(error, ctx, id); } let audit: ComplianceDecision; try { audit = await dependencies.auditCompliance(signal === undefined ? { context: ctx } : { context: ctx, signal }); } catch (error) { host.failInfrastructure(error, ctx, id); } const details = testimony.status === "completed" ? { ...testimony, cost: activation.patient.cost } : testimony; const acceptedDetails = withEngineLaborFallbackField(details, readActivationEngineLaborFallbackField()); return disposeComplianceDecision<AgentToolResult<unknown>>(audit, { pass: (usage) => ({ content: [{ type: "text" as const, text: "Doctor output accepted" }], details: acceptedDetails, terminate: true as const, ...(usage === undefined ? {} : { usage }) }), noReceipt: (auditNoReceipt, usageProjection) => ({ content: [{ type: "text" as const, text: "Doctor output accepted; compliance audit produced no receipt" }], details: { ...acceptedDetails, auditNoReceipt }, terminate: true as const, ...usageProjection }), revise: (violations) => { throw new Error(`Doctor output violates its soul: ${violations.join("; ")}`); }, escalate: (result) => result }, acceptedDetails); } });
15
+ pi.registerTool({ name: DOCTOR_OUTPUT_TOOL_NAME, label: "Doctor Output", description: DOCTOR_OUTPUT_TOOL_DESCRIPTION, parameters: doctorSubmissionSchema, async execute(id, params, signal, _update, ctx): Promise<AgentToolResult<unknown>> { if (!activation) throw new Error("Doctor is not activated"); singleton(id, ctx); const testimony = validateDoctorOutput(params, activation.patient, activation.store); try { appendActiveSessionCustomEntry(ctx, DOCTOR_CANDIDATE_ENTRY_TYPE, { version: 1, testimony, readRecord: activation.store.readRecord(), patientIdentity: activation.patient.identity }, { unavailable: "doctor candidate retention is unavailable", failed: "doctor candidate retention failed" }); } catch (error) { host.failInfrastructure(error, ctx, id); } let audit: ComplianceDecision; try { audit = await dependencies.auditCompliance(signal === undefined ? { context: ctx } : { context: ctx, signal }); } catch (error) { host.failInfrastructure(error, ctx, id); } const details = testimony.status === "completed" ? { ...testimony, cost: activation.patient.cost } : testimony; const acceptedDetails = details; return disposeComplianceDecision<AgentToolResult<unknown>>(audit, { pass: (usage) => ({ content: [{ type: "text" as const, text: "Doctor output accepted" }], details: acceptedDetails, terminate: true as const, ...(usage === undefined ? {} : { usage }) }), noReceipt: (auditNoReceipt, usageProjection) => ({ content: [{ type: "text" as const, text: "Doctor output accepted; compliance audit produced no receipt" }], details: { ...acceptedDetails, auditNoReceipt }, terminate: true as const, ...usageProjection }), revise: (violations) => { throw new Error(`Doctor output violates its soul: ${violations.join("; ")}`); }, escalate: (result) => result }, acceptedDetails); } });
20
16
  pi.on("before_agent_start", (event) => { if (!activation) throw new Error("Doctor is not activated"); const catalog = { version: activation.patient.version, identity: activation.patient.identity, admittedMetrics: { provenance: "runtime-derived from retained session bytes and sealed into the accepted receipt", cost: activation.patient.cost }, lawfulTargetKeys: ["case", ...activation.patient.cost.invocations.sources], evidence: activation.patient.evidence.map(({ id, kind, sha256, byteLength, contentLength }) => ({ id, kind, sha256, byteLength, contentLength })) }; return { systemPrompt: `${event.systemPrompt}\n\n<doctor_soul>\n${activation.soul}\n</doctor_soul>\n\n<doctor_case>\n${JSON.stringify(catalog)}\n</doctor_case>` }; }); }
21
17
  const required = [DOCTOR_EVIDENCE_TOOL_NAME, DOCTOR_OUTPUT_TOOL_NAME]; const names = pi.getAllTools().map((tool) => tool.name); for (const name of required) if (names.filter((item) => item === name).length !== 1) throw new Error(`Doctor required tool collision or missing: ${name}`); pi.setActiveTools(required); const active = pi.getActiveTools?.() ?? required; if (active.length !== 2 || !required.every((name) => active.includes(name))) throw new Error("Doctor active tool narrowing failed"); } };
22
18
  }
@@ -2,9 +2,8 @@
2
2
  * Package-owned engine detour tool (#357 T2 / #378 / #380).
3
3
  * Registered by shared role-runtime when any role + engine activation signal is present.
4
4
  * Evidence-child legs install the same definition via customTools (no spawn in role modules).
5
- * #380: engine process failure soft-returns so the seat rejoins the main road; declaration
6
- * is recorded once via engine-labor-fallback (no fail-closed reject-leg).
7
- * Caller AbortSignal cancel propagates without fallback.
5
+ * Engine process failures stop through the host infrastructure-failure seam.
6
+ * Caller AbortSignal cancellation propagates unchanged.
8
7
  */
9
8
  import type {
10
9
  AgentToolResult,
@@ -22,11 +21,6 @@ import {
22
21
  isEngineDetourFailure,
23
22
  runEngineDetourOnce,
24
23
  } from "./engine-detour.ts";
25
- import {
26
- activationEngineLaborFallbackLatch,
27
- recordEngineLaborFallback,
28
- type EngineLaborFallbackField,
29
- } from "./engine-labor-fallback.ts";
30
24
 
31
25
  const engineDetourArgsSchema = Type.Object(
32
26
  {
@@ -56,27 +50,7 @@ export type EngineDetourToolRegistration = {
56
50
 
57
51
  type EngineDetourLatch = { used: boolean };
58
52
 
59
- function seatFallbackToolResult(
60
- field: EngineLaborFallbackField,
61
- failure: string,
62
- ): AgentToolResult<unknown> {
63
- return {
64
- content: [
65
- {
66
- type: "text" as const,
67
- text:
68
- `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.`,
69
- },
70
- ],
71
- details: {
72
- tool: ENGINE_DETOUR_TOOL_NAME,
73
- detourFailed: true,
74
- ...field,
75
- },
76
- };
77
- }
78
-
79
- /** Caller/upper-layer cancel must propagate; process failure is seat-fallback, not cancel. */
53
+ /** Caller/upper-layer cancellation must propagate unchanged. */
80
54
  function isCallerCancellation(
81
55
  error: unknown,
82
56
  signal: AbortSignal | undefined,
@@ -96,8 +70,8 @@ function isCallerCancellation(
96
70
  * Build one once-latch detour tool definition for a configured engine name.
97
71
  * `latch` is shared so parent registration can reset between activations.
98
72
  * `fail` owns host abort (parent) vs throw (evidence child) for tool misuse only.
99
- * Engine process failure (nonzero/empty/spawn) soft-returns seat fallback (#380).
100
- * Caller AbortSignal cancel propagates without writing fallback.
73
+ * Engine process failures (nonzero/empty/spawn) stop via `fail` with their cause.
74
+ * Caller AbortSignal cancellation propagates unchanged.
101
75
  */
102
76
  export function createEngineDetourToolDefinition(input: {
103
77
  engineName: string;
@@ -116,7 +90,7 @@ export function createEngineDetourToolDefinition(input: {
116
90
  `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.`,
117
91
  "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.",
118
92
  "On success, use the returned stdout as labor content for the existing typed submission / report path.",
119
- "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.",
93
+ "An engine process failure stops this activation; do not continue labor in this session.",
120
94
  ],
121
95
  parameters: engineDetourArgsSchema,
122
96
  async execute(
@@ -145,17 +119,6 @@ export function createEngineDetourToolDefinition(input: {
145
119
  );
146
120
  }
147
121
 
148
- const softFail = (failure: string): AgentToolResult<unknown> => {
149
- // Activation-scoped latch is the sole shared recorder (parent seat + legs).
150
- const fallbackLatch =
151
- activationEngineLaborFallbackLatch() ?? { field: undefined };
152
- const field = recordEngineLaborFallback(fallbackLatch, {
153
- engine: engineName,
154
- failure,
155
- });
156
- return seatFallbackToolResult(field, failure);
157
- };
158
-
159
122
  let result: Awaited<ReturnType<typeof runEngineDetourOnce>>;
160
123
  try {
161
124
  result = await runEngineDetourOnce({
@@ -164,17 +127,19 @@ export function createEngineDetourToolDefinition(input: {
164
127
  ...(signal === undefined ? {} : { signal }),
165
128
  });
166
129
  } catch (error) {
167
- // Caller cancel: propagate. Spawn/engine failure: seat fallback.
168
- if (isCallerCancellation(error, signal)) {
169
- throw error;
170
- }
171
- const failure =
172
- error instanceof Error ? error.message : String(error);
173
- return softFail(failure.trim() === "" ? "engine detour spawn failed" : failure);
130
+ if (isCallerCancellation(error, signal)) throw error;
131
+ const cause = error instanceof Error
132
+ ? error
133
+ : new Error(String(error).trim() || "engine detour spawn failed");
134
+ input.fail(cause, toolCallId, ctx);
174
135
  }
175
136
 
176
137
  if (isEngineDetourFailure(result)) {
177
- return softFail(engineDetourFailureDiagnostic(result));
138
+ input.fail(
139
+ new Error(engineDetourFailureDiagnostic(result)),
140
+ toolCallId,
141
+ ctx,
142
+ );
178
143
  }
179
144
 
180
145
  return {
@@ -610,15 +610,17 @@ export async function executeEvidenceChild(
610
610
  engine: engineName,
611
611
  packageRoot: options.packageRoot,
612
612
  });
613
- // #378/#380: same detour tool as parent seat. Engine process failure soft-returns
614
- // seat main-road fallback (ADR 0069 detour-rejoins-main-road); tool misuse still throws.
613
+ // Evidence legs use the parent detour tool; retain any engine process cause
614
+ // so the enclosing child boundary, rather than a tool-error result, terminates.
615
+ let engineDetourFailure: Error | undefined;
615
616
  const engineDetourTool =
616
617
  engineName === undefined
617
618
  ? undefined
618
619
  : createEngineDetourToolDefinition({
619
620
  engineName,
620
621
  fail(error) {
621
- throw error instanceof Error ? error : new Error(String(error));
622
+ engineDetourFailure ??= error instanceof Error ? error : new Error(String(error));
623
+ throw engineDetourFailure;
622
624
  },
623
625
  });
624
626
  // No tools allowlist — Pi defaults + unrestricted evidence surface (ADR 0064).
@@ -654,8 +656,14 @@ export async function executeEvidenceChild(
654
656
  try {
655
657
  await session.prompt(delivered);
656
658
  } catch (error) {
659
+ if (engineDetourFailure !== undefined) {
660
+ throw classifiedError(engineDetourFailure, "child");
661
+ }
657
662
  throw classifiedError(error, "provider");
658
663
  }
664
+ if (engineDetourFailure !== undefined) {
665
+ throw classifiedError(engineDetourFailure, "child");
666
+ }
659
667
  if (signal?.aborted) throw new Error("Evidence child was cancelled");
660
668
  const lastAssistant = [...session.messages]
661
669
  .reverse()