@akagilnc/pi-workflow-roles 0.1.5406 → 0.1.5475
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.
- package/CLAUDE.md +1 -1
- package/README.md +42 -55
- package/README.zh-CN.md +59 -73
- package/THIRD_PARTY_NOTICES.md +4 -7
- package/dist/acp-host/production-host.js +8261 -9073
- package/dist/collector-tool-schemas.js +1 -1
- package/dist/compliance-transport.js +2 -5
- package/dist/engine-detour-usage.js +3 -30
- package/dist/gatekeeper-role.js +4 -18
- package/dist/headless-host/description.js +0 -209
- package/dist/headless-host/production-host.js +6973 -8035
- package/dist/host-contracts.js +8 -0
- package/dist/migrate-book-topology.js +30 -27
- package/dist/packaged-role-registry.js +1 -9
- package/dist/pi/role-turn-host.js +37 -19
- package/dist/public-cli/auto-resume.js +29 -66
- package/dist/public-cli/countersign-run.js +4 -18
- package/dist/public-cli/instruction-seat-run.js +59 -76
- package/dist/public-cli/main.js +9844 -10720
- package/dist/public-cli/option-definitions.js +1 -1
- package/dist/public-cli/post-admission.js +204 -285
- package/dist/public-cli/process-cancel.js +8 -0
- package/dist/public-cli/run-lifecycle.js +24 -94
- package/dist/public-cli/settlement.js +614 -1524
- package/dist/public-cli/terminal.js +23 -53
- package/dist/public-role-summons.js +3 -8
- package/dist/receipt-delivery-policy.js +11 -12
- package/dist/run-terminal-artifacts.js +19 -14
- package/dist/serialize-thrown-value.js +7 -1
- package/dist/submission-ledger.js +21 -89
- package/package.json +1 -1
- package/packets/fixer-repair.md +3 -1
- package/packets/judge-apply.md +7 -16
- package/packets/judge-authority.md +7 -17
- package/packets/judge-plan.md +7 -18
- package/packets/judge-review.md +8 -22
- package/packets/judge-submission.md +4 -7
- package/resources/collector-bot-handbook.md +2 -2
- package/resources/diarist-collect.md +2 -2
- package/resources/engine-dispatch.md +2 -2
- package/resources/navigator-route-playbook.md +1 -1
- package/resources/secretariat-ticket-identity.md +1 -1
- package/scripts/build-package.mjs +0 -2
- package/souls/coder-output-guide.md +3 -3
- package/souls/coder.md +2 -3
- package/souls/doctor-auditor.md +0 -3
- package/souls/fixer-auditor.md +0 -2
- package/souls/fixer-output-guide.md +3 -3
- package/souls/fixer.md +2 -2
- package/souls/gate-output-guide.md +1 -1
- package/souls/quality-law.md +2 -0
- package/src/acp-host/role-turn-host.ts +168 -75
- package/src/collector-tool-schemas.ts +1 -1
- package/src/compliance-transport.ts +3 -6
- package/src/engine-detour-usage.ts +3 -33
- package/src/external-host-turn-loop.ts +81 -36
- package/src/gatekeeper-role.ts +5 -20
- package/src/headless-host/description.ts +4 -214
- package/src/headless-host/production-host.ts +2 -1
- package/src/headless-host/role-turn-host.ts +153 -186
- package/src/host-contracts.ts +13 -10
- package/src/navigator-public-session.ts +1 -1
- package/src/navigator-session-contracts.ts +1 -1
- package/src/package-contracts/gatekeeper-output.ts +1 -1
- package/src/package-contracts/navigator-output.ts +1 -1
- package/src/package-contracts/terminating-infrastructure.ts +1 -1
- package/src/packaged-role-registry.ts +2 -10
- package/src/pi/adapter.ts +0 -3
- package/src/pi/role-turn-host.ts +42 -28
- package/src/prepared-role-turn.ts +2 -0
- package/src/public-cli/auto-resume.ts +27 -66
- package/src/public-cli/countersign-run.ts +4 -22
- package/src/public-cli/instruction-seat-run.ts +79 -80
- package/src/public-cli/option-definitions.ts +1 -1
- package/src/public-cli/post-admission.ts +264 -340
- package/src/public-cli/process-cancel.ts +9 -0
- package/src/public-cli/run-lifecycle.ts +25 -126
- package/src/public-cli/settlement.ts +792 -1912
- package/src/public-cli/terminal.ts +29 -69
- package/src/public-role-summons.ts +3 -8
- package/src/receipt-delivery-policy.ts +11 -12
- package/src/role-envelope.ts +16 -9
- package/src/role-runtime.ts +14 -81
- package/src/run-terminal-artifacts.ts +21 -17
- package/src/serialize-thrown-value.ts +7 -1
- package/src/submission-ledger.ts +30 -116
- package/dist/audit-escalation.js +0 -7
- package/dist/pi/known-failure.js +0 -51
- package/dist/typed-provider-http.js +0 -57
- package/dist/upstream-error-testimony.js +0 -19
- package/src/pi/known-failure.ts +0 -76
- package/src/typed-provider-http.ts +0 -89
- package/src/upstream-error-testimony.ts +0 -45
|
@@ -37,14 +37,13 @@ function isStationChildOfficerDialogue(role, env) {
|
|
|
37
37
|
return env.stationChild === true && isOfficerReviewSeat(role);
|
|
38
38
|
}
|
|
39
39
|
import { mintEngineDetourInvocationScope, withEngineDetourInvocationScope, } from "../engine-detour-usage.js";
|
|
40
|
-
import { clearCurrentCourt,
|
|
40
|
+
import { clearCurrentCourt, describeErrorIdentity, markRunRunning, readCurrentCourt, recordCurrentCourt, } from "./run-lifecycle.js";
|
|
41
41
|
import { processCancelDiagnostic, processCancelSignalName, } from "./process-cancel.js";
|
|
42
|
-
import { homeFromRunDirectory } from "../activation-ledger-topology.js";
|
|
43
42
|
import { DEFAULT_ROLE_TURN_HOST } from "../host-descriptions.js";
|
|
44
43
|
import { recordRunStart } from "../host-session-record.js";
|
|
45
|
-
import {
|
|
44
|
+
import { classifyPostAdmissionFailure, exitCodeForTerminalOutcome, explicitInternalKnownFailureClassificationInput, formatCliDiagnostic, formatErrorCauseDetail, ledgerReadScope, isLawfulTypedTerminalOutcome, presentStructuralRejection, settleFailureTerminalResult, settleHostEndedNoReceipt, noteSettlementFault, attachRecordedSubmissions, retainPackageFault, } from "./settlement.js";
|
|
46
45
|
import {} from "./terminal.js";
|
|
47
|
-
import { ensureRealArtifactsDirectory, persistReturnedRunState, runWithAutoResumeLoop, TurnDispatchedFailure,
|
|
46
|
+
import { ensureRealArtifactsDirectory, persistReturnedRunState, presentTerminal, runWithAutoResumeLoop, TurnDispatchedFailure, } from "./auto-resume.js";
|
|
48
47
|
import { isRecord, errorText } from "../unknown-value.js";
|
|
49
48
|
function projectRelocatedTurnIdentity(request, result, admitted, relocation) {
|
|
50
49
|
const rewrite = (value) => rewriteRunDirectoryPathValue(value, relocation.oldRunDirectory, relocation.newRunDirectory);
|
|
@@ -60,7 +59,6 @@ function projectRelocatedTurnIdentity(request, result, admitted, relocation) {
|
|
|
60
59
|
}
|
|
61
60
|
}
|
|
62
61
|
if (result.terminal !== undefined) {
|
|
63
|
-
rewritePublishedFailureErrorPath(result.terminal, relocation.oldRunDirectory, relocation.newRunDirectory);
|
|
64
62
|
for (const artifact of result.terminal.artifacts) {
|
|
65
63
|
artifact.path = rewrite(artifact.path);
|
|
66
64
|
}
|
|
@@ -82,8 +80,14 @@ async function settleProcessCancelAfterTurn(input) {
|
|
|
82
80
|
timedOut: input.result.timedOut,
|
|
83
81
|
code: input.result.code,
|
|
84
82
|
stderr: input.result.stderr,
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
...(input.result.signal === undefined ? {} : { signal: input.result.signal }),
|
|
84
|
+
// A host report already on this result stays the report. The cancel
|
|
85
|
+
// name is wording only when the host gave none.
|
|
86
|
+
...(input.result.knownFailure === undefined
|
|
87
|
+
? { knownDiagnostic: processCancelDiagnostic(input.cancelName) }
|
|
88
|
+
: { knownFailure: input.result.knownFailure }),
|
|
89
|
+
cancelName: input.cancelName,
|
|
90
|
+
}, input.invocationScopeId), input.adapters, input.env.principalAuthority, input.io, input.persistRunState, input.notePackageFault);
|
|
87
91
|
return {
|
|
88
92
|
...failed,
|
|
89
93
|
turnDispatched: true,
|
|
@@ -127,33 +131,16 @@ export const POST_ADMISSION_CLEANUP_DIAGNOSTIC_ENTRY_TYPE = "ak_post_admission_c
|
|
|
127
131
|
* redundant second copy (#840 bounce class 1: 同一业务规则只保留一个权威实现).
|
|
128
132
|
*/
|
|
129
133
|
async function recordBestEffortPostDispatchDiagnostic(admitted, env, diagnostic, io) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
...payload,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
catch (artifactError) {
|
|
144
|
-
retentionFailure =
|
|
145
|
-
`post-dispatch diagnostic durable retention failed on both channels (best-effort continue): dossier=${describeErrorIdentity(appendError)}; artifact=${describeErrorIdentity(artifactError)}`;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
try {
|
|
149
|
-
io.stderr(formatCliDiagnostic(diagnostic));
|
|
150
|
-
if (retentionFailure !== undefined) {
|
|
151
|
-
io.stderr(formatCliDiagnostic(retentionFailure));
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
catch {
|
|
155
|
-
// Presentation is best-effort beside an already-formed host terminal.
|
|
156
|
-
}
|
|
134
|
+
await retainPackageFault({
|
|
135
|
+
runDirectory: admitted.runDirectory,
|
|
136
|
+
diagnostic,
|
|
137
|
+
appendSession: (payload) => env.sessionAppender(env.principalAuthority, admitted.principal, POST_ADMISSION_CLEANUP_DIAGNOSTIC_ENTRY_TYPE, payload),
|
|
138
|
+
stderr: (text) => io.stderr(text),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/** The one package-fault receiver, bound to this admitted run's session and io. */
|
|
142
|
+
export function packageFaultNoteFor(admitted, env, io) {
|
|
143
|
+
return (diagnostic) => recordBestEffortPostDispatchDiagnostic(admitted, env, diagnostic, io);
|
|
157
144
|
}
|
|
158
145
|
/**
|
|
159
146
|
* Factory-seat resume method-material face (#833): one authority for
|
|
@@ -175,42 +162,42 @@ export async function resolveResumeMethodMaterialAdapters(input) {
|
|
|
175
162
|
stderr: "",
|
|
176
163
|
thrown: error,
|
|
177
164
|
...(input.knownCause === undefined ? {} : { knownCause: input.knownCause }),
|
|
178
|
-
}, input.emptyAdapters, input.authority, { ...input.io, omitFailureStderrDiagnostic: true })
|
|
165
|
+
}, input.emptyAdapters, input.authority, { ...input.io, omitFailureStderrDiagnostic: true }, true, (diagnostic) => retainPackageFault({
|
|
166
|
+
runDirectory: input.admitted.runDirectory,
|
|
167
|
+
diagnostic,
|
|
168
|
+
stderr: (text) => input.io.stderr(text),
|
|
169
|
+
}));
|
|
179
170
|
return { kind: "terminal", ...terminal };
|
|
180
171
|
}
|
|
181
172
|
}
|
|
182
|
-
export async function presentControlledFailure(admitted, failureInput, adapters, authority, io, persistRunState = true) {
|
|
173
|
+
export async function presentControlledFailure(admitted, failureInput, adapters, authority, io, persistRunState = true, notePackageFault) {
|
|
183
174
|
const hasThrown = Object.hasOwn(failureInput, "thrown");
|
|
184
|
-
const
|
|
185
|
-
runDirectory: admitted.runDirectory,
|
|
186
|
-
...(failureInput.typedHttpObservationSettled === true
|
|
187
|
-
? {
|
|
188
|
-
typedHttpObservationSettled: true,
|
|
189
|
-
...(failureInput.typedHttpObservation === undefined
|
|
190
|
-
? {}
|
|
191
|
-
: { typedHttpObservation: failureInput.typedHttpObservation }),
|
|
192
|
-
}
|
|
193
|
-
: {}),
|
|
194
|
-
});
|
|
195
|
-
const knownFailure = failureInput.knownFailure ?? resumeObservation.observationReadFailure;
|
|
196
|
-
const session = !hasThrown &&
|
|
197
|
-
!failureInput.timedOut &&
|
|
198
|
-
knownFailure === undefined &&
|
|
199
|
-
failureInput.knownCause === undefined &&
|
|
200
|
-
admitted.principal !== undefined
|
|
201
|
-
? await inspectJudgeSession(authority.decode(admitted.principal).sessionFile)
|
|
202
|
-
: undefined;
|
|
175
|
+
const knownFailure = failureInput.knownFailure;
|
|
203
176
|
// knownFailure channel owns details when present; otherwise caller knownDetails.
|
|
204
177
|
const fromKnownFailure = explicitInternalKnownFailureClassificationInput(knownFailure);
|
|
205
|
-
|
|
178
|
+
// Merge the two detail sources so a key carried by either survives; a later
|
|
179
|
+
// spread would otherwise drop the earlier one's keys entirely.
|
|
180
|
+
const callerDetails = failureInput.knownDetails === undefined && fromKnownFailure.knownDetails === undefined
|
|
181
|
+
? undefined
|
|
182
|
+
: { ...(failureInput.knownDetails ?? {}), ...(fromKnownFailure.knownDetails ?? {}) };
|
|
183
|
+
const classified = classifyPostAdmissionFailure({
|
|
206
184
|
timedOut: failureInput.timedOut,
|
|
207
185
|
code: failureInput.code,
|
|
208
186
|
stderr: failureInput.stderr,
|
|
209
187
|
...(hasThrown ? { thrown: failureInput.thrown } : {}),
|
|
210
|
-
|
|
188
|
+
// Both detail sources are real; merging keeps each one's keys instead of
|
|
189
|
+
// letting the later spread drop the earlier one.
|
|
190
|
+
...(callerDetails === undefined ? {} : { knownDetails: callerDetails }),
|
|
191
|
+
// The knownFailure channel's own cause/identity/diagnostic ride with it.
|
|
192
|
+
...(fromKnownFailure.knownCause === undefined
|
|
193
|
+
? {}
|
|
194
|
+
: { knownCause: fromKnownFailure.knownCause }),
|
|
195
|
+
...(fromKnownFailure.knownIdentity === undefined
|
|
196
|
+
? {}
|
|
197
|
+
: { knownIdentity: fromKnownFailure.knownIdentity }),
|
|
198
|
+
...(fromKnownFailure.knownDiagnostic === undefined
|
|
211
199
|
? {}
|
|
212
|
-
: {
|
|
213
|
-
...fromKnownFailure,
|
|
200
|
+
: { knownDiagnostic: fromKnownFailure.knownDiagnostic }),
|
|
214
201
|
...(failureInput.knownCause === undefined
|
|
215
202
|
? {}
|
|
216
203
|
: { knownCause: failureInput.knownCause }),
|
|
@@ -220,20 +207,30 @@ export async function presentControlledFailure(admitted, failureInput, adapters,
|
|
|
220
207
|
...(failureInput.knownDiagnostic === undefined
|
|
221
208
|
? {}
|
|
222
209
|
: { knownDiagnostic: failureInput.knownDiagnostic }),
|
|
223
|
-
...(
|
|
210
|
+
...(failureInput.signal === undefined ? {} : { signal: failureInput.signal }),
|
|
211
|
+
...(failureInput.cancelName === undefined ? {} : { cancelName: failureInput.cancelName }),
|
|
212
|
+
...(failureInput.packageFact === undefined
|
|
213
|
+
? {}
|
|
214
|
+
: { packageFact: failureInput.packageFact }),
|
|
224
215
|
});
|
|
225
|
-
|
|
226
|
-
|
|
216
|
+
const failure = failureInput.stderr.length === 0 || failureInput.stderr === classified.diagnostic
|
|
217
|
+
? classified
|
|
218
|
+
: { ...classified, stderr: failureInput.stderr };
|
|
227
219
|
if (persistRunState && !failureInput.skipRunStateWrite) {
|
|
228
|
-
|
|
220
|
+
try {
|
|
221
|
+
await persistReturnedRunState(admitted);
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
// The host failure is already classified. A run-state write stays beside it.
|
|
225
|
+
await noteSettlementFault(admitted.runDirectory, notePackageFault === undefined ? undefined : { notePackageFault }, `run-state persistence failed beside host terminal: ${describeErrorIdentity(error)}`);
|
|
226
|
+
}
|
|
229
227
|
}
|
|
230
228
|
let publishedErrorPath;
|
|
231
229
|
let terminal;
|
|
232
230
|
try {
|
|
233
231
|
const settled = await settleFailureTerminalResult(admitted, failure, authority, {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
: {}),
|
|
232
|
+
recordAttemptHistory: true,
|
|
233
|
+
...(notePackageFault === undefined ? {} : { notePackageFault }),
|
|
237
234
|
...(failureInput.invocationScopeId === undefined ||
|
|
238
235
|
failureInput.invocationScopeId.length === 0
|
|
239
236
|
? {}
|
|
@@ -248,7 +245,7 @@ export async function presentControlledFailure(admitted, failureInput, adapters,
|
|
|
248
245
|
}
|
|
249
246
|
throw error;
|
|
250
247
|
}
|
|
251
|
-
|
|
248
|
+
await presentTerminal(terminal, io, admitted.runDirectory);
|
|
252
249
|
return {
|
|
253
250
|
exitCode: exitCodeForTerminalOutcome(terminal.roleOutcome),
|
|
254
251
|
admitted,
|
|
@@ -266,9 +263,9 @@ export async function presentControlledFailure(admitted, failureInput, adapters,
|
|
|
266
263
|
* the true cause through its own existing dispatch-exception machinery once
|
|
267
264
|
* the retry budget is exhausted.
|
|
268
265
|
*/
|
|
269
|
-
async function settleAfterTurnStarted(admitted, failureInput, adapters, authority, io, persistRunState) {
|
|
266
|
+
async function settleAfterTurnStarted(admitted, failureInput, adapters, authority, io, persistRunState, notePackageFault) {
|
|
270
267
|
try {
|
|
271
|
-
return (await presentControlledFailure(admitted, failureInput, adapters, authority, io, persistRunState));
|
|
268
|
+
return (await presentControlledFailure(admitted, failureInput, adapters, authority, io, persistRunState, notePackageFault));
|
|
272
269
|
}
|
|
273
270
|
catch (error) {
|
|
274
271
|
throw new TurnDispatchedFailure(error);
|
|
@@ -278,33 +275,21 @@ async function settleAfterTurnStarted(admitted, failureInput, adapters, authorit
|
|
|
278
275
|
* Persist run-state for a result dispatchPostAdmissionTurn deferred
|
|
279
276
|
* (needsPersist — station-child / resumable auto-resume loop, #416/#840):
|
|
280
277
|
* that write must land outside the loop's own retried-dispatch try, but its
|
|
281
|
-
* failure
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
* classify/artifact/Terminal (#836 r12 class 3), and never lawful/non-lawful
|
|
285
|
-
* settling differently (#836 r13 class 2: a caller's io here may be a no-op,
|
|
286
|
-
* so a stderr-only trace is never seen and the failure is otherwise
|
|
287
|
-
* swallowed — 失败诚实宪法 真因必须落痕). Both cases settle through
|
|
288
|
-
* settleAfterTurnStarted, which attaches the run's already-recorded ledger
|
|
289
|
-
* submissions to the new failure terminal and returns skipAutoResume so the
|
|
290
|
-
* caller presents it once and stops — never re-entering auto-resume.
|
|
278
|
+
* failure is a package note beside the terminal already settled. It does
|
|
279
|
+
* not replace that terminal or open another attempt. The note is durable
|
|
280
|
+
* because the caller's io may be a no-op.
|
|
291
281
|
*/
|
|
292
|
-
async function settleDeferredPersist(admitted,
|
|
282
|
+
async function settleDeferredPersist(admitted, env, io, result) {
|
|
293
283
|
if (result.needsPersist !== true || result.terminal === undefined)
|
|
294
284
|
return result;
|
|
295
285
|
const { needsPersist: _needsPersist, ...settledResult } = result;
|
|
296
|
-
const lawful = isLawfulTypedTerminalOutcome(result.terminal.roleOutcome);
|
|
297
286
|
try {
|
|
298
|
-
await persistReturnedRunState(admitted
|
|
287
|
+
await persistReturnedRunState(admitted);
|
|
299
288
|
return settledResult;
|
|
300
289
|
}
|
|
301
290
|
catch (error) {
|
|
302
|
-
|
|
303
|
-
return
|
|
304
|
-
...failed,
|
|
305
|
-
turnDispatched: true,
|
|
306
|
-
skipAutoResume: true,
|
|
307
|
-
};
|
|
291
|
+
await recordBestEffortPostDispatchDiagnostic(admitted, env, `run-state persistence failed beside host terminal (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
292
|
+
return settledResult;
|
|
308
293
|
}
|
|
309
294
|
}
|
|
310
295
|
/**
|
|
@@ -371,17 +356,10 @@ async function buildReceiptDeliveryRequest(input) {
|
|
|
371
356
|
async function settleCompletedHostTurn(input) {
|
|
372
357
|
const { admitted, env, io, adapters, shouldPresent, request, result, persistRunState, deferredPersist } = input;
|
|
373
358
|
const finishAfterTurn = input.finishAfterTurn;
|
|
374
|
-
|
|
375
|
-
//
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
// dropped silently (catch-and-continue with no trace is a defect) — but
|
|
379
|
-
// it also must never become THE controlling failure and wash out a
|
|
380
|
-
// primary child signal that already arrived on `result`, or an
|
|
381
|
-
// already-sealed accepted payload (#836: a secondary failure must not
|
|
382
|
-
// overwrite an already-known cause or an already-recorded leg). It rides
|
|
383
|
-
// beside whatever the real classification below determines, and only
|
|
384
|
-
// becomes the reported failure itself when nothing else is wrong.
|
|
359
|
+
const courtScope = input.courtScope;
|
|
360
|
+
// `result.stderr` stays live in memory for host-failure settlement
|
|
361
|
+
// whether or not this mirror write succeeds. A write failure is noted.
|
|
362
|
+
// It does not replace the host terminal or an already-sealed acceptance.
|
|
385
363
|
//
|
|
386
364
|
// Everything below runs after the host turn genuinely started (#840 r9
|
|
387
365
|
// 判词 class 1 boundary — 覆盖 executeTurn 已启动后至返回带 turnDispatched
|
|
@@ -404,138 +382,47 @@ async function settleCompletedHostTurn(input) {
|
|
|
404
382
|
}
|
|
405
383
|
catch (error) {
|
|
406
384
|
stderrLogWriteFailure = error;
|
|
407
|
-
// Best-effort:
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
// every auto-resume attempt's io is dummyIo (#840 r9 判词 class 1). It
|
|
411
|
-
// still rides beside the classification below and, if nothing else is
|
|
412
|
-
// wrong, becomes the reported failure itself (#836).
|
|
385
|
+
// Best-effort: stderr.log is secondary to the host terminal. A write
|
|
386
|
+
// failure leaves a durable note and, on a host failure, rides in
|
|
387
|
+
// packageFact. It does not become the cause.
|
|
413
388
|
await recordBestEffortPostDispatchDiagnostic(admitted, env, `stderr.log write failed (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
414
389
|
}
|
|
415
|
-
|
|
416
|
-
//
|
|
417
|
-
//
|
|
418
|
-
//
|
|
419
|
-
// throw from any of them (including the session-file decode itself)
|
|
420
|
-
// still routes through settleAfterTurnStarted's TurnDispatchedFailure
|
|
421
|
-
// instead of losing turnDispatched to an uncaught throw. The facts are
|
|
422
|
-
// resolved before trySettle (#836) so an already-accepted settlement is
|
|
423
|
-
// never presented over a real current host/runner failure signal or a
|
|
424
|
-
// real stderr.log durable-write failure — both stay a real failure with
|
|
425
|
-
// the recorded payload riding beside it, not replacing it.
|
|
390
|
+
// Host facts are resolved before trySettle. A host failure skips trySettle
|
|
391
|
+
// so a later read cannot replace that report. A clean host whose settlement
|
|
392
|
+
// read throws is noted above and is not turned into a failure terminal.
|
|
393
|
+
// stderr.log and run-state writes are notes beside the host terminal.
|
|
426
394
|
let settled;
|
|
427
395
|
let settledOutcome;
|
|
428
|
-
|
|
429
|
-
|
|
396
|
+
// The host CLI's own report: timeout, declared failure, cancel, or exit.
|
|
397
|
+
// A null code is a signal death (Node close). No session or history read
|
|
398
|
+
// takes part (owner ea321c6d / 4743ade7).
|
|
399
|
+
const processCancelName = processCancelSignalName(env.signal);
|
|
400
|
+
const hostSignalFailed = result.timedOut
|
|
401
|
+
|| result.knownFailure !== undefined
|
|
402
|
+
|| processCancelName !== undefined
|
|
403
|
+
|| result.code === null
|
|
404
|
+
|| result.code !== 0;
|
|
405
|
+
const hostReportedFailure = result.knownFailure;
|
|
430
406
|
try {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
// A direct, current signal from the host/runner itself (timeout / host
|
|
444
|
-
// knownFailure / runner knownFailure / missing credential) is a real
|
|
445
|
-
// problem regardless of what else already settled — a settled accepted
|
|
446
|
-
// outcome must not paper over it (it rides beside the recorded payload
|
|
447
|
-
// via `submissions`, never replacing the payload). The audited
|
|
448
|
-
// resolution's own typed session read (malformed JSONL, provider-stop,
|
|
449
|
-
// an illegal/unsealed accepted status, a stale/superseded typed-HTTP
|
|
450
|
-
// observation, ...) makes a run with no sealed acceptance a true
|
|
451
|
-
// failure too — it must not be discarded into a lawful no_receipt just
|
|
452
|
-
// because the raw runner signal alone looked clean. But it must not
|
|
453
|
-
// retroactively invalidate an acceptance that already sealed this turn
|
|
454
|
-
// — a resolved 429 observed earlier in the same session is exactly
|
|
455
|
-
// that.
|
|
456
|
-
const processCancelName = processCancelSignalName(env.signal);
|
|
457
|
-
const directHostFailureSignal = result.timedOut
|
|
458
|
-
|| result.knownFailure !== undefined
|
|
459
|
-
|| runnerKnownFailure !== undefined
|
|
460
|
-
|| processCancelName !== undefined;
|
|
461
|
-
hostSignalFailed =
|
|
462
|
-
directHostFailureSignal
|
|
463
|
-
|| (result.code !== null && result.code !== 0)
|
|
464
|
-
|| resolution.knownFailure !== undefined;
|
|
465
|
-
settled = await adapters.trySettle(admitted, env.principalAuthority, courtScope);
|
|
466
|
-
if (settled !== undefined) {
|
|
467
|
-
settled = await attachRecordedSubmissions(admitted, settled, courtScope);
|
|
468
|
-
}
|
|
469
|
-
// #836 r12 class 2: an accepted/audit_escalation settlement can be
|
|
470
|
-
// entirely a prior attempt's stale payload (courtAttempt is a
|
|
471
|
-
// recording tag, not a visibility gate — attachRecordedSubmissions
|
|
472
|
-
// above still surfaces that historical payload honestly either way).
|
|
473
|
-
// Consume the ledger's own subject.attemptId (submission-ledger.ts)
|
|
474
|
-
// only to learn whether *this* attempt itself produced a fresh seal —
|
|
475
|
-
// never to filter what presents.
|
|
476
|
-
const settledIsFreshThisAttempt = settled === undefined
|
|
477
|
-
|| (settled.roleOutcome.kind !== "accepted" && settled.roleOutcome.kind !== "audit_escalation")
|
|
478
|
-
? true
|
|
479
|
-
: await attemptProducedFreshSubmission(admitted, courtScope);
|
|
480
|
-
// A lawful settled outcome already reached this turn takes precedence
|
|
481
|
-
// over a later bare exit-code / session-inspection signal (trailing
|
|
482
|
-
// nonzero exit, late stderr noise, a stale already-superseded
|
|
483
|
-
// typed-HTTP observation, ...) — but never over a direct current
|
|
484
|
-
// host/runner failure signal, nor over a real stderr.log durable-write
|
|
485
|
-
// failure (confirmed infrastructure trouble, not weak/bare evidence),
|
|
486
|
-
// both of which stay a real failure with the recorded payload riding
|
|
487
|
-
// beside it, not replacing it (#836: never kill an already-recorded
|
|
488
|
-
// leg, but never wash a real failure away either). Note: the narrower
|
|
489
|
-
// directHostFailureSignal gates acceptance here; the broader
|
|
490
|
-
// hostSignalFailed (adds bare exit code / resolution.knownFailure)
|
|
491
|
-
// only gates the no-settlement fallback below — except when the
|
|
492
|
-
// settlement itself is entirely stale (no fresh seal this attempt),
|
|
493
|
-
// where a bare nonzero exit / resolution failure must not be outranked
|
|
494
|
-
// by someone else's earlier success (#836 r12 class 2).
|
|
495
|
-
const staleAcceptanceOutranksRealFailure = !settledIsFreshThisAttempt && hostSignalFailed;
|
|
496
|
-
// #855: re-read cancel after trySettle/attach awaits — a signal in this
|
|
497
|
-
// window must not land as lawful accepted.
|
|
498
|
-
const cancelAfterSettle = processCancelSignalName(env.signal);
|
|
499
|
-
if (settled !== undefined
|
|
500
|
-
&& shouldPresent(settled)
|
|
501
|
-
&& !directHostFailureSignal
|
|
502
|
-
&& !staleAcceptanceOutranksRealFailure
|
|
503
|
-
&& stderrLogWriteFailure === undefined
|
|
504
|
-
&& cancelAfterSettle === undefined) {
|
|
505
|
-
// This court sealed — drop open-court pointer (bare resume no longer continues it).
|
|
506
|
-
if (settled.roleOutcome.kind === "accepted" &&
|
|
507
|
-
request.courtAttemptId !== undefined &&
|
|
508
|
-
request.courtAttemptId.length > 0) {
|
|
509
|
-
try {
|
|
510
|
-
await clearCurrentCourt(admitted.runDirectory, request.courtAttemptId);
|
|
511
|
-
}
|
|
512
|
-
catch (error) {
|
|
513
|
-
// Settlement already sealed accepted — a cleanup failure here must
|
|
514
|
-
// not erase that fact or make the caller replay this court's
|
|
515
|
-
// summons over already-delivered work (#840 已交劳动只整理终局不重做).
|
|
516
|
-
// Preserve the cleanup failure as a post-dispatch diagnostic; the
|
|
517
|
-
// accepted settlement remains authoritative (#840 r9 判词 class 1).
|
|
518
|
-
await recordBestEffortPostDispatchDiagnostic(admitted, env, `current-court cleanup failed after accepted settlement (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
519
|
-
}
|
|
407
|
+
if (!hostSignalFailed) {
|
|
408
|
+
settled = await adapters.trySettle(admitted, env.principalAuthority, { ...courtScope, previewOnly: true });
|
|
409
|
+
const cancelAfterSettle = processCancelSignalName(env.signal);
|
|
410
|
+
if (settled !== undefined
|
|
411
|
+
&& shouldPresent(settled)
|
|
412
|
+
&& cancelAfterSettle === undefined) {
|
|
413
|
+
settledOutcome = {
|
|
414
|
+
exitCode: exitCodeForTerminalOutcome(settled.roleOutcome),
|
|
415
|
+
admitted,
|
|
416
|
+
terminal: settled,
|
|
417
|
+
turnDispatched: true,
|
|
418
|
+
};
|
|
520
419
|
}
|
|
521
|
-
settledOutcome = {
|
|
522
|
-
exitCode: exitCodeForTerminalOutcome(settled.roleOutcome),
|
|
523
|
-
admitted,
|
|
524
|
-
terminal: settled,
|
|
525
|
-
turnDispatched: true,
|
|
526
|
-
};
|
|
527
420
|
}
|
|
528
421
|
}
|
|
529
422
|
catch (error) {
|
|
530
|
-
//
|
|
531
|
-
//
|
|
532
|
-
|
|
533
|
-
timedOut: false,
|
|
534
|
-
code: result.code,
|
|
535
|
-
stderr: result.stderr,
|
|
536
|
-
thrown: error,
|
|
537
|
-
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
538
|
-
return { kind: "settled", outcome: await finishAfterTurn({ ...settledFailure, turnDispatched: true, ...deferredPersist }) };
|
|
423
|
+
// The host did not fail. A settlement read here is a package fault and
|
|
424
|
+
// must not become the terminal.
|
|
425
|
+
await recordBestEffortPostDispatchDiagnostic(admitted, env, `settlement read failed beside host terminal (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
539
426
|
}
|
|
540
427
|
if (settledOutcome !== undefined) {
|
|
541
428
|
// #855: final cancel re-read before lawful seal — settlement window may
|
|
@@ -552,66 +439,46 @@ async function settleCompletedHostTurn(input) {
|
|
|
552
439
|
persistRunState,
|
|
553
440
|
deferredPersist,
|
|
554
441
|
invocationScopeId: request.invocationScopeId,
|
|
442
|
+
notePackageFault: courtScope.notePackageFault,
|
|
555
443
|
})) };
|
|
556
444
|
}
|
|
557
445
|
if (persistRunState) {
|
|
558
446
|
try {
|
|
559
|
-
await persistReturnedRunState(admitted
|
|
447
|
+
await persistReturnedRunState(admitted);
|
|
560
448
|
}
|
|
561
449
|
catch (error) {
|
|
562
450
|
await recordBestEffortPostDispatchDiagnostic(admitted, env, `run-state persistence failed beside host terminal (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
563
451
|
}
|
|
564
452
|
}
|
|
565
|
-
//
|
|
453
|
+
// Persist + present is the caller's stop seam (auto-resume loop /
|
|
566
454
|
// manual resume), not this retried host-turn function.
|
|
567
455
|
// #855 cancel re-read lives in finishAfterTurn (before afterDispatch).
|
|
456
|
+
input.markPending(settledOutcome.terminal);
|
|
568
457
|
return { kind: "settled", outcome: await finishAfterTurn({ ...settledOutcome, ...deferredPersist }) };
|
|
569
458
|
}
|
|
570
459
|
// Host/runner true failure coexists with already-recorded payloads — never wash as accepted.
|
|
571
460
|
if (hostSignalFailed) {
|
|
572
|
-
const
|
|
573
|
-
const processCancelName = processCancelSignalName(env.signal);
|
|
574
|
-
const stderrLogWriteDetails = stderrLogWriteFailure === undefined
|
|
461
|
+
const stderrLogWriteFact = stderrLogWriteFailure === undefined
|
|
575
462
|
? undefined
|
|
576
463
|
: { stderrLogWriteFailure: describeCaughtError(stderrLogWriteFailure) };
|
|
577
464
|
const failed = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({
|
|
578
465
|
timedOut: result.timedOut,
|
|
579
466
|
code: result.code,
|
|
580
467
|
stderr: result.stderr,
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
//
|
|
587
|
-
//
|
|
588
|
-
...(
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
...resolutionInput.knownFailure,
|
|
594
|
-
details: { ...(resolutionInput.knownFailure.details ?? {}), ...stderrLogWriteDetails },
|
|
595
|
-
},
|
|
596
|
-
}
|
|
597
|
-
: { knownDetails: stderrLogWriteDetails }),
|
|
598
|
-
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
468
|
+
// A signal-killed child is a fact of its own. The synthetic line is
|
|
469
|
+
// only a fallback for when the host declared no failure of its own —
|
|
470
|
+
// it must never displace words the host actually reported.
|
|
471
|
+
...(result.signal === undefined ? {} : { signal: result.signal }),
|
|
472
|
+
...(hostReportedFailure === undefined ? {} : { knownFailure: hostReportedFailure }),
|
|
473
|
+
// The cancellation is the host's own report of why this call ended;
|
|
474
|
+
// it supplies the wording only when the host declared nothing else.
|
|
475
|
+
...(processCancelName === undefined ? {} : { cancelName: processCancelName }),
|
|
476
|
+
// Secondary fact only — never the cause, and never merged into the
|
|
477
|
+
// host's own details.
|
|
478
|
+
...(stderrLogWriteFact === undefined ? {} : { packageFact: stderrLogWriteFact }),
|
|
479
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState, courtScope.notePackageFault);
|
|
599
480
|
return { kind: "settled", outcome: await finishAfterTurn(withProcessCancelSkipAutoResume({ ...failed, turnDispatched: true, ...deferredPersist }, env.signal)) };
|
|
600
481
|
}
|
|
601
|
-
// Nothing else was wrong, but the durable stderr mirror itself failed to
|
|
602
|
-
// write — that is the real (infrastructure) problem in this case, not a
|
|
603
|
-
// lawful absence of a receipt. Honest and loud, not silently no_receipt.
|
|
604
|
-
if (stderrLogWriteFailure !== undefined) {
|
|
605
|
-
const failed = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({
|
|
606
|
-
timedOut: false,
|
|
607
|
-
code: result.code,
|
|
608
|
-
stderr: result.stderr,
|
|
609
|
-
thrown: stderrLogWriteFailure,
|
|
610
|
-
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
611
|
-
return { kind: "settled", outcome: await finishAfterTurn({ ...failed, turnDispatched: true, ...deferredPersist }) };
|
|
612
|
-
}
|
|
613
|
-
// No receipt at all after a clean host turn: lawful no_receipt, but the
|
|
614
|
-
// caller owns whether to催交 first (票面 3).
|
|
615
482
|
return { kind: "still-missing" };
|
|
616
483
|
}
|
|
617
484
|
export async function dispatchPostAdmissionTurn(input) {
|
|
@@ -625,6 +492,18 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
625
492
|
* lease release — success and failure alike. When a primary failure terminal already
|
|
626
493
|
* exists, keep that cause and leave relocate failure on the shared diagnostic channel.
|
|
627
494
|
*/
|
|
495
|
+
const courtScope = {
|
|
496
|
+
...settlementScopeForTurn(request),
|
|
497
|
+
notePackageFault: (diagnostic) => recordBestEffortPostDispatchDiagnostic(admitted, env, diagnostic, io),
|
|
498
|
+
};
|
|
499
|
+
let pendingSettlement;
|
|
500
|
+
/**
|
|
501
|
+
* The terminal this pending publication belongs to. A later real failure
|
|
502
|
+
* (e.g. a cancel re-read) replaces `result.terminal` with its own settled
|
|
503
|
+
* failure; that failure is already durable, so the superseded preview must
|
|
504
|
+
* not publish over it (失败诚实宪法:接住可以,洗白不行).
|
|
505
|
+
*/
|
|
506
|
+
let pendingTerminal;
|
|
628
507
|
let afterDispatchApplied = false;
|
|
629
508
|
const receiptDelivery = input.receiptDelivery ?? createReceiptDeliveryPolicy(env.autoResumeLimit);
|
|
630
509
|
let turnDispatched = false;
|
|
@@ -638,8 +517,9 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
638
517
|
if (afterDispatchApplied)
|
|
639
518
|
return result;
|
|
640
519
|
afterDispatchApplied = true;
|
|
641
|
-
// #855: re-read cancel before
|
|
642
|
-
//
|
|
520
|
+
// #855: re-read cancel before committing the terminal and afterDispatch.
|
|
521
|
+
// A signal after the history-first commit cannot retroactively erase it.
|
|
522
|
+
// skipRunStateWrite: run-state
|
|
643
523
|
// may already be terminal from a prior lawful persist; do not reopen as resumable.
|
|
644
524
|
const cancelBeforeFinish = processCancelSignalName(env.signal);
|
|
645
525
|
if (cancelBeforeFinish !== undefined &&
|
|
@@ -651,13 +531,43 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
651
531
|
code: null,
|
|
652
532
|
stderr: "",
|
|
653
533
|
knownDiagnostic: processCancelDiagnostic(cancelBeforeFinish),
|
|
534
|
+
cancelName: cancelBeforeFinish,
|
|
654
535
|
skipRunStateWrite: true,
|
|
655
|
-
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState)),
|
|
536
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState, courtScope.notePackageFault)),
|
|
656
537
|
turnDispatched: true,
|
|
657
538
|
skipAutoResume: true,
|
|
658
539
|
...deferredPersist,
|
|
659
540
|
};
|
|
660
541
|
}
|
|
542
|
+
// The last cancellation check precedes the one durable settlement. A preview
|
|
543
|
+
// never publishes pointers or history; a superseding failure owns this turn —
|
|
544
|
+
// `result.terminal === pendingTerminal` is what makes the publication this
|
|
545
|
+
// turn's own rather than a superseded one.
|
|
546
|
+
if (pendingSettlement !== undefined &&
|
|
547
|
+
pendingTerminal !== undefined &&
|
|
548
|
+
result.terminal === pendingTerminal) {
|
|
549
|
+
try {
|
|
550
|
+
const terminal = pendingSettlement === "sealed"
|
|
551
|
+
? await adapters.trySettle(admitted, env.principalAuthority, { ...courtScope, recordAttemptHistory: true })
|
|
552
|
+
: await attachRecordedSubmissions(admitted, await settleHostEndedNoReceipt(admitted, env.principalAuthority, { ...courtScope, recordAttemptHistory: true }, receiptDelivery.issuedDeliveryRequests()), courtScope);
|
|
553
|
+
if (terminal === undefined)
|
|
554
|
+
throw new Error("settled host attempt vanished before publication");
|
|
555
|
+
result = { ...result, terminal, exitCode: exitCodeForTerminalOutcome(terminal.roleOutcome) };
|
|
556
|
+
if (pendingSettlement === "sealed" && terminal.roleOutcome.kind === "accepted"
|
|
557
|
+
&& request.courtAttemptId !== undefined && request.courtAttemptId.length > 0) {
|
|
558
|
+
try {
|
|
559
|
+
await clearCurrentCourt(admitted.runDirectory, request.courtAttemptId);
|
|
560
|
+
}
|
|
561
|
+
catch (error) {
|
|
562
|
+
// The accepted attempt is already durable; cleanup is diagnostic only.
|
|
563
|
+
await recordBestEffortPostDispatchDiagnostic(admitted, env, `current-court cleanup failed after accepted settlement (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
catch (error) {
|
|
568
|
+
await recordBestEffortPostDispatchDiagnostic(admitted, env, `terminal publication failed beside host terminal (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
661
571
|
try {
|
|
662
572
|
// #858 / #1071: the first identifiable ticketNumber field declaration
|
|
663
573
|
// files an unbound run (integer, digit string, or leading #N token).
|
|
@@ -666,7 +576,7 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
666
576
|
// Later receipts remain untouched; prose note/report is never consulted;
|
|
667
577
|
// this seam does not adjudicate a ticket change.
|
|
668
578
|
if (admitted.ticketNumber === undefined) {
|
|
669
|
-
const rows = await readRecordedSubmissionRows(admitted.projectRoot, admitted.runId,
|
|
579
|
+
const rows = await readRecordedSubmissionRows(admitted.projectRoot, admitted.runId, ledgerReadScope(admitted));
|
|
670
580
|
let ticketNumber;
|
|
671
581
|
for (const row of rows) {
|
|
672
582
|
if (row.role !== admitted.role
|
|
@@ -708,7 +618,7 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
708
618
|
code: null,
|
|
709
619
|
stderr: "",
|
|
710
620
|
thrown: error,
|
|
711
|
-
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState)),
|
|
621
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState, courtScope.notePackageFault)),
|
|
712
622
|
...(result.turnDispatched === true ? { turnDispatched: true } : {}),
|
|
713
623
|
...(result.skipAutoResume === true ? { skipAutoResume: true } : {}),
|
|
714
624
|
...deferredPersist,
|
|
@@ -718,7 +628,6 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
718
628
|
try {
|
|
719
629
|
// #987: do not pre-block or classify host dispatch from AK credential
|
|
720
630
|
// catalog facts. Per-invocation runner/host evidence owns provider identity.
|
|
721
|
-
await clearTypedProviderHttpObservation(admitted.runDirectory);
|
|
722
631
|
// The authoritative host write (markRunRunning) is delayed to just before
|
|
723
632
|
// executeTurn — not merely past beforeDispatch (#840 r9 判词 class 2). Any
|
|
724
633
|
// pre-turn retry (beforeDispatch, continuation assembly) must not commit a host
|
|
@@ -740,7 +649,7 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
740
649
|
code: null,
|
|
741
650
|
stderr: "",
|
|
742
651
|
thrown: error,
|
|
743
|
-
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState));
|
|
652
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState, courtScope.notePackageFault));
|
|
744
653
|
return { ...settled, ...deferredPersist };
|
|
745
654
|
}
|
|
746
655
|
}
|
|
@@ -772,6 +681,10 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
772
681
|
// auto-resume re-enters this dispatch and must reuse the same scope.
|
|
773
682
|
let result;
|
|
774
683
|
try {
|
|
684
|
+
// A bare `ak-role resume` only forwards the caller's instruction to the
|
|
685
|
+
// host CLI's native resume. Nothing here may gate, redirect or reshape
|
|
686
|
+
// that dispatch on prior conclusions, row counts or report presence;
|
|
687
|
+
// the authoritative post-turn settlement reads whatever really happened.
|
|
775
688
|
recordRunStart(admitted.runDirectory);
|
|
776
689
|
turnDispatched = true;
|
|
777
690
|
result = await env.roleTurnHost.executeTurn(turnRequest);
|
|
@@ -782,10 +695,11 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
782
695
|
timedOut: false,
|
|
783
696
|
code: null,
|
|
784
697
|
stderr: "",
|
|
785
|
-
thrown
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
698
|
+
// The thrown value is the host's report. A cancel received on this
|
|
699
|
+
// call is a second fact; it must not become a new Error in its place.
|
|
700
|
+
...(processCancelName === undefined ? {} : { cancelName: processCancelName }),
|
|
701
|
+
thrown: error,
|
|
702
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState, courtScope.notePackageFault);
|
|
789
703
|
return await finishAfterTurn(withProcessCancelSkipAutoResume({ ...settled, turnDispatched: true, ...deferredPersist }, env.signal));
|
|
790
704
|
}
|
|
791
705
|
// #1132: settle this turn through the ONE post-turn settlement authority,
|
|
@@ -801,6 +715,8 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
801
715
|
persistRunState,
|
|
802
716
|
deferredPersist,
|
|
803
717
|
finishAfterTurn,
|
|
718
|
+
courtScope,
|
|
719
|
+
markPending: (terminal) => { pendingSettlement = "sealed"; pendingTerminal = terminal; },
|
|
804
720
|
});
|
|
805
721
|
if (settledTurn.kind === "settled")
|
|
806
722
|
return settledTurn.outcome;
|
|
@@ -863,6 +779,8 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
863
779
|
persistRunState: false,
|
|
864
780
|
deferredPersist,
|
|
865
781
|
finishAfterTurn,
|
|
782
|
+
courtScope,
|
|
783
|
+
markPending: (terminal) => { pendingSettlement = "sealed"; pendingTerminal = terminal; },
|
|
866
784
|
});
|
|
867
785
|
// The delivery turn's own outcome IS the run's outcome, except when it
|
|
868
786
|
// simply still has no receipt — that is the loop's own case to keep going.
|
|
@@ -875,9 +793,7 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
875
793
|
// NOT write; this one, which ends the run, must.
|
|
876
794
|
if (persistRunState && deliveryTurn.outcome.terminal !== undefined) {
|
|
877
795
|
try {
|
|
878
|
-
await persistReturnedRunState(admitted
|
|
879
|
-
lawful: isLawfulTypedTerminalOutcome(deliveryTurn.outcome.terminal.roleOutcome),
|
|
880
|
-
});
|
|
796
|
+
await persistReturnedRunState(admitted);
|
|
881
797
|
}
|
|
882
798
|
catch (error) {
|
|
883
799
|
await recordBestEffortPostDispatchDiagnostic(admitted, env, `run-state persistence failed beside催交 terminal (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
@@ -886,7 +802,7 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
886
802
|
return await finishAfterTurn(deliveryTurn.outcome);
|
|
887
803
|
}
|
|
888
804
|
}
|
|
889
|
-
const noReceiptScope =
|
|
805
|
+
const noReceiptScope = { ...courtScope, previewOnly: true };
|
|
890
806
|
const noReceipt = await attachRecordedSubmissions(admitted, await settleHostEndedNoReceipt(admitted, env.principalAuthority, noReceiptScope, receiptDelivery.issuedDeliveryRequests()), noReceiptScope);
|
|
891
807
|
// #855: cancel during no_receipt settlement window is not lawful success.
|
|
892
808
|
const noReceiptCancel = processCancelSignalName(env.signal);
|
|
@@ -901,17 +817,20 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
901
817
|
persistRunState,
|
|
902
818
|
deferredPersist,
|
|
903
819
|
invocationScopeId: request.invocationScopeId,
|
|
820
|
+
notePackageFault: courtScope.notePackageFault,
|
|
904
821
|
}));
|
|
905
822
|
}
|
|
906
823
|
if (persistRunState) {
|
|
907
824
|
try {
|
|
908
|
-
await persistReturnedRunState(admitted
|
|
825
|
+
await persistReturnedRunState(admitted);
|
|
909
826
|
}
|
|
910
827
|
catch (error) {
|
|
911
828
|
await recordBestEffortPostDispatchDiagnostic(admitted, env, `run-state persistence failed beside host terminal (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
912
829
|
}
|
|
913
830
|
}
|
|
914
831
|
// #855 cancel re-read lives in finishAfterTurn (before afterDispatch).
|
|
832
|
+
pendingSettlement = "no_receipt";
|
|
833
|
+
pendingTerminal = noReceipt;
|
|
915
834
|
return await finishAfterTurn({
|
|
916
835
|
exitCode: exitCodeForTerminalOutcome(noReceipt.roleOutcome),
|
|
917
836
|
admitted,
|
|
@@ -1008,7 +927,6 @@ export function resumeTurnRequestProjectionOptions(admitted, request, env, summo
|
|
|
1008
927
|
kind: "resume",
|
|
1009
928
|
prompt,
|
|
1010
929
|
},
|
|
1011
|
-
...(request.message === undefined ? {} : { courtAttemptId: randomUUID() }),
|
|
1012
930
|
...(env.stationChild === undefined ? {} : { stationChild: env.stationChild }),
|
|
1013
931
|
// #1132: one configured ceiling, already resolved by the caller (#422).
|
|
1014
932
|
deliveryRequestLimit: deliveryLimitFromConfig(env.autoResumeLimit),
|
|
@@ -1083,7 +1001,7 @@ async function runSettledAutoResumeLoop(input) {
|
|
|
1083
1001
|
? {}
|
|
1084
1002
|
: { effectiveEngine: input.effectiveEngine }),
|
|
1085
1003
|
});
|
|
1086
|
-
return settleDeferredPersist(input.admitted, input.env
|
|
1004
|
+
return settleDeferredPersist(input.admitted, input.env, attemptIo, result);
|
|
1087
1005
|
},
|
|
1088
1006
|
}),
|
|
1089
1007
|
});
|
|
@@ -1204,11 +1122,13 @@ export async function runPostAdmissionSeatResume(input) {
|
|
|
1204
1122
|
};
|
|
1205
1123
|
}
|
|
1206
1124
|
}
|
|
1207
|
-
// Open court continue, or new court for
|
|
1208
|
-
// (clause 0 新庭可再交卷; #833). Bare resume
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1125
|
+
// Open court continue, or a new court for a real re-summons
|
|
1126
|
+
// (clause 0 新庭可再交卷; #833). Bare resume — with or without caller
|
|
1127
|
+
// words — carries no summons and no open court, so it mints none:
|
|
1128
|
+
// the package must not turn ordinary continuation bytes into an audit
|
|
1129
|
+
// court whose attempt scope would reshape settlement (#1032 修订票:
|
|
1130
|
+
// 此要求不延伸到没有该传召庭次的裸 resume).
|
|
1131
|
+
if (openCourtAttemptId !== undefined || request.summons !== undefined) {
|
|
1212
1132
|
const courtAttemptId = openCourtAttemptId ??
|
|
1213
1133
|
(turnRequest.courtAttemptId !== undefined &&
|
|
1214
1134
|
turnRequest.courtAttemptId.length > 0
|
|
@@ -1328,8 +1248,7 @@ function writeResumeFailurePointer(io, errorPath) {
|
|
|
1328
1248
|
export function showResumeErrorPointer(io, exitCode, terminal) {
|
|
1329
1249
|
if (exitCode === 0 || terminal === undefined)
|
|
1330
1250
|
return;
|
|
1331
|
-
const publishedError = terminal.artifacts.find((artifact) => artifact.kind === "error")?.path
|
|
1332
|
-
?? publishedFailureErrorPath(terminal);
|
|
1251
|
+
const publishedError = terminal.artifacts.find((artifact) => artifact.kind === "error")?.path;
|
|
1333
1252
|
if (publishedError !== undefined)
|
|
1334
1253
|
writeResumeFailurePointer(io, publishedError);
|
|
1335
1254
|
}
|
|
@@ -1470,7 +1389,7 @@ export async function runPostAdmissionManualResume(input) {
|
|
|
1470
1389
|
// through its own settleAfterTurnStarted-backed branches; a lawful result
|
|
1471
1390
|
// only ever reaches this point once that persist has already succeeded
|
|
1472
1391
|
// (#836 r12 class 3 dedup — one persist owner, not a second here).
|
|
1473
|
-
|
|
1392
|
+
await presentTerminal(result.terminal, io, admitted.runDirectory);
|
|
1474
1393
|
}
|
|
1475
1394
|
if (result.terminal !== undefined) {
|
|
1476
1395
|
result.terminal.autoResumeCount = 0;
|