@akagilnc/pi-workflow-roles 0.1.3397 → 0.1.3405

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.
@@ -24944,8 +24944,15 @@ function sealedLedgerHome(admitted) {
24944
24944
  async function sealedLedgerOutcome(admitted) {
24945
24945
  return readSealedSubmission(admitted.projectRoot, admitted.runId, sealedLedgerHome(admitted));
24946
24946
  }
24947
- async function hasSealedAcceptedProjection(admitted) {
24948
- return await sealedLedgerOutcome(admitted) !== void 0;
24947
+ async function sealedAcceptanceRedispatchDisposition(admitted) {
24948
+ try {
24949
+ if (await sealedLedgerOutcome(admitted) !== void 0) {
24950
+ return { kind: "block", reason: "sealed-accepted" };
24951
+ }
24952
+ return { kind: "allow" };
24953
+ } catch (cause) {
24954
+ return { kind: "block", reason: "authority-failed", cause };
24955
+ }
24949
24956
  }
24950
24957
  async function auditEscalationLedgerOutcome(admitted, role) {
24951
24958
  const projection = await readAuditEscalationSubmission(
@@ -27937,36 +27944,21 @@ async function runWithAutoResumeLoop(options) {
27937
27944
  return result2;
27938
27945
  }
27939
27946
  }
27940
- if (options.shouldStopAutoResume !== void 0) {
27941
- let failClosed;
27942
- let sealedStop = false;
27943
- try {
27944
- sealedStop = await options.shouldStopAutoResume();
27945
- } catch (authorityError) {
27946
- failClosed = {
27947
- cause: authorityError,
27948
- endReason: "sealed-acceptance authority failed closed"
27949
- };
27950
- }
27951
- if (failClosed === void 0 && sealedStop) {
27952
- if (result2 !== void 0) {
27953
- const terminal = result2.terminal;
27954
- if (terminal !== void 0) presentTerminal(terminal, options.io);
27947
+ if (options.sealedAcceptanceDisposition !== void 0) {
27948
+ const disposition = await options.sealedAcceptanceDisposition();
27949
+ if (disposition.kind === "block") {
27950
+ if (disposition.reason === "sealed-accepted" && result2 !== void 0) {
27951
+ const terminal2 = result2.terminal;
27952
+ if (terminal2 !== void 0) presentTerminal(terminal2, options.io);
27955
27953
  return result2;
27956
27954
  }
27957
- failClosed = {
27958
- cause: lastThrownError,
27959
- endReason: "sealed accepted projection already present"
27960
- };
27961
- }
27962
- if (failClosed !== void 0) {
27963
27955
  const terminal = dispatchExceptionFailureTerminal({
27964
27956
  role: options.admitted.role,
27965
27957
  runId: options.admitted.runId,
27966
- causeError: failClosed.cause,
27958
+ causeError: disposition.reason === "authority-failed" ? disposition.cause : lastThrownError,
27967
27959
  errorFiles: retainedErrorFiles,
27968
27960
  autoResumeAttempts,
27969
- endReason: failClosed.endReason,
27961
+ endReason: disposition.reason === "authority-failed" ? "sealed-acceptance authority failed closed" : "sealed accepted projection already present",
27970
27962
  everyAttemptThrew
27971
27963
  });
27972
27964
  await finalizeExceptionRunBestEffort(options.admitted.runDirectory, options.io);
@@ -28320,7 +28312,7 @@ async function runPostAdmissionResumable(input) {
28320
28312
  autoResumeLimit: env.autoResumeLimit,
28321
28313
  buildInitialPayload: buildInitialRequest,
28322
28314
  buildResumePayload: buildResumeRequest,
28323
- shouldStopAutoResume: async () => hasSealedAcceptedProjection(admitted),
28315
+ sealedAcceptanceDisposition: () => sealedAcceptanceRedispatchDisposition(admitted),
28324
28316
  dispatch: (request, lease, _isFirst, attemptIo) => dispatchPostAdmissionTurn({
28325
28317
  admitted,
28326
28318
  env: {
@@ -28352,22 +28344,15 @@ async function runPostAdmissionManualResume(input) {
28352
28344
  };
28353
28345
  }
28354
28346
  } catch (error) {
28355
- let failClosed;
28356
- try {
28357
- if (await hasSealedAcceptedProjection(admitted)) {
28358
- failClosed = { cause: error };
28359
- }
28360
- } catch (authorityError) {
28361
- failClosed = { cause: authorityError };
28362
- }
28363
- if (failClosed !== void 0) {
28347
+ const disposition = await sealedAcceptanceRedispatchDisposition(admitted);
28348
+ if (disposition.kind === "block") {
28364
28349
  return await presentControlledFailure2(
28365
28350
  admitted,
28366
28351
  {
28367
28352
  timedOut: false,
28368
28353
  code: null,
28369
28354
  stderr: "",
28370
- thrown: failClosed.cause
28355
+ thrown: disposition.reason === "authority-failed" ? disposition.cause : error
28371
28356
  },
28372
28357
  adapters,
28373
28358
  env.principalAuthority,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.3397",
3
+ "version": "0.1.3405",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -8,8 +8,6 @@
8
8
 
9
9
  取舍位次:过测试 → 显意图 → 无重复 → 最少元素。
10
10
 
11
- 尺子锚定仓内已接受的现状:与紧邻已并入代码同款的写法,不单独判新增行为缺陷;要判就按类判全仓。
12
-
13
11
  ## 测试对行为负责
14
12
 
15
13
  测试只保**调用输入正确**的最短确定性案:从真实入口进入,在外部可见的结构化结果上断言,秒级完成。涉真实宿主、真实 LLM、真实并发、真实网络的行为,不写测试;以真跑为证。
@@ -31,7 +31,11 @@ import {
31
31
  } from "./run-lifecycle.ts";
32
32
  import { parseAutoResumeLimit } from "./config.ts";
33
33
  import { isLawfulTypedTerminalOutcome, formatTerminalResult, type TerminalArtifactRef, type TerminalResult, type TerminalRoleName } from "./terminal.ts";
34
- import { presentFailureTerminal, presentStructuralRejection } from "./settlement.ts";
34
+ import {
35
+ presentFailureTerminal,
36
+ presentStructuralRejection,
37
+ type SealedAcceptanceRedispatchDisposition,
38
+ } from "./settlement.ts";
35
39
  import type { CliIo } from "./cli-io.ts";
36
40
 
37
41
  const dummyIo: CliIo = { stdout: () => {}, stderr: () => {} };
@@ -343,11 +347,12 @@ export async function runWithAutoResumeLoop<
343
347
  buildResumePayload: () => TPayload;
344
348
  dispatch: (payload: TPayload, lease: RunWriterLease, isFirst: boolean, attemptIo: CliIo) => Promise<T>;
345
349
  /**
346
- * After a non-lawful terminal or dispatch throw, stop further auto-resume when a unique sealed
347
- * accepted projection is already readable (publication miss must not redispatch
348
- * and destroy the sealed read — #648 / #599 alignment).
350
+ * After a non-lawful terminal or dispatch throw, consult settlement-owned
351
+ * sealed-acceptance redispatch disposition (#672 / ADR 0080). Publication
352
+ * miss must not redispatch and destroy the sealed read (#648 / #599).
353
+ * Loop presents entry-specific terminals; it must not rebuild the gate.
349
354
  */
350
- shouldStopAutoResume?: () => Promise<boolean>;
355
+ sealedAcceptanceDisposition?: () => Promise<SealedAcceptanceRedispatchDisposition>;
351
356
  }): Promise<T> {
352
357
  // #422 single-point resolution + domain validation. NaN would bypass every
353
358
  // `attempts >= limit` comparison (always false) — reject here, before any dispatch.
@@ -431,42 +436,30 @@ export async function runWithAutoResumeLoop<
431
436
  }
432
437
  }
433
438
 
434
- // One sealed ledger authority before any redispatch (#648): shared for
435
- // non-lawful return and direct throw. Authority throw fail-closes with
436
- // preserved cause — never wash into unsealed redispatch. Only the sealed=true
437
- // projection differs (present returned terminal vs throw-path synthetic).
438
- // Fail-closed decision is independent of cause value: `throw undefined` is
439
- // legal JS and must not fail open. One cause + endReason → one presentation.
440
- if (options.shouldStopAutoResume !== undefined) {
441
- let failClosed: { cause: unknown; endReason: string } | undefined;
442
- let sealedStop = false;
443
- try {
444
- sealedStop = await options.shouldStopAutoResume();
445
- } catch (authorityError) {
446
- failClosed = {
447
- cause: authorityError,
448
- endReason: "sealed-acceptance authority failed closed",
449
- };
450
- }
451
- if (failClosed === undefined && sealedStop) {
452
- if (result !== undefined) {
439
+ // Settlement-owned sealed disposition before any redispatch (#648 / #672):
440
+ // shared for non-lawful return and direct throw. Only entry presentation
441
+ // differs (present returned terminal vs throw-path synthetic).
442
+ if (options.sealedAcceptanceDisposition !== undefined) {
443
+ const disposition = await options.sealedAcceptanceDisposition();
444
+ if (disposition.kind === "block") {
445
+ if (disposition.reason === "sealed-accepted" && result !== undefined) {
453
446
  const terminal = (result as { terminal?: TerminalResult }).terminal;
454
447
  if (terminal !== undefined) presentTerminal(terminal, options.io);
455
448
  return result;
456
449
  }
457
- failClosed = {
458
- cause: lastThrownError,
459
- endReason: "sealed accepted projection already present",
460
- };
461
- }
462
- if (failClosed !== undefined) {
463
450
  const terminal = dispatchExceptionFailureTerminal({
464
451
  role: options.admitted.role,
465
452
  runId: options.admitted.runId,
466
- causeError: failClosed.cause,
453
+ causeError:
454
+ disposition.reason === "authority-failed"
455
+ ? disposition.cause
456
+ : lastThrownError,
467
457
  errorFiles: retainedErrorFiles,
468
458
  autoResumeAttempts,
469
- endReason: failClosed.endReason,
459
+ endReason:
460
+ disposition.reason === "authority-failed"
461
+ ? "sealed-acceptance authority failed closed"
462
+ : "sealed accepted projection already present",
470
463
  everyAttemptThrew,
471
464
  });
472
465
  await finalizeExceptionRunBestEffort(options.admitted.runDirectory, options.io);
@@ -57,7 +57,7 @@ import {
57
57
  resolveAuditedRunnerFailureResolution,
58
58
  resolveControlledFailureResumeObservation,
59
59
  settleFailureTerminalResult,
60
- hasSealedAcceptedProjection,
60
+ sealedAcceptanceRedispatchDisposition,
61
61
  } from "./settlement.ts";
62
62
  import type { CliIo } from "./cli-io.ts";
63
63
  import type { AdmittedRoleInvocation } from "./invocation.ts";
@@ -547,8 +547,8 @@ export async function runPostAdmissionResumable<
547
547
  autoResumeLimit: env.autoResumeLimit,
548
548
  buildInitialPayload: buildInitialRequest,
549
549
  buildResumePayload: buildResumeRequest,
550
- shouldStopAutoResume: async () =>
551
- hasSealedAcceptedProjection(admitted),
550
+ sealedAcceptanceDisposition: () =>
551
+ sealedAcceptanceRedispatchDisposition(admitted),
552
552
  dispatch: (request, lease, _isFirst, attemptIo) =>
553
553
  dispatchPostAdmissionTurn({
554
554
  admitted,
@@ -614,27 +614,21 @@ export async function runPostAdmissionManualResume<
614
614
  };
615
615
  }
616
616
  } catch (error) {
617
- // Sealed accepted + publication/settle throw must fail closed without redispatch
618
- // (#648 / #599): do not treat "sealed + publish threw" as "not sealed".
619
- // Ledger authority failure also fail-closes with preserved cause (never wash to unsealed).
620
- // Fail-closed decision is independent of cause value: `throw undefined` is legal JS
621
- // and must not fail open. One cause → one presentation (settle vs authority).
622
- let failClosed: { cause: unknown } | undefined;
623
- try {
624
- if (await hasSealedAcceptedProjection(admitted)) {
625
- failClosed = { cause: error };
626
- }
627
- } catch (authorityError) {
628
- failClosed = { cause: authorityError };
629
- }
630
- if (failClosed !== undefined) {
617
+ // Settlement-owned sealed disposition (#648 / #599 / #672): sealed accepted +
618
+ // publication/settle throw fail closed without redispatch; authority failure
619
+ // preserves true cause. Manual resume only presents — does not rebuild the gate.
620
+ const disposition = await sealedAcceptanceRedispatchDisposition(admitted);
621
+ if (disposition.kind === "block") {
631
622
  return (await presentControlledFailure(
632
623
  admitted,
633
624
  {
634
625
  timedOut: false,
635
626
  code: null,
636
627
  stderr: "",
637
- thrown: failClosed.cause,
628
+ thrown:
629
+ disposition.reason === "authority-failed"
630
+ ? disposition.cause
631
+ : error,
638
632
  },
639
633
  adapters,
640
634
  env.principalAuthority,
@@ -163,15 +163,31 @@ async function sealedLedgerOutcome(admitted: AdmittedRoleInvocation): Promise<Ex
163
163
  }
164
164
 
165
165
  /**
166
- * Shared sealed-accepted detection for all resumable adapters (#648).
167
- * Ledger projection is the sole authority — not optional per-role adapter wiring.
166
+ * Shared sealed-acceptance redispatch disposition for resume entrypoints
167
+ * (#648 sealed authority; #672 / ADR 0080 single-settlement-disposition).
168
+ * Owns the fail-closed gate auto and manual resume previously rebuilt beside
169
+ * the shared sealed query: sealed → block; authority throw → block with
170
+ * preserved cause; otherwise allow. Callers present entry-specific terminals;
171
+ * they must not re-derive this judgment. Ledger projection remains the sole
172
+ * seal truth — this is disposition over that read, not a second state.
168
173
  */
169
- export async function hasSealedAcceptedProjection(
174
+ export type SealedAcceptanceRedispatchDisposition =
175
+ | { readonly kind: "allow" }
176
+ | { readonly kind: "block"; readonly reason: "sealed-accepted" }
177
+ | { readonly kind: "block"; readonly reason: "authority-failed"; readonly cause: unknown };
178
+
179
+ export async function sealedAcceptanceRedispatchDisposition(
170
180
  admitted: AdmittedRoleInvocation,
171
- ): Promise<boolean> {
172
- // Ledger authority must not wash read failure into "unsealed" (#648).
173
- // Callers treat throw as fail-closed: preserve true cause, never redispatch.
174
- return (await sealedLedgerOutcome(admitted)) !== undefined;
181
+ ): Promise<SealedAcceptanceRedispatchDisposition> {
182
+ try {
183
+ if ((await sealedLedgerOutcome(admitted)) !== undefined) {
184
+ return { kind: "block", reason: "sealed-accepted" };
185
+ }
186
+ return { kind: "allow" };
187
+ } catch (cause) {
188
+ // Ledger authority must not wash read failure into "unsealed" (#648).
189
+ return { kind: "block", reason: "authority-failed", cause };
190
+ }
175
191
  }
176
192
 
177
193
  async function auditEscalationLedgerOutcome(