@akagilnc/pi-workflow-roles 0.1.3932 → 0.1.3975

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 (162) hide show
  1. package/CLAUDE.md +1 -1
  2. package/dist/acp-host/production-host.js +2389 -6018
  3. package/dist/analyst-gate-cycles-read.js +15 -19
  4. package/dist/audit-escalation.js +16 -46
  5. package/dist/auditor-dossier-tool.js +7 -62
  6. package/dist/collector-identity.js +0 -157
  7. package/dist/collector-ledger.js +2 -42
  8. package/dist/compliance-transport.js +40 -14
  9. package/dist/diarist-contracts.js +2 -40
  10. package/dist/doctor-contracts.js +39 -108
  11. package/dist/doctor-evidence.js +1 -5
  12. package/dist/engine-detour-tool.js +1 -1
  13. package/dist/gatekeeper-role.js +49 -38
  14. package/dist/headless-host/production-host.js +4814 -8450
  15. package/dist/host-contracts.js +0 -5
  16. package/dist/merger-contracts.js +4 -6
  17. package/dist/navigator-attendance.js +30 -52
  18. package/dist/navigator-invocation-identity.js +0 -1
  19. package/dist/navigator-public-session.js +2 -1
  20. package/dist/notary-contracts.js +4 -10
  21. package/dist/package-contracts/auditor-output.js +5 -23
  22. package/dist/package-contracts/fixer-output.js +25 -49
  23. package/dist/package-contracts/gatekeeper-output.js +3 -13
  24. package/dist/package-contracts/navigator-output.js +4 -7
  25. package/dist/package-contracts/reviewer-output.js +1 -95
  26. package/dist/package-contracts/terminating-tools.js +33 -144
  27. package/dist/package-contracts/worker-output.js +1 -3
  28. package/dist/public-cli/auto-resume.js +30 -68
  29. package/dist/public-cli/case-dossier-delivery.js +5 -17
  30. package/dist/public-cli/diarist-run.js +9 -4
  31. package/dist/public-cli/inspector-run.js +2 -8
  32. package/dist/public-cli/instruction-seat-run.js +3 -7
  33. package/dist/public-cli/invocation.js +5 -2
  34. package/dist/public-cli/main.js +6068 -6964
  35. package/dist/public-cli/notary-run.js +1 -8
  36. package/dist/public-cli/post-admission.js +314 -113
  37. package/dist/public-cli/run-lifecycle.js +14 -38
  38. package/dist/public-cli/settlement.js +209 -390
  39. package/dist/public-cli/terminal.js +51 -45
  40. package/dist/public-role-summons.js +1 -12
  41. package/dist/reviewer-dispatch.js +12 -258
  42. package/dist/submission-correctable-error.js +0 -12
  43. package/dist/submission-errors.js +6 -1
  44. package/dist/submission-ledger.js +125 -168
  45. package/dist/ticket-provenance-contracts.js +2 -49
  46. package/dist/ticket-provenance.js +82 -31
  47. package/extensions/role-runtime.ts +1 -6
  48. package/package.json +1 -1
  49. package/resources/836-deleted-machine-instruction-inventory.md +18 -0
  50. package/src/acp-host/role-turn-host.ts +8 -3
  51. package/src/analyst-gate-cycles-read.ts +17 -29
  52. package/src/audit-escalation.ts +20 -50
  53. package/src/auditor-dossier-tool.ts +5 -103
  54. package/src/canonical-skill-binding.ts +3 -2
  55. package/src/collector-identity.ts +0 -179
  56. package/src/collector-ledger.ts +3 -46
  57. package/src/collector-role.ts +32 -61
  58. package/src/collector-tool-schemas.ts +20 -14
  59. package/src/compliance-transport.ts +55 -17
  60. package/src/countersign-role.ts +17 -8
  61. package/src/diarist-contracts.ts +3 -36
  62. package/src/diarist-role.ts +13 -7
  63. package/src/diarist.ts +4 -16
  64. package/src/doctor-contracts.ts +73 -94
  65. package/src/doctor-evidence.ts +1 -1
  66. package/src/doctor-role.ts +3 -3
  67. package/src/engine-detour-tool.ts +4 -1
  68. package/src/external-host-turn-loop.ts +9 -3
  69. package/src/gatekeeper-pass-envelope.ts +7 -12
  70. package/src/gatekeeper-role.ts +56 -50
  71. package/src/gleaner-left-role.ts +4 -2
  72. package/src/headless-host/role-turn-host.ts +4 -5
  73. package/src/host-contracts.ts +3 -8
  74. package/src/judge-role.ts +19 -13
  75. package/src/merger-contracts.ts +10 -6
  76. package/src/merger-role.ts +1 -5
  77. package/src/navigator-attendance.ts +37 -74
  78. package/src/navigator-invocation-identity.ts +0 -6
  79. package/src/navigator-public-session.ts +2 -1
  80. package/src/notary-contracts.ts +7 -10
  81. package/src/package-contracts/auditor-output.ts +4 -21
  82. package/src/package-contracts/fixer-output.ts +34 -52
  83. package/src/package-contracts/gatekeeper-output.ts +5 -22
  84. package/src/package-contracts/navigator-output.ts +7 -7
  85. package/src/package-contracts/reviewer-output.ts +2 -150
  86. package/src/package-contracts/terminating-tools.ts +32 -147
  87. package/src/package-contracts/worker-output.ts +0 -1
  88. package/src/pi/adapter.ts +3 -8
  89. package/src/public-cli/auto-resume.ts +41 -94
  90. package/src/public-cli/case-dossier-delivery.ts +5 -15
  91. package/src/public-cli/countersign-run.ts +14 -6
  92. package/src/public-cli/diarist-run.ts +11 -9
  93. package/src/public-cli/inspector-run.ts +2 -18
  94. package/src/public-cli/instruction-seat-run.ts +3 -17
  95. package/src/public-cli/invocation.ts +5 -2
  96. package/src/public-cli/merger-run.ts +1 -1
  97. package/src/public-cli/notary-run.ts +1 -18
  98. package/src/public-cli/post-admission.ts +385 -115
  99. package/src/public-cli/run-lifecycle.ts +13 -44
  100. package/src/public-cli/settlement.ts +314 -562
  101. package/src/public-cli/terminal.ts +66 -72
  102. package/src/public-role-summons.ts +5 -30
  103. package/src/reviewer-dispatch.ts +29 -331
  104. package/src/reviewer-role.ts +16 -83
  105. package/src/role-envelope.ts +16 -24
  106. package/src/role-runtime-dependencies.ts +1 -6
  107. package/src/role-runtime.ts +65 -201
  108. package/src/submission-correctable-error.ts +0 -14
  109. package/src/submission-errors.ts +6 -1
  110. package/src/submission-ledger.ts +187 -187
  111. package/src/ticket-provenance-contracts.ts +4 -60
  112. package/src/ticket-provenance.ts +98 -32
  113. package/src/worker-role.ts +35 -36
  114. package/dist/activation-ledger-session.js +0 -105
  115. package/dist/activation-ledger.js +0 -139
  116. package/dist/activation-trace.js +0 -33
  117. package/dist/atomic-write.js +0 -23
  118. package/dist/auditor-role.js +0 -12
  119. package/dist/canonical-skill-binding.js +0 -61
  120. package/dist/collector-handbook.js +0 -142
  121. package/dist/collector-receipt.js +0 -107
  122. package/dist/collector-role.js +0 -566
  123. package/dist/collector-tool-schemas.js +0 -107
  124. package/dist/countersign-role.js +0 -28
  125. package/dist/diarist-role.js +0 -44
  126. package/dist/diarist.js +0 -66
  127. package/dist/doctor-role.js +0 -75
  128. package/dist/fixer-bash-seatbelt.js +0 -13
  129. package/dist/gatekeeper-pass-envelope.js +0 -77
  130. package/dist/gleaner-left-role.js +0 -26
  131. package/dist/inspector-role.js +0 -29
  132. package/dist/judge-role.js +0 -104
  133. package/dist/merger-role.js +0 -55
  134. package/dist/navigator-role.js +0 -12
  135. package/dist/notary-role.js +0 -134
  136. package/dist/oauth-keepalive.js +0 -158
  137. package/dist/pi/adapter.js +0 -223
  138. package/dist/reviewer-agent.js +0 -122
  139. package/dist/reviewer-child-executor.js +0 -194
  140. package/dist/reviewer-construction.js +0 -103
  141. package/dist/reviewer-execution-ledger.js +0 -166
  142. package/dist/reviewer-failure-diagnostic.js +0 -17
  143. package/dist/reviewer-role.js +0 -159
  144. package/dist/reviewer-scope-prompt.js +0 -21
  145. package/dist/reviewer-settlement.js +0 -66
  146. package/dist/reviewer-workspace.js +0 -132
  147. package/dist/role-runtime.js +0 -1604
  148. package/dist/stderr-jsonl.js +0 -26
  149. package/dist/tool-execution-observation.js +0 -141
  150. package/dist/worker-role.js +0 -345
  151. package/dist/worker-submission-gates.js +0 -305
  152. package/src/collector-receipt.ts +0 -140
  153. package/src/diagnostic-tail.ts +0 -18
  154. package/src/reviewer-agent.ts +0 -135
  155. package/src/reviewer-child-executor.ts +0 -252
  156. package/src/reviewer-construction.ts +0 -195
  157. package/src/reviewer-execution-ledger.ts +0 -212
  158. package/src/reviewer-failure-diagnostic.ts +0 -18
  159. package/src/reviewer-scope-prompt.ts +0 -24
  160. package/src/reviewer-settlement.ts +0 -74
  161. package/src/reviewer-workspace.ts +0 -105
  162. package/src/shape-unreadable-failure.ts +0 -49
