@akagilnc/pi-workflow-roles 0.1.3685 → 0.1.3700

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/acp-host/production-host.js +499 -559
  2. package/dist/analyst-gate-cycles-read.js +11 -1
  3. package/dist/archivist-record-entry.js +1 -1
  4. package/dist/auditor-dossier-tool.js +12 -0
  5. package/dist/dossier-resolution.js +0 -35
  6. package/dist/gatekeeper-pass-envelope.js +39 -34
  7. package/dist/gatekeeper-role.js +70 -77
  8. package/dist/judge-auditor.js +5 -24
  9. package/dist/judge-role.js +39 -42
  10. package/dist/notary-contracts.js +2 -17
  11. package/dist/notary-role.js +3 -6
  12. package/dist/package-contracts/judge-output.js +0 -2
  13. package/dist/package-contracts/terminating-tools.js +2 -2
  14. package/dist/public-cli/inspector-run.js +16 -2
  15. package/dist/public-cli/instruction-seat-run.js +14 -2
  16. package/dist/public-cli/main.js +189 -214
  17. package/dist/public-cli/notary-run.js +23 -20
  18. package/dist/public-cli/post-admission.js +30 -23
  19. package/dist/public-cli/run-lifecycle.js +6 -14
  20. package/dist/public-role-summons.js +38 -11
  21. package/dist/role-runtime.js +28 -3
  22. package/dist/submission-correctable-error.js +2 -1
  23. package/dist/submission-errors.js +26 -11
  24. package/dist/submission-ledger.js +7 -23
  25. package/extensions/role-runtime.ts +0 -2
  26. package/package.json +1 -1
  27. package/souls/countersign.md +3 -1
  28. package/souls/notary.md +5 -0
  29. package/src/acp-host/production-host.ts +1 -4
  30. package/src/acp-host/role-envelope.ts +1 -1
  31. package/src/analyst-gate-cycles-read.ts +10 -1
  32. package/src/archivist-record-entry.ts +7 -3
  33. package/src/auditor-dossier-tool.ts +21 -0
  34. package/src/dossier-resolution.ts +0 -39
  35. package/src/gatekeeper-pass-envelope.ts +59 -43
  36. package/src/gatekeeper-role.ts +114 -113
  37. package/src/host-contracts.ts +3 -3
  38. package/src/judge-auditor.ts +5 -43
  39. package/src/judge-role.ts +41 -54
  40. package/src/notary-contracts.ts +8 -27
  41. package/src/notary-role.ts +2 -7
  42. package/src/package-contracts/judge-output.ts +0 -1
  43. package/src/package-contracts/terminating-tools.ts +2 -2
  44. package/src/public-cli/inspector-run.ts +32 -2
  45. package/src/public-cli/instruction-seat-run.ts +30 -2
  46. package/src/public-cli/notary-run.ts +48 -16
  47. package/src/public-cli/post-admission.ts +31 -37
  48. package/src/public-cli/run-lifecycle.ts +6 -17
  49. package/src/public-role-summons.ts +76 -12
  50. package/src/role-runtime.ts +34 -9
  51. package/src/submission-correctable-error.ts +2 -0
  52. package/src/submission-errors.ts +28 -11
  53. package/src/submission-ledger.ts +8 -32
  54. package/dist/shape-unreadable-failure.js +0 -34
