@akagilnc/pi-workflow-roles 0.1.5068 → 0.1.5096

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 (80) hide show
  1. package/dist/acp-host/production-host.js +429 -383
  2. package/dist/atomic-write.js +13 -19
  3. package/dist/auditor-soul.js +2 -1
  4. package/dist/compliance-transport.js +3 -2
  5. package/dist/countersign-contracts.js +2 -2
  6. package/dist/doctor-contracts.js +1 -1
  7. package/dist/gatekeeper-role.js +14 -35
  8. package/dist/headless-host/production-host.js +461 -415
  9. package/dist/inspector-contracts.js +5 -2
  10. package/dist/merger-contracts.js +1 -1
  11. package/dist/migrate-book-topology.js +49 -33
  12. package/dist/navigator-attendance.js +28 -143
  13. package/dist/navigator-invocation-identity.js +2 -0
  14. package/dist/navigator-public-session.js +13 -1
  15. package/dist/navigator-work-base.js +113 -0
  16. package/dist/notary-contracts.js +0 -2
  17. package/dist/open-tool-schema.js +4 -1
  18. package/dist/package-contracts/fixer-output.js +1 -1
  19. package/dist/package-contracts/gatekeeper-output.js +1 -1
  20. package/dist/package-contracts/judge-output.js +2 -2
  21. package/dist/package-contracts/navigator-output.js +1 -1
  22. package/dist/package-contracts/terminating-infrastructure.js +7 -4
  23. package/dist/packaged-role-registry.js +9 -5
  24. package/dist/public-cli/countersign-run.js +1 -1
  25. package/dist/public-cli/invocation.js +6 -3
  26. package/dist/public-cli/main.js +78 -41
  27. package/dist/public-cli/settlement.js +20 -1
  28. package/dist/receipt-delivery-policy.js +9 -2
  29. package/dist/review-submission.js +19 -8
  30. package/dist/submission-errors.js +17 -0
  31. package/dist/submission-ledger.js +18 -6
  32. package/docs/adr/0057-schema-narrowing-cuts-the-required-set-not-the-declared-set.md +9 -0
  33. package/extensions/role-runtime.ts +0 -27
  34. package/package.json +2 -1
  35. package/resources/836-deleted-machine-instruction-inventory.md +3 -3
  36. package/resources/collector-bot-handbook.md +4 -0
  37. package/resources/navigator-route-playbook.md +4 -6
  38. package/scripts/build-package.mjs +4 -0
  39. package/souls/coder-output-guide.md +1 -0
  40. package/src/auditor-soul.ts +2 -1
  41. package/src/collector-role.ts +6 -7
  42. package/src/collector-tool-schemas.ts +5 -5
  43. package/src/compliance-transport.ts +3 -2
  44. package/src/countersign-contracts.ts +2 -2
  45. package/src/countersign-role.ts +3 -7
  46. package/src/diarist-role.ts +2 -2
  47. package/src/doctor-contracts.ts +1 -1
  48. package/src/gatekeeper-role.ts +15 -36
  49. package/src/gleaner-left-role.ts +2 -2
  50. package/src/inspector-contracts.ts +5 -2
  51. package/src/inspector-role.ts +1 -1
  52. package/src/judge-role.ts +14 -22
  53. package/src/merger-contracts.ts +1 -1
  54. package/src/navigator-attendance.ts +38 -164
  55. package/src/navigator-invocation-identity.ts +2 -0
  56. package/src/navigator-public-session.ts +17 -1
  57. package/src/navigator-role.ts +4 -0
  58. package/src/navigator-session-contracts.ts +2 -0
  59. package/src/navigator-work-base.ts +123 -0
  60. package/src/notary-contracts.ts +0 -4
  61. package/src/open-tool-schema.ts +9 -4
  62. package/src/package-contracts/fixer-output.ts +1 -1
  63. package/src/package-contracts/gatekeeper-output.ts +3 -3
  64. package/src/package-contracts/judge-output.ts +2 -4
  65. package/src/package-contracts/navigator-output.ts +1 -1
  66. package/src/package-contracts/terminating-infrastructure.ts +12 -10
  67. package/src/packaged-role-registry.ts +5 -1
  68. package/src/public-cli/countersign-run.ts +1 -1
  69. package/src/public-cli/invocation.ts +6 -3
  70. package/src/public-cli/settlement.ts +21 -0
  71. package/src/receipt-delivery-policy.ts +9 -1
  72. package/src/review-submission.ts +24 -10
  73. package/src/reviewer-role.ts +3 -3
  74. package/src/role-runtime-dependencies.ts +1 -5
  75. package/src/role-runtime.ts +61 -63
  76. package/src/secretariat-role.ts +3 -3
  77. package/src/submission-errors.ts +16 -0
  78. package/src/submission-gate.ts +2 -1
  79. package/src/submission-ledger.ts +23 -11
  80. package/src/worker-role.ts +7 -9