@@ -4,8 +4,7 @@
4
4
  */
5
5
  import { COLLECTOR_ACCEPTED_TEXT, COLLECTOR_OUTPUT_TOOL, validateAcceptedCollectorReceipt, } from "./collector-output.js";
6
6
  import { JUDGE_ACCEPTED_TEXT, JUDGE_OUTPUT_TOOL_NAME, validateAcceptedJudgeDetails, } from "./judge-output.js";
7
- import { REVIEWER_ACCEPTED_TEXT, REVIEWER_OUTPUT_TOOL_NAME, projectReviewerIntentToReceipt, validateReviewerIntent, validateRuntimeReviewerReceipt, } from "./reviewer-output.js";
8
- import { isAuditEscalationResult } from "../audit-escalation.js";
7
+ import { REVIEWER_ACCEPTED_TEXT, REVIEWER_OUTPUT_TOOL_NAME, validateReviewerIntent, } from "./reviewer-output.js";
9
8
  import { CorrectableSubmissionError } from "../submission-correctable-error.js";
10
9
  import { DOCTOR_ACCEPTED_TEXT, DOCTOR_OUTPUT_TOOL_NAME, validateDoctorSubmissionShape, validateRecordedDoctorOutput } from "../doctor-contracts.js";
11
10
  import { GATEKEEPER_ACCEPTED_TEXT, GATEKEEPER_OUTPUT_TOOL_NAME, validateRecordedGatekeeperOutput } from "./gatekeeper-output.js";