@@ -53,6 +53,19 @@ export type PublicSummonRequest = {
53
53
  * parent tool call is cancelled (#675).
54
54
  */
55
55
  readonly signal?: AbortSignal;
56
+ /**
57
+ * #753 plain-language re-ask when the prior officer reply was not three-state.
58
+ * Nested gate officers (notary + inspector) — rides same-ticket resume
59
+ * summons.instruction. Never folded into argv / parent-run lookup keys.
60
+ */
61
+ readonly reviewReask?: string;
62
+ /**
63
+ * #753/#750 same-parent re-summons: human-readable pointer materials for the
64
+ * new parent submission (source run + optional gate-submission-candidate).
65
+ * Rides summons.instruction when reviewReask is absent. Fresh mint ignores it.
66
+ * Never folded into argv / parent-run lookup keys.
67
+ */
68
+ readonly gateReviewInstruction?: string;
56
69
  };
57
70
 
58
71
  export type PublicSummonResult = {
@@ -278,6 +291,12 @@ export async function summonPublicRole(
278
291
  : { autoResumeLimit: config.autoResumeLimit }),
279
292
  // Parent cancellation reaches the nested activation's own turn dispatch.
280
293
  ...(options.signal === undefined ? {} : { signal: options.signal }),
294
+ // #753: reask rides the existing notary same-ticket resume summons.instruction.
295
+ ...(options.reviewReask === undefined ? {} : { reviewReask: options.reviewReask }),
296
+ // #753/#750: new-submission pointers on same-parent resume (not conclusion re-ask).
297
+ ...(options.gateReviewInstruction === undefined
298
+ ? {}
299
+ : { gateReviewInstruction: options.gateReviewInstruction }),
281
300
  };
282
301
  const captured = options.io === undefined ? createCapturingIo() : undefined;
283
302
  const io = options.io ?? captured!.io;
@@ -376,9 +395,9 @@ export async function summonPublicRole(
376
395
  };
377
396
  }
378
397
 
