@akagilnc/pi-workflow-roles 0.1.5375 → 0.1.5406
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/README.md +2 -0
- package/README.zh-CN.md +2 -0
- package/dist/acp-host/production-host.js +6381 -5684
- package/dist/archivist-record-pointer.js +38 -0
- package/dist/collector-tool-schemas.js +18 -0
- package/dist/compliance-transport.js +5 -0
- package/dist/diarist-contracts.js +26 -0
- package/dist/doctor-auditor.js +1 -0
- package/dist/engine-detour-usage.js +6 -6
- package/dist/filed-submission.js +28 -0
- package/dist/gatekeeper-role.js +6 -10
- package/dist/gleaner-left-contracts.js +16 -0
- package/dist/headless-host/production-host.js +1644 -947
- package/dist/judge-role.js +21 -16
- package/dist/migrate-book-topology.js +452 -81
- package/dist/navigator-attendance.js +10 -9
- package/dist/navigator-invocation-identity.js +18 -0
- package/dist/package-contracts/reviewer-output.js +16 -0
- package/dist/package-contracts/worker-output.js +18 -2
- package/dist/pi/role-turn-host.js +9 -0
- package/dist/public-cli/cli.js +1 -0
- package/dist/public-cli/instruction-seat-run.js +258 -55
- package/dist/public-cli/main.js +1209 -346
- package/dist/public-cli/post-admission.js +427 -244
- package/dist/public-cli/settlement.js +111 -48
- package/dist/public-cli/terminal.js +12 -1
- package/dist/public-cli/turn-request.js +3 -0
- package/dist/receipt-delivery-policy.js +152 -17
- package/dist/role-submission-declarations.js +131 -0
- package/dist/secretariat-contracts.js +23 -1
- package/dist/submission-errors.js +11 -1
- package/dist/submission-gate.js +32 -12
- package/package.json +1 -1
- package/src/archivist-record-pointer.ts +48 -0
- package/src/auditor-role.ts +2 -8
- package/src/collector-role.ts +5 -28
- package/src/compliance-transport.ts +10 -2
- package/src/countersign-role.ts +2 -7
- package/src/diarist-contracts.ts +37 -0
- package/src/diarist-role.ts +4 -44
- package/src/doctor-auditor.ts +3 -0
- package/src/doctor-role.ts +16 -11
- package/src/engine-detour-usage.ts +7 -6
- package/src/external-host-turn-loop.ts +24 -12
- package/src/filed-submission.ts +44 -0
- package/src/gatekeeper-role.ts +8 -16
- package/src/gleaner-left-contracts.ts +26 -1
- package/src/gleaner-left-role.ts +4 -32
- package/src/host-contracts.ts +7 -0
- package/src/inspector-role.ts +2 -7
- package/src/judge-role.ts +31 -24
- package/src/merger-role.ts +5 -8
- package/src/navigator-attendance.ts +17 -12
- package/src/navigator-invocation-identity.ts +31 -0
- package/src/navigator-role.ts +2 -8
- package/src/notary-role.ts +5 -20
- package/src/package-contracts/reviewer-output.ts +25 -0
- package/src/package-contracts/worker-output.ts +43 -5
- package/src/pi/adapter.ts +33 -8
- package/src/pi/role-turn-host.ts +7 -0
- package/src/public-cli/cli.ts +1 -0
- package/src/public-cli/instruction-seat-run.ts +292 -61
- package/src/public-cli/post-admission.ts +556 -324
- package/src/public-cli/settlement.ts +112 -50
- package/src/public-cli/terminal.ts +16 -4
- package/src/public-cli/turn-request.ts +10 -1
- package/src/receipt-delivery-policy.ts +209 -17
- package/src/reviewer-role.ts +7 -35
- package/src/role-envelope.ts +5 -1
- package/src/role-runtime-dependencies.ts +1 -0
- package/src/role-runtime.ts +72 -56
- package/src/role-submission-declarations.ts +143 -0
- package/src/secretariat-contracts.ts +35 -0
- package/src/secretariat-role.ts +4 -40
- package/src/submission-correctable-error.ts +5 -2
- package/src/submission-errors.ts +12 -1
- package/src/submission-gate.ts +41 -12
- package/src/worker-role.ts +81 -86
- package/src/worker-submission-gates.ts +32 -7
|
@@ -17,6 +17,7 @@ import { readDeclaredTicketNumber } from "../run-ticket-number.js";
|
|
|
17
17
|
import { pathContainedIn } from "../activation-ledger-topology.js";
|
|
18
18
|
import { pickEngineAxis } from "../package-resources/engine-material.js";
|
|
19
19
|
import { readStoredHostSessionId } from "../session-identity.js";
|
|
20
|
+
import { createReceiptDeliveryPolicy, deliveryLimitFromConfig, } from "../receipt-delivery-policy.js";
|
|
20
21
|
import { rewriteRunDirectoryPathValue } from "../role-run-relocation.js";
|
|
21
22
|
import { isOfficerReviewSeat } from "../packaged-role-registry.js";
|
|
22
23
|
/** Original error bytes, never relabeled — a secondary fact riding beside a classified cause. */
|
|
@@ -39,6 +40,7 @@ import { mintEngineDetourInvocationScope, withEngineDetourInvocationScope, } fro
|
|
|
39
40
|
import { clearCurrentCourt, clearTypedProviderHttpObservation, describeErrorIdentity, markRunRunning, readCurrentCourt, recordCurrentCourt, renderResumeCommand, } from "./run-lifecycle.js";
|
|
40
41
|
import { processCancelDiagnostic, processCancelSignalName, } from "./process-cancel.js";
|
|
41
42
|
import { homeFromRunDirectory } from "../activation-ledger-topology.js";
|
|
43
|
+
import { DEFAULT_ROLE_TURN_HOST } from "../host-descriptions.js";
|
|
42
44
|
import { recordRunStart } from "../host-session-record.js";
|
|
43
45
|
import { attemptProducedFreshSubmission, classifyPostAdmissionFailure, controlledFailureInputFromResolution, exitCodeForTerminalOutcome, explicitInternalKnownFailureClassificationInput, formatCliDiagnostic, formatErrorCauseDetail, formatTerminalResult, inspectJudgeSession, isLawfulTypedTerminalOutcome, presentFailureTerminal, publishedFailureErrorPath, presentStructuralRejection, resolveAuditedRunnerFailureResolution, resolveControlledFailureResumeObservation, settleFailureTerminalResult, settleHostEndedNoReceipt, attachRecordedSubmissions, rewritePublishedFailureErrorPath, } from "./settlement.js";
|
|
44
46
|
import {} from "./terminal.js";
|
|
@@ -298,9 +300,320 @@ async function settleDeferredPersist(admitted, authority, adapters, io, result)
|
|
|
298
300
|
}
|
|
299
301
|
catch (error) {
|
|
300
302
|
const failed = await settleAfterTurnStarted(admitted, { timedOut: false, code: null, stderr: "", thrown: error, skipRunStateWrite: true }, adapters, authority, io, true);
|
|
301
|
-
return {
|
|
303
|
+
return {
|
|
304
|
+
...failed,
|
|
305
|
+
turnDispatched: true,
|
|
306
|
+
skipAutoResume: true,
|
|
307
|
+
};
|
|
302
308
|
}
|
|
303
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* Court/invocation scope for settlement reads. The one construction of this
|
|
312
|
+
* shape for every turn (#1132 — the first turn and each 催交 turn share it).
|
|
313
|
+
*/
|
|
314
|
+
function settlementScopeForTurn(request) {
|
|
315
|
+
const hasCourt = request.courtAttemptId !== undefined && request.courtAttemptId.length > 0;
|
|
316
|
+
const hasScope = request.invocationScopeId !== undefined && request.invocationScopeId.length > 0;
|
|
317
|
+
if (!hasCourt && !hasScope)
|
|
318
|
+
return undefined;
|
|
319
|
+
return {
|
|
320
|
+
...(hasCourt ? { courtAttemptId: request.courtAttemptId } : {}),
|
|
321
|
+
...(hasScope ? { invocationScopeId: request.invocationScopeId } : {}),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* #1132: one 没交卷催交 turn, dispatched through the SAME settlement path as the
|
|
326
|
+
* first turn.
|
|
327
|
+
*
|
|
328
|
+
* There is deliberately no second settlement implementation here. A delivery
|
|
329
|
+
* turn is an ordinary host turn on this same run/session: this builds the
|
|
330
|
+
* host-neutral resume request (the adapter's stored native session id, ADR 0082;
|
|
331
|
+
* pi's existing delivery-state content reused verbatim — no new prompt wording,
|
|
332
|
+
* no new host capability). The caller settles it through the same
|
|
333
|
+
* `settleCompletedHostTurn` the first turn uses (ADR 0080): the runner/host
|
|
334
|
+
* failure re-read, the shouldPresent gate, the fresh-seal check, open-court
|
|
335
|
+
* cleanup, cancel re-reads, run-state persist, and the stderr mirror.
|
|
336
|
+
*
|
|
337
|
+
* The caller owns the live delivery policy; the request projects its next send.
|
|
338
|
+
*/
|
|
339
|
+
async function buildReceiptDeliveryRequest(input) {
|
|
340
|
+
const { admitted, env, request, receiptDelivery } = input;
|
|
341
|
+
const hostSessionId = await readStoredHostSessionId(env.host, env.principalAuthority, admitted.principal);
|
|
342
|
+
return {
|
|
343
|
+
...request,
|
|
344
|
+
continuation: {
|
|
345
|
+
kind: "resume",
|
|
346
|
+
prompt: JSON.stringify({
|
|
347
|
+
...receiptDelivery.deliveryState(),
|
|
348
|
+
deliveryTurns: receiptDelivery.issuedDeliveryRequests() + 1,
|
|
349
|
+
}),
|
|
350
|
+
...(hostSessionId === undefined ? {} : { hostSessionId }),
|
|
351
|
+
},
|
|
352
|
+
deliveryRequestLimit: receiptDelivery.limit,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* #1132: settle ONE completed host turn — the single post-turn settlement
|
|
357
|
+
* authority shared by the first turn and by every 没交卷催交 turn (ADR 0080).
|
|
358
|
+
*
|
|
359
|
+
* Extracted deliberately instead of re-entering `dispatchPostAdmissionTurn`.
|
|
360
|
+
* The turn-BEFORE concerns (beforeDispatch / markRunRunning / relocate /
|
|
361
|
+
* afterDispatch / writer-lease release) belong to the outermost dispatch and
|
|
362
|
+
* run exactly once per run; everything from the stderr mirror onward is
|
|
363
|
+
* per-turn. A 催交 turn is a real host turn on this same run/session, so it
|
|
364
|
+
* settles through THIS function — not a reduced copy — and it re-runs neither
|
|
365
|
+
* the admission hooks nor the run-state terminal write.
|
|
366
|
+
*
|
|
367
|
+
* Returns `still-missing` when the turn simply produced no receipt: the caller
|
|
368
|
+
* (first turn or the 催交 loop) then decides between issuing another delivery
|
|
369
|
+
* request and settling no_receipt.
|
|
370
|
+
*/
|
|
371
|
+
async function settleCompletedHostTurn(input) {
|
|
372
|
+
const { admitted, env, io, adapters, shouldPresent, request, result, persistRunState, deferredPersist } = input;
|
|
373
|
+
const finishAfterTurn = input.finishAfterTurn;
|
|
374
|
+
// `result.stderr` stays live in memory for the real classification below
|
|
375
|
+
// regardless of whether this durable mirror write succeeds. A write
|
|
376
|
+
// failure here (unwritable run directory, stderr.log occupied as a
|
|
377
|
+
// directory, ...) is a real infrastructure problem and must never be
|
|
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.
|
|
385
|
+
//
|
|
386
|
+
// Everything below runs after the host turn genuinely started (#840 r9
|
|
387
|
+
// 判词 class 1 boundary — 覆盖 executeTurn 已启动后至返回带 turnDispatched
|
|
388
|
+
// 结果前的全部异常). Each fallible step routes any failure through the
|
|
389
|
+
// single existing settlement authority (presentControlledFailure /
|
|
390
|
+
// settleFailureTerminalResult) rather than a second one (ADR 0080:
|
|
391
|
+
// one settlement disposition owner) — never by fabricating a terminal here.
|
|
392
|
+
// A cleanup step that runs only after a real settlement (clearCurrentCourt)
|
|
393
|
+
// is protected by logging and keeping that already-obtained result, never
|
|
394
|
+
// by discarding it (#840 已交劳动只整理终局不重做). A failure inside the
|
|
395
|
+
// settlement authority itself (presentControlledFailure's own reads) goes
|
|
396
|
+
// through settleAfterTurnStarted: still no fabricated terminal, but the
|
|
397
|
+
// re-thrown TurnDispatchedFailure still tells runWithAutoResumeLoop the
|
|
398
|
+
// turn genuinely started, so the next retry sends a resume payload — the
|
|
399
|
+
// true cause settles through the loop's own existing dispatch-exception
|
|
400
|
+
// machinery once the retry budget is exhausted.
|
|
401
|
+
let stderrLogWriteFailure;
|
|
402
|
+
try {
|
|
403
|
+
await writeFile(join(admitted.runDirectory, "stderr.log"), result.stderr, "utf8");
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
stderrLogWriteFailure = error;
|
|
407
|
+
// Best-effort: the turn's own stderr capture is secondary to lawful /
|
|
408
|
+
// controlled-failure settlement below, but the failure itself must
|
|
409
|
+
// still leave a real trace (失败诚实宪法 真因必须落痕) — durably, since
|
|
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).
|
|
413
|
+
await recordBestEffortPostDispatchDiagnostic(admitted, env, `stderr.log write failed (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
414
|
+
}
|
|
415
|
+
const courtScope = settlementScopeForTurn(request);
|
|
416
|
+
// Single complete boundary (#840 r9 判词 class 1): resolving the
|
|
417
|
+
// host/runner failure facts, trySettle, its shouldPresent gate, and the
|
|
418
|
+
// accepted-settlement cleanup all settle through this one catch — a
|
|
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.
|
|
426
|
+
let settled;
|
|
427
|
+
let settledOutcome;
|
|
428
|
+
let hostSignalFailed = false;
|
|
429
|
+
let resolution;
|
|
430
|
+
try {
|
|
431
|
+
const sessionFile = admitted.principal !== undefined
|
|
432
|
+
? env.principalAuthority.decode(admitted.principal).sessionFile
|
|
433
|
+
: "";
|
|
434
|
+
const runnerKnownFailure = adapters.resolveRunnerKnownFailure !== undefined && sessionFile !== ""
|
|
435
|
+
? await adapters.resolveRunnerKnownFailure({ result, sessionFile })
|
|
436
|
+
: result.knownFailure;
|
|
437
|
+
resolution = await resolveAuditedRunnerFailureResolution({
|
|
438
|
+
runner: runnerKnownFailure,
|
|
439
|
+
sessionFile,
|
|
440
|
+
credential: undefined,
|
|
441
|
+
runDirectory: admitted.runDirectory,
|
|
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
|
+
}
|
|
520
|
+
}
|
|
521
|
+
settledOutcome = {
|
|
522
|
+
exitCode: exitCodeForTerminalOutcome(settled.roleOutcome),
|
|
523
|
+
admitted,
|
|
524
|
+
terminal: settled,
|
|
525
|
+
turnDispatched: true,
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
catch (error) {
|
|
530
|
+
// Settle (or its shouldPresent gate, or the failure-fact resolution
|
|
531
|
+
// above) throw is a real failure fact — never swallow into undefined.
|
|
532
|
+
const settledFailure = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({
|
|
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 }) };
|
|
539
|
+
}
|
|
540
|
+
if (settledOutcome !== undefined) {
|
|
541
|
+
// #855: final cancel re-read before lawful seal — settlement window may
|
|
542
|
+
// have received SIGTERM/SIGINT/SIGHUP after the earlier snapshot.
|
|
543
|
+
const lateCancel = processCancelSignalName(env.signal);
|
|
544
|
+
if (lateCancel !== undefined) {
|
|
545
|
+
return { kind: "settled", outcome: await finishAfterTurn(await settleProcessCancelAfterTurn({
|
|
546
|
+
admitted,
|
|
547
|
+
cancelName: lateCancel,
|
|
548
|
+
result,
|
|
549
|
+
adapters,
|
|
550
|
+
env,
|
|
551
|
+
io,
|
|
552
|
+
persistRunState,
|
|
553
|
+
deferredPersist,
|
|
554
|
+
invocationScopeId: request.invocationScopeId,
|
|
555
|
+
})) };
|
|
556
|
+
}
|
|
557
|
+
if (persistRunState) {
|
|
558
|
+
try {
|
|
559
|
+
await persistReturnedRunState(admitted, { lawful: true });
|
|
560
|
+
}
|
|
561
|
+
catch (error) {
|
|
562
|
+
await recordBestEffortPostDispatchDiagnostic(admitted, env, `run-state persistence failed beside host terminal (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
// Lawful persist + present is the caller's stop seam (auto-resume loop /
|
|
566
|
+
// manual resume), not this retried host-turn function.
|
|
567
|
+
// #855 cancel re-read lives in finishAfterTurn (before afterDispatch).
|
|
568
|
+
return { kind: "settled", outcome: await finishAfterTurn({ ...settledOutcome, ...deferredPersist }) };
|
|
569
|
+
}
|
|
570
|
+
// Host/runner true failure coexists with already-recorded payloads — never wash as accepted.
|
|
571
|
+
if (hostSignalFailed) {
|
|
572
|
+
const resolutionInput = controlledFailureInputFromResolution(resolution);
|
|
573
|
+
const processCancelName = processCancelSignalName(env.signal);
|
|
574
|
+
const stderrLogWriteDetails = stderrLogWriteFailure === undefined
|
|
575
|
+
? undefined
|
|
576
|
+
: { stderrLogWriteFailure: describeCaughtError(stderrLogWriteFailure) };
|
|
577
|
+
const failed = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({
|
|
578
|
+
timedOut: result.timedOut,
|
|
579
|
+
code: result.code,
|
|
580
|
+
stderr: result.stderr,
|
|
581
|
+
...resolutionInput,
|
|
582
|
+
...(processCancelName === undefined
|
|
583
|
+
? {}
|
|
584
|
+
: { knownDiagnostic: processCancelDiagnostic(processCancelName) }),
|
|
585
|
+
// Secondary fact only — never the cause. Rides on whichever channel
|
|
586
|
+
// classification actually reads (knownFailure.details owns it when
|
|
587
|
+
// a knownFailure exists; the top-level knownDetails otherwise).
|
|
588
|
+
...(stderrLogWriteDetails === undefined
|
|
589
|
+
? {}
|
|
590
|
+
: resolutionInput.knownFailure !== undefined
|
|
591
|
+
? {
|
|
592
|
+
knownFailure: {
|
|
593
|
+
...resolutionInput.knownFailure,
|
|
594
|
+
details: { ...(resolutionInput.knownFailure.details ?? {}), ...stderrLogWriteDetails },
|
|
595
|
+
},
|
|
596
|
+
}
|
|
597
|
+
: { knownDetails: stderrLogWriteDetails }),
|
|
598
|
+
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
599
|
+
return { kind: "settled", outcome: await finishAfterTurn(withProcessCancelSkipAutoResume({ ...failed, turnDispatched: true, ...deferredPersist }, env.signal)) };
|
|
600
|
+
}
|
|
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
|
+
return { kind: "still-missing" };
|
|
616
|
+
}
|
|
304
617
|
export async function dispatchPostAdmissionTurn(input) {
|
|
305
618
|
const { admitted, env, io, request, lease, adapters, effectiveEngine } = input;
|
|
306
619
|
const persistRunState = input.persistRunState !== false;
|
|
@@ -313,6 +626,14 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
313
626
|
* exists, keep that cause and leave relocate failure on the shared diagnostic channel.
|
|
314
627
|
*/
|
|
315
628
|
let afterDispatchApplied = false;
|
|
629
|
+
const receiptDelivery = input.receiptDelivery ?? createReceiptDeliveryPolicy(env.autoResumeLimit);
|
|
630
|
+
let turnDispatched = false;
|
|
631
|
+
// #1132: the AK-side 催交 covers hosts that do not deliver to themselves.
|
|
632
|
+
// Pi runs the package role runtime in-process, and that runtime already issues
|
|
633
|
+
// its own delivery requests at `agent_end` and records the real count — asking
|
|
634
|
+
// again here would double-deliver on one run. External hosts (ACP / headless
|
|
635
|
+
// CLI) have no such in-child loop, so the AK execution seam owns it for them.
|
|
636
|
+
const akSeamDeliversReceipt = env.host !== undefined && env.host !== "" && env.host !== DEFAULT_ROLE_TURN_HOST;
|
|
316
637
|
const finishAfterTurn = async (result) => {
|
|
317
638
|
if (afterDispatchApplied)
|
|
318
639
|
return result;
|
|
@@ -452,6 +773,7 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
452
773
|
let result;
|
|
453
774
|
try {
|
|
454
775
|
recordRunStart(admitted.runDirectory);
|
|
776
|
+
turnDispatched = true;
|
|
455
777
|
result = await env.roleTurnHost.executeTurn(turnRequest);
|
|
456
778
|
}
|
|
457
779
|
catch (error) {
|
|
@@ -466,257 +788,106 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
466
788
|
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
467
789
|
return await finishAfterTurn(withProcessCancelSkipAutoResume({ ...settled, turnDispatched: true, ...deferredPersist }, env.signal));
|
|
468
790
|
}
|
|
469
|
-
//
|
|
470
|
-
//
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
//
|
|
486
|
-
//
|
|
487
|
-
//
|
|
488
|
-
//
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
//
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
// never presented over a real current host/runner failure signal or a
|
|
530
|
-
// real stderr.log durable-write failure — both stay a real failure with
|
|
531
|
-
// the recorded payload riding beside it, not replacing it.
|
|
532
|
-
let settled;
|
|
533
|
-
let settledOutcome;
|
|
534
|
-
let hostSignalFailed = false;
|
|
535
|
-
let resolution;
|
|
536
|
-
try {
|
|
537
|
-
const sessionFile = admitted.principal !== undefined
|
|
538
|
-
? env.principalAuthority.decode(admitted.principal).sessionFile
|
|
539
|
-
: "";
|
|
540
|
-
const runnerKnownFailure = adapters.resolveRunnerKnownFailure !== undefined && sessionFile !== ""
|
|
541
|
-
? await adapters.resolveRunnerKnownFailure({ result, sessionFile })
|
|
542
|
-
: result.knownFailure;
|
|
543
|
-
resolution = await resolveAuditedRunnerFailureResolution({
|
|
544
|
-
runner: runnerKnownFailure,
|
|
545
|
-
sessionFile,
|
|
546
|
-
credential: undefined,
|
|
547
|
-
runDirectory: admitted.runDirectory,
|
|
548
|
-
});
|
|
549
|
-
// A direct, current signal from the host/runner itself (timeout / host
|
|
550
|
-
// knownFailure / runner knownFailure / missing credential) is a real
|
|
551
|
-
// problem regardless of what else already settled — a settled accepted
|
|
552
|
-
// outcome must not paper over it (it rides beside the recorded payload
|
|
553
|
-
// via `submissions`, never replacing the payload). The audited
|
|
554
|
-
// resolution's own typed session read (malformed JSONL, provider-stop,
|
|
555
|
-
// an illegal/unsealed accepted status, a stale/superseded typed-HTTP
|
|
556
|
-
// observation, ...) makes a run with no sealed acceptance a true
|
|
557
|
-
// failure too — it must not be discarded into a lawful no_receipt just
|
|
558
|
-
// because the raw runner signal alone looked clean. But it must not
|
|
559
|
-
// retroactively invalidate an acceptance that already sealed this turn
|
|
560
|
-
// — a resolved 429 observed earlier in the same session is exactly
|
|
561
|
-
// that.
|
|
562
|
-
const processCancelName = processCancelSignalName(env.signal);
|
|
563
|
-
const directHostFailureSignal = result.timedOut
|
|
564
|
-
|| result.knownFailure !== undefined
|
|
565
|
-
|| runnerKnownFailure !== undefined
|
|
566
|
-
|| processCancelName !== undefined;
|
|
567
|
-
hostSignalFailed =
|
|
568
|
-
directHostFailureSignal
|
|
569
|
-
|| (result.code !== null && result.code !== 0)
|
|
570
|
-
|| resolution.knownFailure !== undefined;
|
|
571
|
-
settled = await adapters.trySettle(admitted, env.principalAuthority, courtScope);
|
|
572
|
-
if (settled !== undefined) {
|
|
573
|
-
settled = await attachRecordedSubmissions(admitted, settled, courtScope);
|
|
791
|
+
// #1132: settle this turn through the ONE post-turn settlement authority,
|
|
792
|
+
// then — if it simply produced no receipt — 催交 before settling no_receipt.
|
|
793
|
+
const settledTurn = await settleCompletedHostTurn({
|
|
794
|
+
admitted,
|
|
795
|
+
env,
|
|
796
|
+
io,
|
|
797
|
+
adapters,
|
|
798
|
+
shouldPresent,
|
|
799
|
+
request,
|
|
800
|
+
result,
|
|
801
|
+
persistRunState,
|
|
802
|
+
deferredPersist,
|
|
803
|
+
finishAfterTurn,
|
|
804
|
+
});
|
|
805
|
+
if (settledTurn.kind === "settled")
|
|
806
|
+
return settledTurn.outcome;
|
|
807
|
+
// #1132: 没交卷先回本人催交,不直接结为 no_receipt. The AK execution seam
|
|
808
|
+
// owns the 催交 and its real count. Each request is a real host turn on this
|
|
809
|
+
// same run/session, dispatched and settled through the same two seams as the
|
|
810
|
+
// first turn — never a reduced copy. 催交 never substitutes for host failure
|
|
811
|
+
// recovery: a delivery turn that fails keeps its true cause below.
|
|
812
|
+
// The delivery budget is spent HERE, at the outermost level; only when it is
|
|
813
|
+
// exhausted with still no receipt does the settlement seam record no_receipt.
|
|
814
|
+
while (akSeamDeliversReceipt && receiptDelivery.nextAction() === "request-delivery") {
|
|
815
|
+
// Assembly sits in the same post-start catch as the host call. A throw
|
|
816
|
+
// here is a started turn: resume the session, and do not count a request
|
|
817
|
+
// that never reached the host.
|
|
818
|
+
let deliveryResult;
|
|
819
|
+
let deliveryTurnRequest;
|
|
820
|
+
try {
|
|
821
|
+
const deliveryRequest = await buildReceiptDeliveryRequest({
|
|
822
|
+
admitted,
|
|
823
|
+
env,
|
|
824
|
+
// Host and the other axes already selected on this turn ride the
|
|
825
|
+
// shared projection. The pre-projection request does not have them.
|
|
826
|
+
request: turnRequest,
|
|
827
|
+
receiptDelivery,
|
|
828
|
+
});
|
|
829
|
+
// The same native-resume projection the first turn uses (host adapter owns
|
|
830
|
+
// the resume; AK only hands it the request — ADR 0082).
|
|
831
|
+
deliveryTurnRequest = {
|
|
832
|
+
...deliveryRequest,
|
|
833
|
+
principal: admitted.principal,
|
|
834
|
+
runDirectory: admitted.runDirectory,
|
|
835
|
+
...(env.signal === undefined ? {} : { signal: env.signal }),
|
|
836
|
+
};
|
|
837
|
+
receiptDelivery.recordDeliveryRequest();
|
|
838
|
+
deliveryResult = await env.roleTurnHost.executeTurn(deliveryTurnRequest);
|
|
839
|
+
}
|
|
840
|
+
catch (error) {
|
|
841
|
+
// 催交 cannot substitute for host failure recovery: a real failure with
|
|
842
|
+
// its true cause, through the one settlement authority (ADR 0080).
|
|
843
|
+
// The turn started, so the caller's loop resumes instead of replaying
|
|
844
|
+
// the initial payload. The issued count survives that re-entry.
|
|
845
|
+
const settled = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({ timedOut: false, code: null, stderr: "", thrown: error }, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
846
|
+
return await finishAfterTurn(withProcessCancelSkipAutoResume({
|
|
847
|
+
...settled,
|
|
848
|
+
turnDispatched: true,
|
|
849
|
+
...deferredPersist,
|
|
850
|
+
}, env.signal));
|
|
574
851
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
//
|
|
591
|
-
//
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
// by someone else's earlier success (#836 r12 class 2).
|
|
601
|
-
const staleAcceptanceOutranksRealFailure = !settledIsFreshThisAttempt && hostSignalFailed;
|
|
602
|
-
// #855: re-read cancel after trySettle/attach awaits — a signal in this
|
|
603
|
-
// window must not land as lawful accepted.
|
|
604
|
-
const cancelAfterSettle = processCancelSignalName(env.signal);
|
|
605
|
-
if (settled !== undefined
|
|
606
|
-
&& shouldPresent(settled)
|
|
607
|
-
&& !directHostFailureSignal
|
|
608
|
-
&& !staleAcceptanceOutranksRealFailure
|
|
609
|
-
&& stderrLogWriteFailure === undefined
|
|
610
|
-
&& cancelAfterSettle === undefined) {
|
|
611
|
-
// This court sealed — drop open-court pointer (bare resume no longer continues it).
|
|
612
|
-
if (settled.roleOutcome.kind === "accepted" &&
|
|
613
|
-
request.courtAttemptId !== undefined &&
|
|
614
|
-
request.courtAttemptId.length > 0) {
|
|
852
|
+
const deliveryTurn = await settleCompletedHostTurn({
|
|
853
|
+
admitted,
|
|
854
|
+
env,
|
|
855
|
+
// Only the final Terminal presents; intermediate delivery attempts stay quiet.
|
|
856
|
+
io: { ...io, stdout: () => { } },
|
|
857
|
+
adapters,
|
|
858
|
+
shouldPresent,
|
|
859
|
+
request: deliveryTurnRequest,
|
|
860
|
+
result: deliveryResult,
|
|
861
|
+
// Run-state terminal write belongs to the OUTERMOST turn only, so a
|
|
862
|
+
// still-silent delivery attempt never makes the run look lawfully terminal.
|
|
863
|
+
persistRunState: false,
|
|
864
|
+
deferredPersist,
|
|
865
|
+
finishAfterTurn,
|
|
866
|
+
});
|
|
867
|
+
// The delivery turn's own outcome IS the run's outcome, except when it
|
|
868
|
+
// simply still has no receipt — that is the loop's own case to keep going.
|
|
869
|
+
if (deliveryTurn.kind === "settled") {
|
|
870
|
+
// #1132: the delivery turn settles with persistRunState:false, so a run
|
|
871
|
+
// that ends HERE (得卷 or a real failure) still needs the outermost
|
|
872
|
+
// single run-state write. Without it the ledger says accepted while
|
|
873
|
+
// run-state stays `running`, and every later resume entry (ADR 0080)
|
|
874
|
+
// reads a finished run as live. The still-silent attempts above must
|
|
875
|
+
// NOT write; this one, which ends the run, must.
|
|
876
|
+
if (persistRunState && deliveryTurn.outcome.terminal !== undefined) {
|
|
615
877
|
try {
|
|
616
|
-
await
|
|
878
|
+
await persistReturnedRunState(admitted, {
|
|
879
|
+
lawful: isLawfulTypedTerminalOutcome(deliveryTurn.outcome.terminal.roleOutcome),
|
|
880
|
+
});
|
|
617
881
|
}
|
|
618
882
|
catch (error) {
|
|
619
|
-
|
|
620
|
-
// not erase that fact or make the caller replay this court's
|
|
621
|
-
// summons over already-delivered work (#840 已交劳动只整理终局不重做).
|
|
622
|
-
// Preserve the cleanup failure as a post-dispatch diagnostic; the
|
|
623
|
-
// accepted settlement remains authoritative (#840 r9 判词 class 1).
|
|
624
|
-
await recordBestEffortPostDispatchDiagnostic(admitted, env, `current-court cleanup failed after accepted settlement (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
883
|
+
await recordBestEffortPostDispatchDiagnostic(admitted, env, `run-state persistence failed beside催交 terminal (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
625
884
|
}
|
|
626
885
|
}
|
|
627
|
-
|
|
628
|
-
exitCode: exitCodeForTerminalOutcome(settled.roleOutcome),
|
|
629
|
-
admitted,
|
|
630
|
-
terminal: settled,
|
|
631
|
-
turnDispatched: true,
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
catch (error) {
|
|
636
|
-
// Settle (or its shouldPresent gate, or the failure-fact resolution
|
|
637
|
-
// above) throw is a real failure fact — never swallow into undefined.
|
|
638
|
-
const settledFailure = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({
|
|
639
|
-
timedOut: false,
|
|
640
|
-
code: result.code,
|
|
641
|
-
stderr: result.stderr,
|
|
642
|
-
thrown: error,
|
|
643
|
-
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
644
|
-
return await finishAfterTurn({ ...settledFailure, turnDispatched: true, ...deferredPersist });
|
|
645
|
-
}
|
|
646
|
-
if (settledOutcome !== undefined) {
|
|
647
|
-
// #855: final cancel re-read before lawful seal — settlement window may
|
|
648
|
-
// have received SIGTERM/SIGINT/SIGHUP after the earlier snapshot.
|
|
649
|
-
const lateCancel = processCancelSignalName(env.signal);
|
|
650
|
-
if (lateCancel !== undefined) {
|
|
651
|
-
return await finishAfterTurn(await settleProcessCancelAfterTurn({
|
|
652
|
-
admitted,
|
|
653
|
-
cancelName: lateCancel,
|
|
654
|
-
result,
|
|
655
|
-
adapters,
|
|
656
|
-
env,
|
|
657
|
-
io,
|
|
658
|
-
persistRunState,
|
|
659
|
-
deferredPersist,
|
|
660
|
-
invocationScopeId: request.invocationScopeId,
|
|
661
|
-
}));
|
|
662
|
-
}
|
|
663
|
-
if (persistRunState) {
|
|
664
|
-
try {
|
|
665
|
-
await persistReturnedRunState(admitted, { lawful: true });
|
|
666
|
-
}
|
|
667
|
-
catch (error) {
|
|
668
|
-
await recordBestEffortPostDispatchDiagnostic(admitted, env, `run-state persistence failed beside host terminal (best-effort continue): ${describeErrorIdentity(error)}`, io);
|
|
669
|
-
}
|
|
886
|
+
return await finishAfterTurn(deliveryTurn.outcome);
|
|
670
887
|
}
|
|
671
|
-
// Lawful persist + present is the caller's stop seam (auto-resume loop /
|
|
672
|
-
// manual resume), not this retried host-turn function.
|
|
673
|
-
// #855 cancel re-read lives in finishAfterTurn (before afterDispatch).
|
|
674
|
-
return await finishAfterTurn({ ...settledOutcome, ...deferredPersist });
|
|
675
888
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
const resolutionInput = controlledFailureInputFromResolution(resolution);
|
|
679
|
-
const processCancelName = processCancelSignalName(env.signal);
|
|
680
|
-
const stderrLogWriteDetails = stderrLogWriteFailure === undefined
|
|
681
|
-
? undefined
|
|
682
|
-
: { stderrLogWriteFailure: describeCaughtError(stderrLogWriteFailure) };
|
|
683
|
-
const failed = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({
|
|
684
|
-
timedOut: result.timedOut,
|
|
685
|
-
code: result.code,
|
|
686
|
-
stderr: result.stderr,
|
|
687
|
-
...resolutionInput,
|
|
688
|
-
...(processCancelName === undefined
|
|
689
|
-
? {}
|
|
690
|
-
: { knownDiagnostic: processCancelDiagnostic(processCancelName) }),
|
|
691
|
-
// Secondary fact only — never the cause. Rides on whichever channel
|
|
692
|
-
// classification actually reads (knownFailure.details owns it when
|
|
693
|
-
// a knownFailure exists; the top-level knownDetails otherwise).
|
|
694
|
-
...(stderrLogWriteDetails === undefined
|
|
695
|
-
? {}
|
|
696
|
-
: resolutionInput.knownFailure !== undefined
|
|
697
|
-
? {
|
|
698
|
-
knownFailure: {
|
|
699
|
-
...resolutionInput.knownFailure,
|
|
700
|
-
details: { ...(resolutionInput.knownFailure.details ?? {}), ...stderrLogWriteDetails },
|
|
701
|
-
},
|
|
702
|
-
}
|
|
703
|
-
: { knownDetails: stderrLogWriteDetails }),
|
|
704
|
-
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
705
|
-
return await finishAfterTurn(withProcessCancelSkipAutoResume({ ...failed, turnDispatched: true, ...deferredPersist }, env.signal));
|
|
706
|
-
}
|
|
707
|
-
// Nothing else was wrong, but the durable stderr mirror itself failed to
|
|
708
|
-
// write — that is the real (infrastructure) problem in this case, not a
|
|
709
|
-
// lawful absence of a receipt. Honest and loud, not silently no_receipt.
|
|
710
|
-
if (stderrLogWriteFailure !== undefined) {
|
|
711
|
-
const failed = await settleAfterTurnStarted(admitted, withEngineDetourInvocationScope({
|
|
712
|
-
timedOut: false,
|
|
713
|
-
code: result.code,
|
|
714
|
-
stderr: result.stderr,
|
|
715
|
-
thrown: stderrLogWriteFailure,
|
|
716
|
-
}, request.invocationScopeId), adapters, env.principalAuthority, io, persistRunState);
|
|
717
|
-
return await finishAfterTurn({ ...failed, turnDispatched: true, ...deferredPersist });
|
|
718
|
-
}
|
|
719
|
-
const noReceipt = await attachRecordedSubmissions(admitted, await settleHostEndedNoReceipt(admitted, env.principalAuthority, courtScope), courtScope);
|
|
889
|
+
const noReceiptScope = settlementScopeForTurn(request);
|
|
890
|
+
const noReceipt = await attachRecordedSubmissions(admitted, await settleHostEndedNoReceipt(admitted, env.principalAuthority, noReceiptScope, receiptDelivery.issuedDeliveryRequests()), noReceiptScope);
|
|
720
891
|
// #855: cancel during no_receipt settlement window is not lawful success.
|
|
721
892
|
const noReceiptCancel = processCancelSignalName(env.signal);
|
|
722
893
|
if (noReceiptCancel !== undefined) {
|
|
@@ -749,6 +920,12 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
749
920
|
...deferredPersist,
|
|
750
921
|
});
|
|
751
922
|
}
|
|
923
|
+
catch (error) {
|
|
924
|
+
if (turnDispatched && !(error instanceof TurnDispatchedFailure)) {
|
|
925
|
+
throw new TurnDispatchedFailure(error);
|
|
926
|
+
}
|
|
927
|
+
throw error;
|
|
928
|
+
}
|
|
752
929
|
finally {
|
|
753
930
|
// Public manual resume (#987) holds no package writer lease.
|
|
754
931
|
if (lease !== undefined) {
|
|
@@ -833,6 +1010,8 @@ export function resumeTurnRequestProjectionOptions(admitted, request, env, summo
|
|
|
833
1010
|
},
|
|
834
1011
|
...(request.message === undefined ? {} : { courtAttemptId: randomUUID() }),
|
|
835
1012
|
...(env.stationChild === undefined ? {} : { stationChild: env.stationChild }),
|
|
1013
|
+
// #1132: one configured ceiling, already resolved by the caller (#422).
|
|
1014
|
+
deliveryRequestLimit: deliveryLimitFromConfig(env.autoResumeLimit),
|
|
836
1015
|
};
|
|
837
1016
|
}
|
|
838
1017
|
/** Shared new-turn and in-call auto-resume projection. Seat code supplies prompt and activation. */
|
|
@@ -846,6 +1025,8 @@ export function roleTurnOptions(env, admitted, continuation) {
|
|
|
846
1025
|
: { correlationId }),
|
|
847
1026
|
continuation,
|
|
848
1027
|
...(env.stationChild === undefined ? {} : { stationChild: env.stationChild }),
|
|
1028
|
+
// #1132: one configured ceiling, already resolved by the caller (#422).
|
|
1029
|
+
deliveryRequestLimit: deliveryLimitFromConfig(env.autoResumeLimit),
|
|
849
1030
|
};
|
|
850
1031
|
}
|
|
851
1032
|
/**
|
|
@@ -875,6 +1056,7 @@ async function dispatchAfterWriterLease(input) {
|
|
|
875
1056
|
async function runSettledAutoResumeLoop(input) {
|
|
876
1057
|
const adapters = withOnceSuccessfulBeforeDispatch(input.adapters);
|
|
877
1058
|
const dispatchEnv = input.dispatchEnv ?? input.env;
|
|
1059
|
+
const receiptDelivery = createReceiptDeliveryPolicy(input.env.autoResumeLimit);
|
|
878
1060
|
return runWithAutoResumeLoop({
|
|
879
1061
|
admitted: input.admitted,
|
|
880
1062
|
principalAuthority: input.env.principalAuthority,
|
|
@@ -896,6 +1078,7 @@ async function runSettledAutoResumeLoop(input) {
|
|
|
896
1078
|
...(lease === undefined ? {} : { lease }),
|
|
897
1079
|
adapters,
|
|
898
1080
|
persistRunState: false,
|
|
1081
|
+
receiptDelivery,
|
|
899
1082
|
...(input.effectiveEngine === undefined
|
|
900
1083
|
? {}
|
|
901
1084
|
: { effectiveEngine: input.effectiveEngine }),
|