@@ -18,7 +17,7 @@ import { GLEANER_LEFT_ACCEPTED_TEXT, GLEANER_LEFT_OUTPUT_TOOL_NAME, validateReco
18
17
  import { INSPECTOR_ACCEPTED_TEXT, INSPECTOR_OUTPUT_TOOL_NAME, validateRecordedInspectorOutput } from "../inspector-contracts.js";
19
18
  import { DIARIST_ACCEPTED_TEXT, DIARIST_OUTPUT_TOOL_NAME, validateRecordedDiaristOutput } from "../diarist-contracts.js";
20
19
  import { CODER_ACCEPTED_TEXT, CODER_OUTPUT_TOOL_NAME, FIXER_ACCEPTED_TEXT, FIXER_OUTPUT_TOOL_NAME, validateAcceptedWorkerDetails, } from "./worker-output.js";
21
- export { CODER_ACCEPTED_TEXT, CODER_OUTPUT_TOOL_NAME, COLLECTOR_ACCEPTED_TEXT, COLLECTOR_OUTPUT_TOOL, FIXER_ACCEPTED_TEXT, FIXER_OUTPUT_TOOL_NAME, JUDGE_ACCEPTED_TEXT, JUDGE_OUTPUT_TOOL_NAME, REVIEWER_ACCEPTED_TEXT, REVIEWER_OUTPUT_TOOL_NAME, MERGER_ACCEPTED_TEXT, MERGER_OUTPUT_TOOL_NAME, validateAcceptedCollectorReceipt, validateAcceptedJudgeDetails, projectReviewerIntentToReceipt, validateReviewerIntent, validateRuntimeReviewerReceipt, validateAcceptedWorkerDetails, validateDoctorSubmissionShape, validateRecordedDoctorOutput, validateMergerOutput, validateRecordedNotaryOutput, validateRecordedCountersignOutput, validateRecordedGleanerLeftOutput, validateRecordedInspectorOutput, validateRecordedGatekeeperOutput, validateRecordedNavigatorOutput, validateRecordedAuditorOutput, validateRecordedDiaristOutput, };
20
+ export { CODER_ACCEPTED_TEXT, CODER_OUTPUT_TOOL_NAME, COLLECTOR_ACCEPTED_TEXT, COLLECTOR_OUTPUT_TOOL, FIXER_ACCEPTED_TEXT, FIXER_OUTPUT_TOOL_NAME, JUDGE_ACCEPTED_TEXT, JUDGE_OUTPUT_TOOL_NAME, REVIEWER_ACCEPTED_TEXT, REVIEWER_OUTPUT_TOOL_NAME, MERGER_ACCEPTED_TEXT, MERGER_OUTPUT_TOOL_NAME, validateAcceptedCollectorReceipt, validateAcceptedJudgeDetails, validateReviewerIntent, validateAcceptedWorkerDetails, validateDoctorSubmissionShape, validateRecordedDoctorOutput, validateMergerOutput, validateRecordedNotaryOutput, validateRecordedCountersignOutput, validateRecordedGleanerLeftOutput, validateRecordedInspectorOutput, validateRecordedGatekeeperOutput, validateRecordedNavigatorOutput, validateRecordedAuditorOutput, validateRecordedDiaristOutput, };
22
21
  export const TERMINATING_TOOL_NAMES = [
23
22
  CODER_OUTPUT_TOOL_NAME,
24
23
  FIXER_OUTPUT_TOOL_NAME,
@@ -80,155 +79,45 @@ export class AcceptedDetailsContractError extends CorrectableSubmissionError {
80
79
  this.name = "AcceptedDetailsContractError";
81
80
  }
82
81
  }
83
- function safeProperty(candidate, property) {
84
- try {
85
- return candidate?.[property];
86
- }
87
- catch {
88
- return undefined;
89
- }
90
- }
91
- export function validateAcceptedDetails(toolName, details) {
92
- const candidate = details !== null && typeof details === "object" && !Array.isArray(details)
93
- ? details
94
- : undefined;
95
- let auditEscalation = false;
96
- try {
97
- auditEscalation = isAuditEscalationResult(details);
98
- }
99
- catch {
100
- // Hostile getters are not recognizable audit escalation evidence.
101
- }
102
- if (auditEscalation || safeProperty(candidate, "kind") === "audit_escalation") {
103
- throw new AcceptedDetailsContractError("audit escalation is not an accepted role receipt");
104
- }
105
- const statusKey = toolName === JUDGE_OUTPUT_TOOL_NAME
106
- ? "judgeStatus"
107
- : toolName === COUNTERSIGN_OUTPUT_TOOL_NAME
108
- ? "countersignStatus"
109
- : "status";
110
- const discriminator = safeProperty(candidate, statusKey);
111
- const lawfulStatuses = {
112
- [CODER_OUTPUT_TOOL_NAME]: ["planned", "completed", "refused", "unfinished"],
113
- [FIXER_OUTPUT_TOOL_NAME]: ["planned", "completed", "refused", "partially_completed", "unfinished"],
114
- [REVIEWER_OUTPUT_TOOL_NAME]: ["completed", "refused"],
115
- [JUDGE_OUTPUT_TOOL_NAME]: ["converged", "continue", "escalate"],
116
- [COLLECTOR_OUTPUT_TOOL]: [],
117
- [DOCTOR_OUTPUT_TOOL_NAME]: ["completed", "refused"],
118
- [MERGER_OUTPUT_TOOL_NAME]: ["completed", "escalate"],
119
- [NOTARY_OUTPUT_TOOL_NAME]: ["pass", "bounce", "escalate"],
120
- [COUNTERSIGN_OUTPUT_TOOL_NAME]: ["converged", "continue", "escalate"],
121
- [GLEANER_LEFT_OUTPUT_TOOL_NAME]: ["completed"],
122
- [INSPECTOR_OUTPUT_TOOL_NAME]: ["pass", "bounce", "escalate"],
123
- [GATEKEEPER_OUTPUT_TOOL_NAME]: ["dispatch", "pass"],
124
- [NAVIGATOR_OUTPUT_TOOL_NAME]: ["advice"],
125
- [AUDITOR_OUTPUT_TOOL_NAME]: ["pass", "bounce", "escalate"],
126
- [DIARIST_OUTPUT_TOOL_NAME]: ["completed", "escalate"],
127
- };
128
- const collectorDiscriminator = toolName === COLLECTOR_OUTPUT_TOOL && Array.isArray(candidate?.groups);
129
- const baseDiscriminator = discriminator;
130
- const runtimeBindingMissing = (toolName === DOCTOR_OUTPUT_TOOL_NAME && baseDiscriminator === "completed" && !(candidate?.cost !== null && typeof candidate?.cost === "object")) ||
131
- (toolName === REVIEWER_OUTPUT_TOOL_NAME && candidate?.version !== 2);
132
- if (runtimeBindingMissing ||
133
- (!collectorDiscriminator && (typeof discriminator !== "string" || !lawfulStatuses[toolName].includes(baseDiscriminator)))) {
134
- throw new AcceptedDetailsContractError("terminating receipt has no recognized execution discriminator");
135
- }
136
- try {
137
- switch (toolName) {
138
- case CODER_OUTPUT_TOOL_NAME:
139
- return validateAcceptedWorkerDetails(details, "Coder");
140
- case FIXER_OUTPUT_TOOL_NAME:
141
- return validateAcceptedWorkerDetails(details, "Fixer");
142
- case REVIEWER_OUTPUT_TOOL_NAME:
143
- return validateRuntimeReviewerReceipt(details);
144
- case JUDGE_OUTPUT_TOOL_NAME:
145
- return validateAcceptedJudgeDetails(details);
146
- case COLLECTOR_OUTPUT_TOOL:
147
- return validateAcceptedCollectorReceipt(details);
148
- case DOCTOR_OUTPUT_TOOL_NAME:
149
- return validateRecordedDoctorOutput(details);
150
- case MERGER_OUTPUT_TOOL_NAME:
151
- return validateMergerOutput(details);
152
- case NOTARY_OUTPUT_TOOL_NAME:
153
- return validateRecordedNotaryOutput(details);
154
- case COUNTERSIGN_OUTPUT_TOOL_NAME:
155
- return validateRecordedCountersignOutput(details);
156
- case GLEANER_LEFT_OUTPUT_TOOL_NAME:
157
- return validateRecordedGleanerLeftOutput(details);
158
- case INSPECTOR_OUTPUT_TOOL_NAME:
159
- return validateRecordedInspectorOutput(details);
160
- case GATEKEEPER_OUTPUT_TOOL_NAME:
161
- return validateRecordedGatekeeperOutput(details);
162
- case NAVIGATOR_OUTPUT_TOOL_NAME:
163
- return validateRecordedNavigatorOutput(details);
164
- case AUDITOR_OUTPUT_TOOL_NAME:
165
- return validateRecordedAuditorOutput(details);
166
- case DIARIST_OUTPUT_TOOL_NAME:
167
- return validateRecordedDiaristOutput(details);
168
- }
169
- }
170
- catch (error) {
171
- if (error instanceof Error && error.constructor === Error)
172
- throw new AcceptedDetailsContractError(error.message, { cause: error });
173
- throw error;
174
- }
175
- }
176
- export function validateAcceptedLifecycle(toolName, argumentsValue, detailsValue) {
177
- const details = validateAcceptedDetails(toolName, detailsValue);
178
- if (toolName === DOCTOR_OUTPUT_TOOL_NAME) {
179
- const testimony = validateDoctorSubmissionShape(argumentsValue);
180
- if ((String(testimony.status)) === "refused") {
181
- if (!deepEqual(testimony, details))
182
- throw new Error("accepted tool lifecycle details mismatch");
183
- return details;
184
- }
185
- const receipt = details;
186
- if ((String(receipt.status)) !== "completed") {
187
- throw new Error("accepted tool lifecycle details mismatch");
188
- }
189
- const { cost: _runtimeCost, ...projected } = receipt;
190
- if (!deepEqual(testimony, projected))
191
- throw new Error("accepted tool lifecycle details mismatch");
192
- return details;
193
- }
194
- if (toolName === DIARIST_OUTPUT_TOOL_NAME) {
195
- // Envelope-owned mechanical sitian facts are runtime-bound on details only
196
- // (same shape as the Doctor runtime cost); the submitted arguments carry the
197
- // role's own entries. Machine facts never come from model self-report.
198
- const { sitian: _mechanical, ...submitted } = details;
199
- const testimony = validateAcceptedDetails(toolName, argumentsValue);
200
- if (!deepEqual(testimony, submitted))
201
- throw new Error("accepted tool lifecycle details mismatch");
82
+ /**
83
+ * #836: status allowlist rejection deleted. Original object is the receipt.
84
+ * Callers that need a typed view may still cast; code must not bounce on status.
85
+ */
86
+ export function validateAcceptedDetails(_toolName, details) {
87
+ if (details !== null && typeof details === "object" && !Array.isArray(details)) {
202
88
  return details;
203
89
  }
204
- const argumentsDetails = validateAcceptedDetails(toolName, argumentsValue);
205
- if (!deepEqual(argumentsDetails, details))
206
- throw new Error("accepted tool lifecycle details mismatch");
207
- return details;
90
+ // Non-object payload still records as empty object rather than rejecting.
91
+ return {};
92
+ }
93
+ /**
94
+ * #836: args↔details equality rejection deleted. Details are recorded as submitted.
95
+ */
96
+ export function validateAcceptedLifecycle(toolName, _argumentsValue, detailsValue) {
97
+ return validateAcceptedDetails(toolName, detailsValue);
208
98
  }
99
+ /** Read status/commit leaves the role wrote — never invent defaults (#836). */
209
100
  export function acceptedFacts(toolName, details) {
101
+ const record = details;
210
102
  switch (toolName) {
211
- case CODER_OUTPUT_TOOL_NAME:
212
- case FIXER_OUTPUT_TOOL_NAME:
213
- case REVIEWER_OUTPUT_TOOL_NAME:
214
- case DOCTOR_OUTPUT_TOOL_NAME:
215
- case NOTARY_OUTPUT_TOOL_NAME:
216
- case GLEANER_LEFT_OUTPUT_TOOL_NAME:
217
- case INSPECTOR_OUTPUT_TOOL_NAME:
218
- case GATEKEEPER_OUTPUT_TOOL_NAME:
219
- case NAVIGATOR_OUTPUT_TOOL_NAME:
220
- case AUDITOR_OUTPUT_TOOL_NAME:
221
- return { status: details.status };
222
- case DIARIST_OUTPUT_TOOL_NAME: return { status: details.status };
223
- case JUDGE_OUTPUT_TOOL_NAME: return { status: details.judgeStatus };
224
- case COUNTERSIGN_OUTPUT_TOOL_NAME: return { status: details.countersignStatus };
103
+ case JUDGE_OUTPUT_TOOL_NAME:
104
+ return typeof record.judgeStatus === "string" ? { status: record.judgeStatus } : {};
105
+ case COUNTERSIGN_OUTPUT_TOOL_NAME:
106
+ return typeof record.countersignStatus === "string" ? { status: record.countersignStatus } : {};
225
107
  case MERGER_OUTPUT_TOOL_NAME: {
226
- const output = details;
227
- const status = output.status;
228
- return { status, ...(status === "completed" && typeof output.mergeCommitId === "string" ? { commit: output.mergeCommitId } : {}) };
108
+ const status = typeof record.status === "string" ? record.status : undefined;
109
+ return {
110
+ ...(status === undefined ? {} : { status }),
111
+ ...(status === "completed" && typeof record.mergeCommitId === "string"
112
+ ? { commit: record.mergeCommitId }
113
+ : {}),
114
+ };
229
115
  }
230
116
  case COLLECTOR_OUTPUT_TOOL:
231
- return { status: "collected" };
117
+ // Collector has no status leaf — do not invent "collected".
118
+ return typeof record.status === "string" ? { status: record.status } : {};
119
+ default:
120
+ return typeof record.status === "string" ? { status: record.status } : {};
232
121
  }
233
122
  }
234
123
  /** Deep structural equality for lifecycle agreement checks. */
@@ -2,8 +2,7 @@ import {
2
2
  FIXER_ACCEPTED_TEXT,
3
3
  FIXER_OUTPUT_TOOL_NAME,
4
4
  fixerOutputSchema,
5
- validateFixerOutput,
6
- validateFixerOutputForPacket
5
+ validateFixerOutput
7
6
  } from "./fixer-output.js";
8
7
  import { fixerPrerequisiteSchema, fixerPrerequisitesSchema, parseFixerPrerequisites, validateFixerPrerequisites } from "./fixer-packet.js";
9
8
  import { validateFixerOutput as validateFixerOutput2 } from "./fixer-output.js";
@@ -27,6 +26,5 @@ export {
27
26
  validateAcceptedCoderDetails,
28
27
  validateAcceptedWorkerDetails,
29
28
  validateFixerOutput,
30
- validateFixerOutputForPacket,
31
29
  validateFixerPrerequisites
32
30
  };
@@ -208,29 +208,35 @@ function jsonSafeReplacer() {
208
208
  * open (O_EXCL) with a per-attempt unique name enforces 史必追加 (#419): a later
209
209
  * attempt can never overwrite an earlier attempt's file.
210
210
  */
211
- async function retainDispatchError(admitted, principalAuthority, sessionAppender, attempt, error) {
212
- const artifactsDir = await ensureRealArtifactsDirectory(admitted.runDirectory);
213
- const filePath = join(artifactsDir, `dispatch-error-attempt-${attempt}-${randomUUID()}.json`);
214
- // Whole-object dump: everything the thrown value carries, nothing picked.
215
- const payload = `${JSON.stringify({
216
- version: 1,
217
- attempt,
218
- recordedAt: new Date().toISOString(),
219
- error: serializeThrownValue(error),
220
- }, jsonSafeReplacer(), 2)}\n`;
221
- // O_EXCL: exclusive create — the retention history is append-only by
222
- // construction; a colliding name fails loudly instead of overwriting.
223
- // O_NOFOLLOW when the platform provides it keeps a planted symlink from
224
- // being followed; on platforms without it, exclusivity still holds.
225
- // AK artifact owner stays here; only the session JSONL pointer uses Pi codec.
211
+ /**
212
+ * Hardened create-once JSON write shared by every durable artifact this loop
213
+ * retains directly (dispatch-error dumps, lawful-persist-failure error/
214
+ * evidence records): O_EXCL (fail loud on a colliding name, never overwrite)
215
+ * + O_NOFOLLOW where the platform provides it (a planted symlink is never
216
+ * followed) — mirrors settlement.ts's own hardened artifact writers.
217
+ */
218
+ async function writeHardenedArtifactFile(artifactsDir, namePrefix, payload) {
219
+ const filePath = join(artifactsDir, `${namePrefix}-${randomUUID()}.json`);
220
+ const body = `${JSON.stringify(payload, jsonSafeReplacer(), 2)}\n`;
226
221
  const noFollowFlag = typeof fsConstants.O_NOFOLLOW === "number" ? fsConstants.O_NOFOLLOW : 0;
227
222
  const handle = await open(filePath, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | noFollowFlag, 0o600);
228
223
  try {
229
- await handle.writeFile(payload, "utf8");
224
+ await handle.writeFile(body, "utf8");
230
225
  }
231
226
  finally {
232
227
  await handle.close();
233
228
  }
229
+ return filePath;
230
+ }
231
+ async function retainDispatchError(admitted, principalAuthority, sessionAppender, attempt, error) {
232
+ const artifactsDir = await ensureRealArtifactsDirectory(admitted.runDirectory);
233
+ // Whole-object dump: everything the thrown value carries, nothing picked.
234
+ const filePath = await writeHardenedArtifactFile(artifactsDir, `dispatch-error-attempt-${attempt}`, {
235
+ version: 1,
236
+ attempt,
237
+ recordedAt: new Date().toISOString(),
238
+ error: serializeThrownValue(error),
239
+ });
234
240
  // Addressable pointer in the dossier (卷宗): Pi session custom-entry codec
235
241
  // (appendPiSessionCustomEntry). Lease still owned here with run-writer.
236
242
  let pointerLease;
@@ -380,7 +386,14 @@ export async function runWithAutoResumeLoop(options) {
380
386
  }
381
387
  }
382
388
  dispatchOrdinal += 1;
383
- let persistFailure;
389
+ // #836 r12 class 3: run-state persist for a dispatch that deferred it
390
+ // (needsPersist) is settled by the dispatch closure itself, before this
391
+ // loop ever sees the result — through the single existing
392
+ // presentControlledFailure / settleFailureTerminalResult authority (ADR
393
+ // 0080), never a second hand-rolled classify/artifact/Terminal here. This
394
+ // loop only ever sees the already-resolved outcome: a lawful/failure
395
+ // Terminal (with skipAutoResume set when persist failed after an
396
+ // already-settled result) or the original non-lawful result unchanged.
384
397
  if (result !== undefined) {
385
398
  everyAttemptThrew = false;
386
399
  const terminal = result.terminal;
@@ -388,21 +401,6 @@ export async function runWithAutoResumeLoop(options) {
388
401
  terminal.autoResumeCount = autoResumeAttempts;
389
402
  }
390
403
  const lawful = terminal !== undefined && isLawfulTypedTerminalOutcome(terminal.roleOutcome);
391
- if (result.needsPersist === true) {
392
- // Persist on the loop seam, not inside retried dispatch.
393
- try {
394
- await persistReturnedRunState(options.admitted, { isAvailable: isPrincipalAvailable }, lawful ? { lawful: true } : undefined);
395
- }
396
- catch (persistError) {
397
- // Lawful persist stays loud (no fake terminal). Non-lawful persist is
398
- // not a host-turn failure — do not auto-resume it — but sealed stop
399
- // must still see the already-settled result (#648).
400
- if (lawful)
401
- throw persistError;
402
- persistFailure = { error: persistError };
403
- lastThrownError = persistError;
404
- }
405
- }
406
404
  if (lawful) {
407
405
  if (terminal !== undefined) {
408
406
  // Present lawful terminal once to real io (dummy was used inside dispatch)
@@ -416,42 +414,6 @@ export async function runWithAutoResumeLoop(options) {
416
414
  return result;
417
415
  }
418
416
  }
419
- // Settlement-owned sealed disposition before any redispatch (#648 / #672):
420
- // shared for non-lawful return and direct throw. Only entry presentation
421
- // differs (present returned terminal vs throw-path synthetic).
422
- if (options.sealedAcceptanceDisposition !== undefined) {
423
- const disposition = await options.sealedAcceptanceDisposition();
424
- if (disposition.kind === "block") {
425
- if (disposition.reason === "sealed-accepted" && result !== undefined) {
426
- const terminal = result.terminal;
427
- if (terminal !== undefined)
428
- presentTerminal(terminal, options.io);
429
- return result;
430
- }
431
- const terminal = dispatchExceptionFailureTerminal({
432
- role: options.admitted.role,
433
- runId: options.admitted.runId,
434
- causeError: disposition.reason === "authority-failed"
435
- ? disposition.cause
436
- : lastThrownError,
437
- errorFiles: retainedErrorFiles,
438
- autoResumeAttempts,
439
- endReason: disposition.reason === "authority-failed"
440
- ? "sealed-acceptance authority failed closed"
441
- : "sealed accepted projection already present",
442
- everyAttemptThrew,
443
- });
444
- await finalizeExceptionRunBestEffort(options.admitted.runDirectory, options.io);
445
- presentTerminal(terminal, options.io);
446
- return {
447
- exitCode: 1,
448
- terminal,
449
- };
450
- }
451
- }
452
- if (persistFailure !== undefined) {
453
- throw persistFailure.error;
454
- }
455
417
  if (result !== undefined) {
456
418
  const terminal = result.terminal;
457
419
  if (autoResumeAttempts >= limit) {
@@ -7,24 +7,12 @@
7
7
  *
8
8
  * 递送挂载点唯一:`post-admission` 在 beforeDispatch 之后为每个公共入口追加本段。
9
9
  */
10
- import { stat } from "node:fs/promises";
11
10
  import { resolveTicketProvenanceVolume } from "../ticket-provenance.js";
12
11
  /** Section heading of the system-delivered dossier pointer (presentation only). */
13
12
  const CASE_DOSSIER_SECTION_HEADING = "## 本票起居录(系统随案提供)";
14
- /** Honest one-line state of one dossier file: present, absent, or unreadable. */
15
- async function describeDossierFile(path) {
16
- try {
17
- const stats = await stat(path);
18
- if (!stats.isFile())
19
- return `不可读(非普通文件):${path}`;
20
- return path;
21
- }
22
- catch (error) {
23
- const code = error.code;
24
- if (code === "ENOENT")
25
- return `尚未生成:${path}`;
26
- return `不可读(${code ?? "未知错误"}):${path}`;
27
- }
13
+ /** Pointer only — presence/absence is for the role to observe at the path. */
14
+ function describeDossierFile(path) {
15
+ return path;
28
16
  }
29
17
  /**
30
18
  * Pointer section for a bound ticket's existing 起居录, or undefined when the
@@ -40,7 +28,7 @@ export async function projectCaseDossierPointerSection(input) {
40
28
  CASE_DOSSIER_SECTION_HEADING,
41
29
  "",
42
30
  `票号:#${input.ticketNumber}`,
43
- `人读视图:${await describeDossierFile(volume.humanViewFile)}`,
44
- `记录卷宗:${await describeDossierFile(volume.recordFile)}`,
31
+ `人读视图:${describeDossierFile(volume.humanViewFile)}`,
32
+ `记录卷宗:${describeDossierFile(volume.recordFile)}`,
45
33
  ].join("\n");
46
34
  }
@@ -5,6 +5,7 @@ import { prepareSummonsResumeMaterials, runPostAdmissionOneShot, runPostAdmissio
5
5
  import { loadResumableDiaristRun, markRunAdmitted, } from "./run-lifecycle.js";
6
6
  import { tryResumeSameTicketSeatRun } from "./seat-ticket-binding.js";
7
7
  import { presentStructuralRejection, trySettleDiaristTerminalResult, } from "./settlement.js";
8
+ import { lastRolePayloadRecord } from "./terminal.js";
8
9
  import { projectRoleTurnRequest, } from "./turn-request.js";
9
10
  /** Project admitted invocation onto the host-neutral turn request. */
10
11
  export function buildDiaristTurnRequest(admitted, options) {
@@ -114,13 +115,17 @@ export async function runPublicDiarist(argv, env, io, parseDiaristArgv) {
114
115
  // leak an unverified ticketNumber into the caller-visible typed key.
115
116
  if (admitted.ticketNumber === undefined && result.admitted !== undefined) {
116
117
  const roleOutcome = result.terminal?.roleOutcome;
118
+ const facts = roleOutcome?.kind === "accepted"
119
+ ? lastRolePayloadRecord(roleOutcome.payloads ?? [])
120
+ : undefined;
117
121
  if (roleOutcome !== undefined &&
118
122
  roleOutcome.kind === "accepted" &&
119
- roleOutcome.status !== "escalate") {
120
- const facts = roleOutcome.decisiveFacts;
121
- const raw = typeof facts.ticketNumber === "number"
123
+ facts?.status !== "escalate") {
124
+ const raw = typeof facts?.ticketNumber === "number"
122
125
  ? facts.ticketNumber
123
- : typeof facts.sitian?.ticketNumber === "number"
126
+ : typeof facts?.sitian === "object"
127
+ && facts.sitian !== null
128
+ && typeof facts.sitian.ticketNumber === "number"
124
129
  ? facts.sitian.ticketNumber
125
130
  : undefined;
126
131
  if (typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 1) {
@@ -34,8 +34,9 @@ export async function runPublicInspector(argv, env, io, parseInspectorArgv) {
34
34
  // #753/#786: gate re-ask / verbatim submission body ride summons.instruction on resume.
35
35
  const parentRunPath = parentRunPathFromGatePointerInstruction(parsed.instruction);
36
36
  if (parentRunPath !== undefined) {
37
- const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
37
+ const resumeInstruction = env.reviewReask;
38
38
  const summons = {
39
+ sourceRunPath: parentRunPath,
39
40
  ...(resumeInstruction === undefined
40
41
  ? {
41
42
  instruction: parsed.instruction,
@@ -55,13 +56,6 @@ export async function runPublicInspector(argv, env, io, parseInspectorArgv) {
55
56
  });
56
57
  if (resumed !== undefined)
57
58
  return resumed;
58
- // Reask without a prior same-parent run cannot deliver the plain-language ask
59
- // on a fresh mint without inventing a second prompt path — fail loud (#753).
60
- // gateReviewInstruction (verbatim body) alone is resume-only; fresh mint keeps argv 卷宗指针.
61
- if (env.reviewReask !== undefined) {
62
- presentStructuralRejection(new CliUsageError("inspector review reask requires a prior same-parent run to resume"), io);
63
- return { exitCode: 2 };
64
- }
65
59
  }
66
60
  let admitted;
67
61
  try {
@@ -151,7 +151,7 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
151
151
  }
152
152
  }
153
153
  // #756/#786: auditor reask / verbatim submission body ride summons.instruction on resume.
154
- const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction;
154
+ const resumeInstruction = env.reviewReask;
155
155
  const summons = {
156
156
  ...(resumeInstruction === undefined
157
157
  ? {
@@ -177,12 +177,8 @@ export async function runPublicInstructionSeat(argv, env, io, role, parseArgv) {
177
177
  });
178
178
  if (resumed !== undefined)
179
179
  return resumed;
180
- // Reask without a prior same-parent run cannot deliver the plain-language ask
181
- // on a fresh mint without inventing a second prompt path — fail loud (#753 / #756).
182
- if (env.reviewReask !== undefined) {
183
- presentStructuralRejection(new CliUsageError("auditor review reask requires a prior same-parent run to resume"), io);
184
- return { exitCode: 2 };
185
- }
180
+ // #836: reask without a prior same-parent run still mints; summons.instruction
181
+ // already carries the reask text. Do not structurally terminate the queue.
186
182
  }
187
183
  let admitted;
188
184
  try {
@@ -1816,13 +1816,16 @@ export async function admitReviewerInvocation(options) {
1816
1816
  }
1817
1817
  /**
1818
1818
  * Build the Pi prompt transport for an admitted Reviewer request.
1819
- * Semantic input is fixed base only — caller instruction stays provenance on disk.
1820
- * Optional engine material follows the same dual-path coordinates as Judge (#376/#378).
1819
+ * #836: caller instruction is no longer dropped (A3.5).
1820
+ * Fixed base + optional caller words + engine material.
1821
1821
  */
1822
1822
  export function buildReviewerTransportPrompt(admitted, engineMaterial) {
1823
1823
  const lines = [
1824
1824
  `本次审查的固定基点:${admitted.baseRevision}`,
1825
1825
  ];
1826
+ if (!admitted.instructionEmpty && admitted.instruction.trim() !== "") {
1827
+ lines.push("", admitted.instruction);
1828
+ }
1826
1829
  return appendEngineSessionMaterial(lines, engineMaterial).join("\n");
1827
1830
  }
1828
1831
  /**