379
- /** Gate officer summons: notary via --source-run; inspector via pointer instruction. */
398
+ /** Gate officer summons: notary/auditor via --source-run; inspector via pointer instruction. */
380
399
  export async function summonGateOfficer(options: {
381
- readonly officer: "inspector" | "notary";
400
+ readonly officer: "inspector" | "notary" | "auditor";
382
401
  readonly sourceRunDirectory: string;
383
402
  readonly cwd: string;
384
403
  readonly home?: string;
@@ -386,6 +405,17 @@ export async function summonGateOfficer(options: {
386
405
  readonly io?: CliIo;
387
406
  /** Parent cancellation for the nested officer activation (#675). */
388
407
  readonly signal?: AbortSignal;
408
+ /**
409
+ * Plain-language re-ask when the prior officer reply was not three-state (#753 / #756).
410
+ * All three officers: reviewReask → same-ticket resume summons.instruction.
411
+ * Never concatenated into argv (inspector parentRunPath is the pure 卷宗指针).
412
+ */
413
+ readonly reask?: string;
414
+ /**
415
+ * Path of this turn's gate-submission-candidate when written (#632 / #753).
416
+ * Becomes human-readable resume materials when reask is absent.
417
+ */
418
+ readonly submissionCandidatePath?: string;
389
419
  }): Promise<PublicSummonResult> {
390
420
  let home = options.home;
391
421
  if (home === undefined) {
@@ -393,24 +423,58 @@ export async function summonGateOfficer(options: {
393
423
  // Hard path resolve: fail loud — never fall through to packageMachineHome (#604 / #675).
394
424
  home = homeFromRunDirectory(options.sourceRunDirectory);
395
425
  }
426
+ // Conclusion re-ask keeps sole ownership of reviewReask. New-submission materials
427
+ // ride a separate field so first mint never fails the "reask requires prior" gate.
428
+ let gateReviewInstruction: string | undefined;
429
+ if (options.reask === undefined) {
430
+ const { buildGateOfficerReviewInstruction } = await import("./auditor-dossier-tool.ts");
431
+ gateReviewInstruction = buildGateOfficerReviewInstruction({
432
+ sourceRunDirectory: options.sourceRunDirectory,
433
+ ...(options.submissionCandidatePath === undefined
434
+ ? {}
435
+ : { submissionCandidatePath: options.submissionCandidatePath }),
436
+ });
437
+ }
438
+ const common = {
439
+ cwd: options.cwd,
440
+ ...(home === undefined ? {} : { home }),
441
+ ...(options.packageRoot === undefined ? {} : { packageRoot: options.packageRoot }),
442
+ ...(options.io === undefined ? {} : { io: options.io }),
443
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
444
+ ...(options.reask === undefined ? {} : { reviewReask: options.reask }),
445
+ ...(gateReviewInstruction === undefined
446
+ ? {}
447
+ : { gateReviewInstruction }),
448
+ } as const;
396
449
  if (options.officer === "notary") {
450
+ // #753: reask rides summonPublicRole → runPublicNotary summons.instruction
451
+ // (same resume seam as ordinary same-parent re-summons — no parallel stack).
397
452
  return summonPublicRole({
398
453
  role: "notary",
399
454
  argv: ["--source-run", options.sourceRunDirectory, "--project", options.cwd],
400
- cwd: options.cwd,
401
- ...(home === undefined ? {} : { home }),
402
- ...(options.packageRoot === undefined ? {} : { packageRoot: options.packageRoot }),
403
- ...(options.io === undefined ? {} : { io: options.io }),
404
- ...(options.signal === undefined ? {} : { signal: options.signal }),
455
+ ...common,
405
456
  });
406
457
  }
458
+ if (options.officer === "auditor") {
459
+ // #756: judge compliance path — same queue law as notary/inspector.
460
+ // Subject is judge (doctor compliance stays on the disposeCompliance path).
461
+ const { AUDITOR_DOSSIER_PROMPT } = await import("./compliance-transport.ts");
462
+ return summonPublicRole({
463
+ role: "auditor",
464
+ argv: [
465
+ "--subject",
466
+ "judge",
467
+ "--source-run",
468
+ options.sourceRunDirectory,
469
+ AUDITOR_DOSSIER_PROMPT,
470
+ ],
471
+ ...common,
472
+ });
473
+ }
474
+ // Inspector: argv stays pure 卷宗指针 (#747 parentRunPath); reask rides env only.
407
475
  return summonPublicRole({
408
476
  role: "inspector",
409
477
  argv: [`卷宗指针:${options.sourceRunDirectory}`],
410
- cwd: options.cwd,
411
- ...(home === undefined ? {} : { home }),
412
- ...(options.packageRoot === undefined ? {} : { packageRoot: options.packageRoot }),
413
- ...(options.io === undefined ? {} : { io: options.io }),
414
- ...(options.signal === undefined ? {} : { signal: options.signal }),
478
+ ...common,
415
479
  });
416
480
  }
@@ -116,7 +116,6 @@ import { PACKAGED_ROLE_REGISTRY, packagedRoleMetadata, packagedRoleOutputTool, p
116
116
  import { isAuditEscalationProjection } from "./audit-escalation.ts";
117
117
  import {
118
118
  createJudgeRoleRuntime,
119
- type SoulAuditResult,
120
119
  } from "./judge-role.ts";
121
120
  import {
122
121
  createReviewerRoleRuntime,
@@ -364,7 +363,9 @@ export {
364
363
  createGatekeeperOutputTool,
365
364
  runGatekeeper,
366
365
  } from "./gatekeeper-role.ts";
367
- export type { GatekeeperResult, GatekeeperSubject, GatekeeperNonPassResult, RunGatekeeperOptions } from "./gatekeeper-role.ts";
366
+ export type { GatekeeperResult, GatekeeperSubject, GatekeeperNonPassResult, GateOfficer, RunGatekeeperOptions } from "./gatekeeper-role.ts";
367
+ export { gateOfficerForSubject } from "./gatekeeper-role.ts";
368
+ import { ParentQueueReaskError } from "./submission-errors.ts";
368
369
 
369
370
  export {
370
371
  DOCTOR_EVIDENCE_TOOL_NAME,
@@ -376,7 +377,6 @@ export { loadDoctorCase } from "./doctor-evidence.ts";
376
377
  export {
377
378
  JUDGE_OUTPUT_TOOL_NAME,
378
379
  type JudgeVerdict,
379
- type SoulAuditResult,
380
380
  } from "./judge-role.ts";
381
381
  export { ENGINE_DETOUR_TOOL_NAME, AK_ROLE_ENGINE_ENV } from "./engine-detour.ts";
382
382
  export {
@@ -410,7 +410,7 @@ export {
410
410
  resolveAuditorSubject,
411
411
  } from "./auditor-soul.ts";
412
412
  export type { AuditorSoulRole } from "./auditor-soul.ts";
413
- export { JUDGE_AUDIT_TOOL_NAME, SOUL_AUDIT_TOOL_NAME, createPiJudgeAuditor } from "./judge-auditor.ts";
413
+ export { JUDGE_AUDIT_TOOL_NAME, SOUL_AUDIT_TOOL_NAME } from "./judge-auditor.ts";
414
414
  export { DOCTOR_AUDIT_TOOL_NAME, createPiDoctorAuditor } from "./doctor-auditor.ts";
415
415
  export type { ComplianceDecision } from "./compliance-transport.ts";
416
416
  export {
@@ -651,9 +651,6 @@ export type RoleRuntimeDependencies = {
651
651
  options: { context: HostContext; signal?: AbortSignal },
652
652
  ): Promise<ReviewerDispatchRunResult>;
653
653
  shutdownReviewerAgent?(): Promise<void>;
654
- auditSoulCompliance(
655
- options: { context: HostContext; signal?: AbortSignal },
656
- ): Promise<SoulAuditResult>;
657
654
  activationClock?(): string;
658
655
  activationTraceWriter?: (record: ActivationTraceRecord) => void | Promise<void>;
659
656
  /** Wall-clock ISO timestamps for tool-execution observation records; defaults to activationClock/Date. */
@@ -1090,6 +1087,16 @@ function loadFrozenDiaristCatalog(
1090
1087
  return loadDiaristSourceCatalog(raw);
1091
1088
  }
1092
1089
 
1090
+ /** Countersign status words the queue reads (#753). */
1091
+ const COUNTERSIGN_QUEUE_STATUSES = new Set(["converged", "continue", "escalate"]);
1092
+
1093
+ /**
1094
+ * Plain-language re-ask when countersignStatus is not a known queue word.
1095
+ * Back to countersign itself — notary is not summoned (#753).
1096
+ */
1097
+ const COUNTERSIGN_STATUS_REASK =
1098
+ "countersignStatus 不是 converged、continue、escalate 三态之一。请重新交卷,status 写明其一。" as const;
1099
+
1093
1100
  export function createCountersignRoleRuntime(
1094
1101
  roleHost: RoleHost,
1095
1102
  dependencies: CountersignRuntimeDependencies,
@@ -1097,9 +1104,28 @@ export function createCountersignRoleRuntime(
1097
1104
  ) {
1098
1105
  // Notary inner gate difference only — lifecycle stays on the shared envelope.
1099
1106
  // Pointer-only summons: officer self-fetches from run dossier (#632 / ADR 0079).
1107
+ // #753 queue: read countersignStatus only — escalate skips gate (thrown to caller);
1108
+ // unreadable status returns to countersign; else notary inner gate.
1100
1109
  const beforeAccept: FiledOfficerBeforeAccept | undefined =
1101
1110
  hostActions !== undefined && roleHost.requireGatekeeperPass !== undefined
1102
- ? async ({ toolCallId, signal, ctx }) => {
1111
+ ? async ({ toolCallId, parameters, signal, ctx }) => {
1112
+ const record =
1113
+ parameters !== null && typeof parameters === "object" && !Array.isArray(parameters)
1114
+ ? (parameters as Record<string, unknown>)
1115
+ : undefined;
1116
+ const status =
1117
+ record !== undefined && typeof record.countersignStatus === "string"
1118
+ ? record.countersignStatus
1119
+ : undefined;
1120
+ if (status === undefined || !COUNTERSIGN_QUEUE_STATUSES.has(status)) {
1121
+ // Parent status unreadable → back to countersign itself; do not summon notary,
1122
+ // do not forge an officer bounce face (#753).
1123
+ throw new ParentQueueReaskError(COUNTERSIGN_STATUS_REASK);
1124
+ }
1125
+ if (status === "escalate") {
1126
+ // Parent escalate → throw to caller as-is; notary does not attend (#753).
1127
+ return undefined;
1128
+ }
1103
1129
  await roleHost.requireGatekeeperPass!({
1104
1130
  context: ctx,
1105
1131
  subject: { kind: "countersign_verdict" },
@@ -1483,7 +1509,6 @@ export function createRoleRuntimeExtension(
1483
1509
  roleHost,
1484
1510
  {
1485
1511
  loadSoul: dependencies.loadJudgeSoul,
1486
- auditSoulCompliance: dependencies.auditSoulCompliance,
1487
1512
  },
1488
1513
  hostActions,
1489
1514
  );
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  GatekeeperDecisionError,
3
+ ParentQueueReaskError,
3
4
  WorkerCommitReminderError,
4
5
  WorkerPrefixReminderError,
5
6
  WorkerUnfinishedReasonReminderError,
@@ -25,6 +26,7 @@ export function isCorrectableExecuteError(error: unknown): boolean {
25
26
  return (
26
27
  isCorrectableSubmissionError(error)
27
28
  || error instanceof GatekeeperDecisionError
29
+ || error instanceof ParentQueueReaskError
28
30
  || error instanceof WorkerCommitReminderError
29
31
  || error instanceof WorkerPrefixReminderError
30
32
  || error instanceof WorkerUnfinishedReasonReminderError
@@ -1,24 +1,29 @@
1
1
  import type { GatekeeperNonPassResult } from "./gatekeeper-role.ts";
2
- import { joinReadableGateItems } from "./readable-gate-item.ts";
2
+ import { readableGateItem } from "./readable-gate-item.ts";
3
+
4
+ /**
5
+ * Tool-result text the parent model sees (#753 / #750 evidence).
6
+ * bounce | escalate → officer receipt verbatim (JSON when structured).
7
+ * no_receipt → honest lifecycle fact. No findings rewrite, no「(无 findings)」.
8
+ * #775: structured field content via readableGateItem (DRY with other gate seams).
9
+ */
10
+ function serializeReceipt(receipt: unknown): string {
11
+ return readableGateItem(receipt);
12
+ }
3
13
 
4
14
  function gatekeeperNonPassMessage(result: GatekeeperNonPassResult): string {
5
- if (result.status === "bounce") {
6
- const findings = result.findings.length === 0
7
- ? "(无 findings)"
8
- : joinReadableGateItems(result.findings);
9
- return `门下省打回重写,findings:${findings}`;
10
- }
11
- if (result.status === "unreadable") {
12
- return `门下省官回执形状不可读(${result.officer}):${result.reason}`;
15
+ if (result.status === "bounce" || result.status === "escalate") {
16
+ return serializeReceipt(result.receipt);
13
17
  }
18
+ // no_receipt
14
19
  return `门下省 ${result.status}(${result.stage}):${result.reason}`;
15
20
  }
16
21
 
17
22
  /** Structured non-pass; `.result` is session-projected via tool_result, message feeds the model. */
18
23
  export class GatekeeperDecisionError extends Error {
19
24
  readonly result: GatekeeperNonPassResult;
20
- constructor(result: GatekeeperNonPassResult) {
21
- super(gatekeeperNonPassMessage(result));
25
+ constructor(result: GatekeeperNonPassResult, message?: string) {
26
+ super(message ?? gatekeeperNonPassMessage(result));
22
27
  this.name = "GatekeeperDecisionError";
23
28
  this.result = result;
24
29
  }
@@ -47,3 +52,15 @@ export class WorkerUnfinishedReasonReminderError extends Error {
47
52
  this.name = "WorkerUnfinishedReasonReminderError";
48
53
  }
49
54
  }
55
+
56
+ /**
57
+ * Parent seat status unreadable for queueing (#753).
58
+ * Correctable back to the parent itself — not a gate officer bounce, no officer field.
59
+ */
60
+ export class ParentQueueReaskError extends Error {
61
+ readonly code = "parent_queue_reask" as const;
62
+ constructor(message: string) {
63
+ super(message);
64
+ this.name = "ParentQueueReaskError";
65
+ }
66
+ }
@@ -3,8 +3,8 @@ import {
3
3
  tryHomeFromAkRolesPath,
4
4
  } from "./activation-ledger-topology.ts";
5
5
  import type { HostContext, HostToolResult, RoleHost } from "./host-contracts.ts";
6
- import { isAuditEscalationProjection, projectAuditEscalation } from "./audit-escalation.ts";
7
- import { GatekeeperEscalationError } from "./gatekeeper-role.ts";
6
+ import { isAuditEscalationProjection } from "./audit-escalation.ts";
7
+
8
8
 
9
9
  import {
10
10
  acceptedFacts,
@@ -358,18 +358,6 @@ export function createSubmissionLedgerHost(
358
358
  // correctable-rejection path as execute-thrown rejections below. The
359
359
  // candidate is booked first so the bounced attempt stays observable.
360
360
  append({ type: "candidate", attemptId, toolCallId, toolName: tool.name, sequence: ++state.sequence });
361
- const projectOfficerEscalation = (
362
- officer: "inspector" | "notary",
363
- original: Record<string, unknown>,
364
- deliveredOutput: Record<string, unknown> = original,
365
- ): HostToolResult<unknown> => projectAuditEscalation({
366
- ...original,
367
- status: "escalate",
368
- officer,
369
- reason: original.reason === undefined
370
- ? `门下省${officer}上呈(原卷未附 reason)`
371
- : original.reason,
372
- }, deliveredOutput);
373
361
  let result: HostToolResult<unknown>;
374
362
  try {
375
363
  failOnInfrastructureFailureDeclaration(
@@ -385,14 +373,9 @@ export function createSubmissionLedgerHost(
385
373
  );
386
374
  result = await tool.execute(toolCallId, params, signal, update, context);
387
375
  } catch (error) {
388
- if (error instanceof GatekeeperEscalationError) {
389
- const decision = error.gatekeeper as unknown as Record<string, unknown>;
390
- result = projectOfficerEscalation(
391
- error.gatekeeper.officer,
392
- decision,
393
- params as Record<string, unknown>,
394
- );
395
- } else if (isCorrectableExecuteError(error)) {
376
+ // #753: gate no longer throws GatekeeperEscalationError to select parent
377
+ // next-step. Officer bounce|escalate returns as correctable with raw receipt.
378
+ if (isCorrectableExecuteError(error)) {
396
379
  append({
397
380
  type: "outcome",
398
381
  attemptId,
@@ -413,16 +396,9 @@ export function createSubmissionLedgerHost(
413
396
  throw error;
414
397
  }
415
398
  }
416
- if (
417
- (role === "inspector" || role === "notary") &&
418
- typeof result.details === "object" &&
419
- result.details !== null &&
420
- !Array.isArray(result.details) &&
421
- (result.details as Record<string, unknown>).status === "escalate"
422
- ) {
423
- const details = result.details as Record<string, unknown>;
424
- result = projectOfficerEscalation(role, details);
425
- }
399
+ // #753 escalate-thrown-verbatim: officer escalate seals as accepted with the
400
+ // raw receipt — no projectOfficerEscalation rewrite, no fabricated reason.
401
+ // Compliance seats still return live audit_escalation projections below.
426
402
  if (isAuditEscalationProjection(result.details)) {
427
403
  const candidates = rounds.get(attemptId) ?? [];
428
404
  candidates.push({
@@ -1,34 +0,0 @@
1
- /**
2
- * Single authoritative shape-unreadable marker (#675 / ADR 0055).
3
- * Settlement stamps it; consumers only read it — never re-derive from cause=output.
4
- */
5
- export const SHAPE_UNREADABLE_KEY = "shapeUnreadable";
6
- function isRecord(value) {
7
- return typeof value === "object" && value !== null && !Array.isArray(value);
8
- }
9
- /** Settlement seat path: stamp retained candidate as shape-unreadable (not infrastructure). */
10
- export function stampShapeUnreadableDetails(candidate) {
11
- return {
12
- [SHAPE_UNREADABLE_KEY]: true,
13
- candidate,
14
- acceptedReceipt: false,
15
- };
16
- }
17
- /**
18
- * Read the settlement-stamped shape candidate from a failure decisiveFacts payload.
19
- * Looks at root and secondaryEvidence only for the typed marker — no cause inference.
20
- */
21
- export function retainedShapeUnreadable(decisiveFacts) {
22
- if (!isRecord(decisiveFacts))
23
- return undefined;
24
- if (decisiveFacts[SHAPE_UNREADABLE_KEY] === true) {
25
- return { candidate: decisiveFacts.candidate };
26
- }
27
- const secondary = isRecord(decisiveFacts.secondaryEvidence)
28
- ? decisiveFacts.secondaryEvidence
29
- : undefined;
30
- if (secondary !== undefined && secondary[SHAPE_UNREADABLE_KEY] === true) {
31
- return { candidate: secondary.candidate };
32
- }
33
- return undefined;
34
- }