@@ -23,6 +23,7 @@ import {
23
23
  durableSessionPointer,
24
24
  resolveBookKeyFromGit,
25
25
  } from "./activation-ledger.ts";
26
+ import { readPackageMaterial } from "./session-opening-materials.ts";
26
27
  import { writeStderrJsonlRecord } from "./stderr-jsonl.ts";
27
28
  import {
28
29
  createToolExecutionObservationFace,
@@ -38,7 +39,7 @@ import {
38
39
  } from "./engine-detour.ts";
39
40
  import { engineSessionMaterialFromOptions } from "./package-resources/engine-material.ts";
40
41
  import { registerEngineDetourTool } from "./engine-detour-tool.ts";
41
- import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, RECEIPT_DELIVERY_PROMPT } from "./receipt-delivery-policy.ts";
42
+ import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE } from "./receipt-delivery-policy.ts";
42
43
  import type { CollectorClock } from "./collector-evidence.ts";
43
44
  import type { CollectorGitHubTransport } from "./collector-github.ts";
44
45
  import {
@@ -98,7 +99,8 @@ import {
98
99
  type AuditorRuntimeDependencies,
99
100
  } from "./auditor-role.ts";
100
101
 
101
- import { formatNavigatorReport, NAVIGATOR_EVENT_TYPE, navigatorSubjectKey, navigatorUnavailableError, subjectPath, type NavigatorAttendance, type NavigatorAttendanceOptions, type NavigatorEvent, type NavigatorPhase, type NavigatorReport, type NavigatorSettlement, type NavigatorSubjectProvenance, type NavigatorTargetRole, type NavigatorWorkContext } from "./navigator-attendance.ts";
102
+ import { formatNavigatorReport, NAVIGATOR_EVENT_TYPE, NAVIGATOR_ROUTE_PLAYBOOK_FAILURE_ENTRY, navigatorSubjectKey, navigatorUnavailableError, subjectPath, type NavigatorAttendance, type NavigatorAttendanceOptions, type NavigatorEvent, type NavigatorPhase, type NavigatorReport, type NavigatorSettlement, type NavigatorSubjectProvenance, type NavigatorWorkContext } from "./navigator-attendance.ts";
103
+ import { loadNavigatorWorkBaseSuffix } from "./navigator-work-base.ts";
102
104
  import {
103
105
  buildNavigatorInfrastructureFailureFact,
104
106
  classifyPackagedRoleTerminalResult,
@@ -108,7 +110,7 @@ import {
108
110
  } from "./navigator-invocation-identity.ts";
109
111
  import { recordTypedProviderHttpStatus } from "./typed-provider-http.ts";
110
112
  import { NAVIGATOR_POST_ROLE_GRACE_MS, raceNavigatorGrace } from "./public-cli/settlement.ts";
111
- import { PACKAGED_ROLE_REGISTRY, isOfficerReviewSeat, packagedRoleActivationFlags, packagedRoleInputFlag, packagedRoleMetadata, packagedRoleOutputTool, packagedRolePhaseFlag, type PackagedRole } from "./packaged-role-registry.ts";
113
+ import { PACKAGED_ROLE_REGISTRY, isOfficerReviewSeat, packagedRoleActivationFlags, packagedRoleMetadata, packagedRoleOutputTool, packagedRolePhaseFlag, type PackagedRole } from "./packaged-role-registry.ts";
112
114
  import { isAuditEscalationProjection } from "./audit-escalation.ts";
113
115
  import {
114
116
  createJudgeRoleRuntime,
@@ -301,7 +303,6 @@ import {
301
303
  NOTARY_OUTPUT_TOOL,
302
304
  INSPECTOR_OUTPUT_TOOL,
303
305
  GatekeeperDecisionError,
304
- createGatekeeperOutputTool,
305
306
  runGatekeeper,
306
307
  gateOfficerForSubject,
307
308
  } from "./gatekeeper-role.ts";
@@ -309,12 +310,12 @@ export {
309
310
  NOTARY_OUTPUT_TOOL,
310
311
  INSPECTOR_OUTPUT_TOOL,
311
312
  GatekeeperDecisionError,
312
- createGatekeeperOutputTool,
313
313
  runGatekeeper,
314
314
  gateOfficerForSubject,
315
315
  };
316
316
  export type { GatekeeperResult, GatekeeperSubject, SubmissionGateNonPassResult, GateOfficer, RunGatekeeperOptions } from "./gatekeeper-role.ts";
317
- import { ParentQueueReaskError } from "./submission-errors.ts";
317
+ import { ParentQueueReaskError, unreadableDiscriminatorNotice } from "./submission-errors.ts";
318
+ import { REVIEW_QUEUE_STATUSES } from "./review-submission.ts";
318
319
 
319
320
  export {
320
321
  DOCTOR_EVIDENCE_TOOL_NAME,
@@ -451,29 +452,7 @@ export const ROLE_FLAG = {
451
452
  },
452
453
  } as const;
453
454
 
454
- /** Host-neutral in-process role help for Navigator prepare (Pi and Grok share this). */
455
- export function formatNavigatorRoleHelp(role: NavigatorTargetRole): string {
456
- const metadata = packagedRoleMetadata(role);
457
- const lines = [
458
- `Usage: ak-role ${role}`,
459
- ROLE_FLAG.definition.description,
460
- ];
461
- const inputFlag = packagedRoleInputFlag(role);
462
- if (inputFlag !== undefined) {
463
- lines.push(` --${inputFlag} <value> ${role} input material`);
464
- }
465
- const phaseFlag = packagedRolePhaseFlag(role);
466
- if (phaseFlag !== undefined && metadata !== undefined) {
467
- lines.push(
468
- ` --${phaseFlag} <value> ${role} phase: ${(metadata.phases.filter((p) => p !== null) as string[]).join(" | ")}`,
469
- );
470
- }
471
- lines.push(`Public next-command form: ak-role ${role}`);
472
- return lines.join("\n");
473
- }
474
-
475
- type NavigatorAttendanceDependency = Omit<NavigatorAttendance, "knownRoutePlaybookReadFailure"> &
476
- Partial<Pick<NavigatorAttendance, "knownRoutePlaybookReadFailure">>;
455
+ type NavigatorAttendanceDependency = NavigatorAttendance;
477
456
 
478
457
  export type RoleRuntimeDependencies = {
479
458
  /** Package root for packaged engine-note resolution (#879). */
@@ -753,7 +732,7 @@ export function createNavigatorRoleRuntime(
753
732
  roleHost: RoleHost,
754
733
  dependencies: NavigatorRuntimeDependencies,
755
734
  ) {
756
- return createFiledOfficerRuntime(
735
+ const base = createFiledOfficerRuntime(
757
736
  roleHost,
758
737
  {
759
738
  role: "navigator",
@@ -762,6 +741,43 @@ export function createNavigatorRoleRuntime(
762
741
  },
763
742
  dependencies,
764
743
  );
744
+ let playbookBound = false;
745
+ return {
746
+ async activate() {
747
+ await base.activate();
748
+ if (playbookBound) return;
749
+ playbookBound = true;
750
+ // Standing system prompt, not a per-turn user message. Native read failure
751
+ // is the explanation (ADR 0061); no code-written sentence.
752
+ const loadRoutePlaybook = dependencies.loadRoutePlaybook
753
+ ?? (() => readPackageMaterial("resources/navigator-route-playbook.md"));
754
+ let playbookRead: Promise<string> | undefined;
755
+ roleHost.on("before_agent_start", async (event) => {
756
+ const basePrompt = typeof event.systemPrompt === "string" ? event.systemPrompt : "";
757
+ const parts: string[] = [];
758
+ try {
759
+ playbookRead ??= loadRoutePlaybook();
760
+ const content = await playbookRead;
761
+ dependencies.recordRoutePlaybookReadFailure?.(undefined);
762
+ if (content.trim() !== "") parts.push(content);
763
+ } catch (error) {
764
+ const message = error instanceof Error ? error.message : String(error);
765
+ if (message.trim() !== "") {
766
+ dependencies.recordRoutePlaybookReadFailure?.(message);
767
+ parts.push(message);
768
+ }
769
+ }
770
+ const prompt = typeof event.prompt === "string" ? event.prompt : "";
771
+ const work = await loadNavigatorWorkBaseSuffix(prompt);
772
+ if (work !== undefined && work.trim() !== "") parts.push(work);
773
+ if (parts.length === 0) return;
774
+ const text = parts.join("\n\n");
775
+ return {
776
+ systemPrompt: basePrompt.trim() === "" ? text : `${basePrompt}\n\n${text}`,
777
+ };
778
+ });
779
+ },
780
+ };
765
781
  }
766
782
 
767
783
  /** #675: public 审刑院 seat on the shared filed-officer envelope. */
@@ -872,8 +888,6 @@ function readDiaristRunCoordinates(ctx: HostContext): {
872
888
  const DIARIST_BOUNDS_REASK =
873
889
  "边界无法使用。请重交 sessions:每卷 path + ranges,每端以原生 id 或本轮行号二选一指名。" as const;
874
890
  const DIARIST_ROUTING_STATUSES = new Set(["completed", "escalate"]);
875
- const DIARIST_STATUS_REASK =
876
- "status 不是 completed、escalate 之一。请重新交卷,status 写明其一。" as const;
877
891
 
878
892
  /**
879
893
  * #708 / #779 / #901: 起居郎 public seat on the shared filed-officer envelope.
@@ -900,7 +914,7 @@ export function createDiaristRoleRuntime(
900
914
  // before interpreting any other submitted field; caller-owned data stays open.
901
915
  const status = submitted?.status;
902
916
  if (typeof status !== "string" || !DIARIST_ROUTING_STATUSES.has(status)) {
903
- throw new ParentQueueReaskError(DIARIST_STATUS_REASK);
917
+ throw new ParentQueueReaskError(unreadableDiscriminatorNotice("status", submitted?.status));
904
918
  }
905
919
  if (status === "escalate") return parameters;
906
920
  const assertion = readDiaristTicketAssertion(submitted);
@@ -943,26 +957,9 @@ export function createDiaristRoleRuntime(
943
957
  );
944
958
  }
945
959
 
946
- /** Shared review-submission status words the queue reads (#1028). */
947
- const COUNTERSIGN_QUEUE_STATUSES = new Set(["converged", "continue", "escalate"]);
948
-
949
- /**
950
- * Plain-language re-ask when status is not a known queue word.
951
- * Back to countersign itself — notary is not summoned (#753).
952
- */
953
- const COUNTERSIGN_STATUS_REASK =
954
- "status 不是 converged、continue、escalate 三态之一。请重新交卷,status 写明其一。" as const;
955
-
956
- /** Secretariat status words the submission gate reads. */
960
+ /** Secretariat status words the submission gate reads. Not the review three-state field. */
957
961
  const SECRETARIAT_QUEUE_STATUSES = new Set(["converged", "escalate"]);
958
962
 
959
- /**
960
- * Plain-language re-ask when secretariatStatus is not a known queue word.
961
- * Back to secretariat itself — 给事中 is not summoned for escalation.
962
- */
963
- const SECRETARIAT_STATUS_REASK =
964
- "secretariatStatus 不是 converged、escalate 之一。请重新交卷,status 写明其一。" as const;
965
-
966
963
  /**
967
964
  * Secretariat output is terminal; converged submissions enter the shared
968
965
  * submission gate on every host that mounts gatekeeper actions.
@@ -989,7 +986,9 @@ export function createSecretariatRoleRuntime(
989
986
  ? record.secretariatStatus
990
987
  : undefined;
991
988
  if (status === undefined || !SECRETARIAT_QUEUE_STATUSES.has(status)) {
992
- throw new ParentQueueReaskError(SECRETARIAT_STATUS_REASK);
989
+ throw new ParentQueueReaskError(
990
+ unreadableDiscriminatorNotice("secretariatStatus", record?.secretariatStatus),
991
+ );
993
992
  }
994
993
  if (status === "escalate") {
995
994
  // Parent escalate → throw to caller as-is; countersign does not attend.
@@ -1132,10 +1131,10 @@ export function createCountersignRoleRuntime(
1132
1131
  record !== undefined && typeof record.status === "string"
1133
1132
  ? record.status
1134
1133
  : undefined;
1135
- if (status === undefined || !COUNTERSIGN_QUEUE_STATUSES.has(status)) {
1134
+ if (status === undefined || !REVIEW_QUEUE_STATUSES.has(status)) {
1136
1135
  // Parent status unreadable → back to countersign itself; do not summon notary,
1137
1136
  // do not forge an officer bounce face (#753).
1138
- throw new ParentQueueReaskError(COUNTERSIGN_STATUS_REASK);
1137
+ throw new ParentQueueReaskError(unreadableDiscriminatorNotice("status", record?.status));
1139
1138
  }
1140
1139
  if (status === "escalate") {
1141
1140
  // Parent escalate → throw to caller as-is; notary does not attend (#753).
@@ -1327,13 +1326,11 @@ export function createRoleRuntimeExtension(
1327
1326
  const raced = await raceNavigatorGrace(settlePromise, NAVIGATOR_POST_ROLE_GRACE_MS);
1328
1327
  if (raced.status !== "timeout") return;
1329
1328
  if (pendingNavigatorPresentation === undefined) {
1330
- const routePlaybookReadFailure = attendance.knownRoutePlaybookReadFailure?.();
1331
1329
  const report: NavigatorReport = {
1332
1330
  disposition: "unavailable",
1333
1331
  unavailableReason: "Navigator exceeded post-role delivery grace",
1334
1332
  unavailableSource: "unknown",
1335
1333
  unavailableCause: "unknown",
1336
- ...(routePlaybookReadFailure === undefined ? {} : { routePlaybookReadFailure }),
1337
1334
  };
1338
1335
  const event: NavigatorEvent = {
1339
1336
  version: 1,
@@ -1345,7 +1342,6 @@ export function createRoleRuntimeExtension(
1345
1342
  unavailableReason: "Navigator exceeded post-role delivery grace",
1346
1343
  unavailableSource: "unknown",
1347
1344
  unavailableCause: "unknown",
1348
- ...(routePlaybookReadFailure === undefined ? {} : { routePlaybookReadFailure }),
1349
1345
  };
1350
1346
  pendingNavigatorPresentation = { event, report };
1351
1347
  }
@@ -1616,7 +1612,7 @@ export function createRoleRuntimeExtension(
1616
1612
  } catch {}
1617
1613
  envelopeHost.sendMessage({
1618
1614
  customType: "ak-receipt-delivery-prompt",
1619
- content: RECEIPT_DELIVERY_PROMPT,
1615
+ content: JSON.stringify(receiptDelivery.deliveryState()),
1620
1616
  display: false,
1621
1617
  }, { triggerTurn: true, deliverAs: "followUp" });
1622
1618
  } else if (receiptDelivery.nextAction() === "no-receipt" && !noReceiptRecorded) {
@@ -1789,6 +1785,11 @@ export function createRoleRuntimeExtension(
1789
1785
  });
1790
1786
  const navigator = createNavigatorRoleRuntime(roleHost, {
1791
1787
  loadSoul: () => requireRoleSoul("navigator"),
1788
+ recordRoutePlaybookReadFailure: (message) => {
1789
+ envelopeHost.appendEntry(NAVIGATOR_ROUTE_PLAYBOOK_FAILURE_ENTRY, {
1790
+ message: message ?? "",
1791
+ });
1792
+ },
1792
1793
  });
1793
1794
  const auditor = createAuditorRoleRuntime(roleHost, {
1794
1795
  loadSoul: () => requireRoleSoul("auditor"),
@@ -2175,11 +2176,8 @@ export function createRoleRuntimeExtension(
2175
2176
  pendingNavigatorPresentation = { event: navigatorEvent, report };
2176
2177
  },
2177
2178
  });
2178
- // Warm live help during activation so prepare is not help-bound under load.
2179
- // Concrete work context also starts full preparation so session create
2180
- // overlaps the role run. Placeholder subjects wait for before_agent_start
2181
- // (user prompt may replace the subject key) but still inherit warm help.
2182
- navigatorAttendance.warmHelp?.();
2179
+ // Concrete work context starts standby attendance (record only, no model).
2180
+ // Placeholder subjects wait for before_agent_start (user prompt may replace the subject key).
2183
2181
  if (
2184
2182
  navigatorWorkContext.contextError === undefined &&
2185
2183
  navigatorWorkContext.subjectProvenance !== "placeholder"
@@ -12,12 +12,12 @@ import { SECRETARIAT_OUTPUT_TOOL_NAME, type SecretariatVerdict } from "./secreta
12
12
  export { SECRETARIAT_OUTPUT_TOOL_NAME } from "./secretariat-contracts.ts";
13
13
  export type { SecretariatVerdict };
14
14
 
15
- /** 中书省终局回执形状;形状指引,非 schema 闸。 */
15
+ /** 中书省终局回执形状。 */
16
16
  export const secretariatVerdictSchema = withTerminatingOutputDeclarations(
17
17
  Type.Object(
18
18
  {
19
19
  secretariatStatus: Type.Unknown({
20
- description: "converged | escalate(终局形状指引,非闸)",
20
+ description: "converged | escalate",
21
21
  }),
22
22
  ticketNumber: Type.Optional(
23
23
  Type.Number({ description: "本票号;署时指向最终正文所在票" }),
@@ -44,7 +44,7 @@ export type SecretariatVerdictParameters = Static<typeof secretariatVerdictSchem
44
44
  export const SECRETARIAT_OUTPUT_TOOL_SPEC = {
45
45
  name: SECRETARIAT_OUTPUT_TOOL_NAME,
46
46
  label: "中书省输出",
47
- description: "中书省终局回执(署或上呈);给事中封驳须改票重送,不在此终局。",
47
+ description: "中书省终局回执。",
48
48
  promptSnippet: "中书省终局回执",
49
49
  parameters: secretariatVerdictSchema,
50
50
  } as const;
@@ -58,6 +58,22 @@ export class WorkerUnfinishedReasonReminderError extends Error {
58
58
  }
59
59
  }
60
60
 
61
+ /**
62
+ * Host constraint missed a routing discriminator (#1055).
63
+ * Names live only in the submission schema. This notice carries the field and the received value.
64
+ */
65
+ export function unreadableDiscriminatorNotice(field: string, received: unknown): string {
66
+ if (received === undefined) return `读不出 ${field}`;
67
+ return `读不出 ${field}:${readableGateItem(received)}`;
68
+ }
69
+
70
+ /** The received discriminator only — never the rest of the receipt. */
71
+ export function receivedDiscriminator(receipt: unknown, field: string): unknown {
72
+ if (receipt === null || typeof receipt !== "object" || Array.isArray(receipt)) return undefined;
73
+ if (!Object.hasOwn(receipt, field)) return undefined;
74
+ return (receipt as Record<string, unknown>)[field];
75
+ }
76
+
61
77
  /**
62
78
  * Parent seat status unreadable for queueing (#753).
63
79
  * Correctable back to the parent itself — not a gate officer bounce, no officer field.
@@ -27,6 +27,7 @@ import {
27
27
  type GateOfficerSummon,
28
28
  } from "./gatekeeper-role.ts";
29
29
  import type { PublicSummonResult } from "./public-role-summons.ts";
30
+
30
31
  import { sessionFileFromPublicSummon } from "./session-assistant-usage.ts";
31
32
 
32
33
  /**
@@ -178,7 +179,7 @@ export async function requireSubmissionGate(options: {
178
179
  };
179
180
  }
180
181
  if (gatekeeper.status === "needs_reask") {
181
- reask = officerConclusionReask(projected.officer);
182
+ reask = officerConclusionReask(gatekeeper.receivedStatus);
182
183
  continue;
183
184
  }
184
185
  if (gatekeeper.status === "transport_failure") {
@@ -20,7 +20,8 @@ import type { SitianRecord } from "./sitian-contracts.ts";
20
20
  import { findRunDirectoryById } from "./public-cli/run-lifecycle.ts";
21
21
  import type { TerminalRoleName } from "./public-cli/terminal.ts";
22
22
  import { isCorrectableExecuteError } from "./submission-correctable-error.ts";
23
- import { failOnInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.ts";
23
+ import { failOnInfrastructureFailureDeclaration, infrastructureFailureDiagnostic } from "./package-contracts/terminating-infrastructure.ts";
24
+ import { REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.ts";
24
25
 
25
26
  export type SubmissionCall = { readonly id: string; readonly name: string };
26
27
  export type SubmissionOutcomeKind = "correctable-rejection" | "audit-escalation" | "infrastructure";
@@ -678,17 +679,28 @@ export function createSubmissionLedgerHost(
678
679
  });
679
680
  let result: HostToolResult<unknown>;
680
681
  try {
681
- failOnInfrastructureFailureDeclaration(
682
- params,
683
- {
684
- failInfrastructure(error, ctx) {
685
- failInfrastructure(error, ctx);
682
+ // A review escalate that carries the failure declaration is the
683
+ // seat's own submission. Every other declaration stays on the
684
+ // host-failure seam. Tool identity comes from the registry map.
685
+ const reviewEscalate = tool.name === REVIEW_SUBMISSION_OUTPUT_TOOL_NAME
686
+ && infrastructureFailureDiagnostic(params) !== undefined
687
+ && params !== null
688
+ && typeof params === "object"
689
+ && !Array.isArray(params)
690
+ && (params as Record<string, unknown>).status === "escalate";
691
+ if (!reviewEscalate) {
692
+ failOnInfrastructureFailureDeclaration(
693
+ params,
694
+ {
695
+ failInfrastructure(error, ctx) {
696
+ failInfrastructure(error, ctx);
697
+ },
686
698
  },
687
- },
688
- context,
689
- toolCallId,
690
- tool.bounceInfrastructureDeclaration,
691
- );
699
+ context,
700
+ toolCallId,
701
+ tool.bounceInfrastructureDeclaration,
702
+ );
703
+ }
692
704
  result = await tool.execute(toolCallId, params, signal, update, context);
693
705
  } catch (error) {
694
706
  if (isCorrectableExecuteError(error)) {
@@ -5,7 +5,7 @@ import { withTerminatingOutputDeclarations } from "./package-contracts/terminati
5
5
 
6
6
  import { readableGateItem } from "./readable-gate-item.ts";
7
7
  import { projectGatekeeperEscalation } from "./audit-escalation.ts";
8
- import { GatekeeperDecisionError, ParentQueueReaskError } from "./submission-errors.ts";
8
+ import { GatekeeperDecisionError, ParentQueueReaskError, unreadableDiscriminatorNotice } from "./submission-errors.ts";
9
9
  import {
10
10
  CODER_OUTPUT_TOOL_NAME,
11
11
  FIXER_OUTPUT_TOOL_NAME,
@@ -44,11 +44,11 @@ export type { WorkerOutput };
44
44
  // no code branches on their length. `reason` alone keeps minLength: the worker
45
45
  // gate reads `reason.trim().length > 0` to pick typed-reminder-bounce vs accept
46
46
  // (src/worker-submission-gates.ts:159-163,297-302).
47
- // #836 (ADR 0003 Amendment): status kept open like countersignStatus
48
- // (src/countersign-role.ts) — one shared description across every variant
47
+ // Worker status is not the review three-state field (#1055): schema stays open.
48
+ // One shared description across every variant
49
49
  // so openToolObjectFromUnion's identical-declaration collapse drops none of it.
50
50
  const CODER_STATUS_DESCRIPTION =
51
- "planned | completed | refused | partially_completed | unfinished — 形状指引,非 schema 闸;completed 回执含 TDD、同模式、引入回归、行为事实四项证据;unfinished 缺前置或违宪约束致本局未完成时可用,缺待决 owner 决定或答复属缺前置。" as const;
51
+ "planned | completed | refused | partially_completed | unfinished。unfinished:缺前置或违宪约束致本局未完成。" as const;
52
52
  const coderOutputVariants = Type.Union([
53
53
  Type.Object({
54
54
  status: Type.Unknown({ description: CODER_STATUS_DESCRIPTION }),
@@ -135,8 +135,6 @@ const WORKER_ROUTING_STATUSES = new Set([
135
135
  "partially_completed",
136
136
  "unfinished",
137
137
  ]);
138
- const WORKER_STATUS_REASK =
139
- "status 不是 planned、completed、refused、partially_completed、unfinished 之一。请重新交卷,status 写明其一。" as const;
140
138
 
141
139
  /** Read only the field that selects the worker's next package-owned path. */
142
140
  function workerStatusOf(output: WorkerOutput): string | undefined {
@@ -148,7 +146,7 @@ function workerStatusOf(output: WorkerOutput): string | undefined {
148
146
  function requireReadableWorkerStatus(output: WorkerOutput): string {
149
147
  const status = workerStatusOf(output);
150
148
  if (status === undefined || !WORKER_ROUTING_STATUSES.has(status)) {
151
- throw new ParentQueueReaskError(WORKER_STATUS_REASK);
149
+ throw new ParentQueueReaskError(unreadableDiscriminatorNotice("status", isRecord(output) ? output.status : undefined));
152
150
  }
153
151
  return status;
154
152
  }
@@ -256,7 +254,7 @@ export function createFixerRoleRuntime(
256
254
  pi.registerTool({
257
255
  name: FIXER_OUTPUT_TOOL_NAME,
258
256
  label: "修内司输出",
259
- description: "提交修内司终局回执;基础设施失败走 abort,不经本工具。",
257
+ description: "提交修内司终局回执。",
260
258
  promptSnippet: "提交修内司终局回执",
261
259
  parameters: fixerOutputSchema,
262
260
  async execute(toolCallId: string, parameters: unknown, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext): Promise<HostToolResult<unknown>> {
@@ -383,7 +381,7 @@ export function createCoderRoleRuntime(
383
381
  pi.registerTool({
384
382
  name: CODER_OUTPUT_TOOL_NAME,
385
383
  label: "将作监输出",
386
- description: "提交将作监终局回执;本工具无 escalate 通道。",
384
+ description: "提交将作监终局回执。",
387
385
  promptSnippet: "提交将作监终局回执",
388
386
  parameters: coderOutputSchema,
389
387
  async execute(toolCallId: string, parameters: unknown, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext): Promise<HostToolResult<unknown>> {