@akagilnc/pi-workflow-roles 0.1.2381 → 0.1.2397

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 (36) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/audit-escalation.js +0 -13
  4. package/dist/compliance-transport.js +28 -5
  5. package/dist/dossier-resolution.js +21 -2
  6. package/dist/evidence-child-executor.js +1 -1
  7. package/dist/navigator-invocation-identity.js +16 -4
  8. package/dist/notary-contracts.js +4 -17
  9. package/dist/package-contracts/terminating-tools.js +1 -1
  10. package/dist/public-cli/main.js +101 -374
  11. package/package.json +1 -1
  12. package/src/analyst-gate-cycles-read.ts +5 -8
  13. package/src/audit-escalation.ts +0 -25
  14. package/src/compliance-transport.ts +38 -6
  15. package/src/doctor-auditor.ts +3 -3
  16. package/src/doctor-role.ts +1 -1
  17. package/src/dossier-resolution.ts +25 -4
  18. package/src/evidence-child-executor.ts +1 -1
  19. package/src/gatekeeper-role.ts +33 -43
  20. package/src/judge-auditor.ts +3 -3
  21. package/src/judge-role.ts +0 -1
  22. package/src/navigator-invocation-identity.ts +34 -13
  23. package/src/notary-contracts.ts +5 -19
  24. package/src/notary-role.ts +2 -2
  25. package/src/package-contracts/terminating-tools.ts +1 -1
  26. package/src/public-cli/doctor-run.ts +0 -2
  27. package/src/public-cli/fixer-run.ts +0 -15
  28. package/src/public-cli/judge-run.ts +0 -16
  29. package/src/public-cli/notary-run.ts +1 -1
  30. package/src/public-cli/one-shot-dispatch.ts +1 -1
  31. package/src/public-cli/reviewer-run.ts +0 -15
  32. package/src/public-cli/settlement.ts +102 -348
  33. package/src/public-cli/terminal.ts +3 -71
  34. package/src/reviewer-auditor.ts +3 -3
  35. package/src/reviewer-role.ts +0 -1
  36. package/src/role-runtime.ts +42 -9
package/README.md CHANGED
@@ -51,7 +51,7 @@ Receipts are typed, so callers compose roles without parsing prose; ordering and
51
51
 
52
52
  ### Menxia submission gate
53
53
 
54
- On completing-side submissions the package may spawn the Menxia province before the run settles: `gatekeeper` reads the subject and dispatches an officer (`inspector` or `notary`); existing auditor hooks stay where already wired. The gate runs inside the submission session; bounce means rewrite-and-resubmit in that same session — not role failure; the final receipt is the post-gate product. `planned` / `refused` / `unfinished` skip the province. Pointers only: [ADR 0067](docs/adr/0067-menxia-province-founding-jishizhong-fubaolang.md), [ADR 0072](docs/adr/0072-menxia-pre-pr-submission-hooks.md). Do not scrape session prose for gate status.
54
+ On completing-side submissions the package may spawn the Menxia province before the run settles: `gatekeeper` reads the subject and dispatches an officer (`inspector` or `notary`); existing auditor hooks stay where already wired. The gate runs inside the submission session; bounce means rewrite-and-resubmit in that same session — not role failure; the final receipt is the post-gate product. `planned` / `refused` / `unfinished` skip the province. Pointers only: [ADR 0067](docs/adr/0067-menxia-province-founding-jishizhong-fubaolang.md), [ADR 0072](docs/adr/0072-menxia-pre-pr-submission-hooks.md). Gate history is projected into the typed TerminalResult: an optional menxia section lists the seats that actually sat, each dispatch round (officer, optional reason verbatim), and each officer report (seat, status, findings). Absent when no gate ran. Do not scrape session prose for gate status — read the typed section.
55
55
 
56
56
  When a labor-engine detour fails and the seat continues the labor on the main road, the typed receipt may carry a mechanical `engineLaborFallback` field: `{ engine, failure, laborBy: "seat" }`. It appears only after a real detour failure that fell back to seat labor—not on detour success or caller cancel. First failure wins for the activation; model-forged `engineLaborFallback` keys are stripped unless the package latch recorded one. Sole producer: `src/engine-labor-fallback.ts`; decision record: [ADR 0071](docs/adr/0071-engine-detour-failure-seat-fallback-declaration.md). This README only projects that contract.
57
57
 
package/README.zh-CN.md CHANGED
@@ -51,7 +51,7 @@ ak-role config set-auto-resume-limit 3
51
51
 
52
52
  ### 门下省交卷闸
53
53
 
54
- 完成侧交卷时,包可能在本局结算前起门下省:`gatekeeper` 读受审物并派官(`inspector` 给事中或 `notary` 符宝郎);既有审刑院挂钩仍在原位。闸在交卷 session 内运行;封驳=当场重写重交,不是角色失败;最终回执即过闸产物。`planned`/`refused`/`unfinished` 不调省。指针:[ADR 0067](docs/adr/0067-menxia-province-founding-jishizhong-fubaolang.md)、[ADR 0072](docs/adr/0072-menxia-pre-pr-submission-hooks.md)。勿刮 session 散文当闸状态。
54
+ 完成侧交卷时,包可能在本局结算前起门下省:`gatekeeper` 读受审物并派官(`inspector` 给事中或 `notary` 符宝郎);既有审刑院挂钩仍在原位。闸在交卷 session 内运行;封驳=当场重写重交,不是角色失败;最终回执即过闸产物。`planned`/`refused`/`unfinished` 不调省。指针:[ADR 0067](docs/adr/0067-menxia-province-founding-jishizhong-fubaolang.md)、[ADR 0072](docs/adr/0072-menxia-pre-pr-submission-hooks.md)。闸史已投影进 typed 回执:可选 menxia 段列出实际在场席位、每轮派官(officer 与逐字 reason)与各官报告(席位/判决/findings);无闸调用时该段缺席。勿刮 session 散文当闸状态——读 typed 段。
55
55
 
56
56
  当劳务引擎绕行失败、座席回到主路继续劳务时,typed 回执可带机械字段 `engineLaborFallback`:`{ engine, failure, laborBy: "seat" }`。仅在真实绕行失败并座席顶班后出现——成功绕行或调用方 cancel 不出现。同一次 activation 内先到先得;无包内 latch 时剥离模型伪造的 `engineLaborFallback` 键。唯一构造点:`src/engine-labor-fallback.ts`;决策记录:[ADR 0071](docs/adr/0071-engine-detour-failure-seat-fallback-declaration.md)。本文只投影该契约。
57
57
 
@@ -69,14 +69,6 @@ export function projectAuditEscalation(decision, deliveredOutput) {
69
69
  ...(decision.usage === undefined ? {} : { usage: decision.usage }),
70
70
  };
71
71
  }
72
- export function projectAuditIncomplete(decision) {
73
- return {
74
- content: [{ type: "text", text: "Compliance audit incomplete; no role receipt was formed." }],
75
- details: decision,
76
- terminate: true,
77
- ...(decision.usage === undefined ? {} : { usage: decision.usage }),
78
- };
79
- }
80
72
  /**
81
73
  * Discriminator-only recognition (ADR 0040). Shape of conflicts/options/gate
82
74
  * is not a reject gate — element types and cardinality are delivery content.
@@ -106,10 +98,5 @@ export async function disposeComplianceDecision(decision, handlers, deliveredOut
106
98
  return await handlers.revise(decision.violations);
107
99
  case "escalate":
108
100
  return await handlers.escalate(projectAuditEscalation(decision, deliveredOutput));
109
- case "audit-incomplete":
110
- if (handlers.auditIncomplete === undefined) {
111
- throw new Error("Compliance audit-incomplete handler is unavailable");
112
- }
113
- return await handlers.auditIncomplete(projectAuditIncomplete(decision));
114
101
  }
115
102
  }
@@ -1,12 +1,34 @@
1
1
  import { Type } from "typebox";
2
2
  import { executeAuditorChild, } from "./evidence-child-executor.js";
3
3
  import { createAuditorDossierTool } from "./auditor-dossier-tool.js";
4
+ /** Unreadable compliance candidate — infrastructure failure, not a judgment status (#475). */
5
+ export class ComplianceCandidateUnreadableError extends Error {
6
+ observation;
7
+ candidate;
8
+ usage;
9
+ constructor(observation, candidate, usage) {
10
+ const detail = observation.kind === "non-object-arguments"
11
+ ? `${observation.kind}:${observation.type}`
12
+ : observation.kind === "object-status-unreadable"
13
+ ? `${observation.kind}:${observation.status}`
14
+ : observation.kind === "missing-subject"
15
+ ? `${observation.kind}:${observation.subject}`
16
+ : observation.kind;
17
+ super(`Compliance candidate unreadable: ${detail}`);
18
+ this.name = "ComplianceCandidateUnreadableError";
19
+ this.observation = observation;
20
+ this.candidate = candidate;
21
+ if (usage !== undefined)
22
+ this.usage = usage;
23
+ }
24
+ }
4
25
  /** Zero-projection kickoff — soul already carries dossier-fetch duty; no hand-delivered materials. */
5
26
  export const AUDITOR_DOSSIER_PROMPT = "Audit the current run dossier.";
6
27
  const nonblank = Type.String({ minLength: 1, pattern: "\\S" });
7
28
  const decisionGateSchema = Type.Object({ question: nonblank, options: Type.Array(nonblank, { minItems: 1 }) }, { additionalProperties: false });
8
- // Transport must retain malformed candidates so they can settle as typed
9
- // audit-incomplete outcomes; status values are guidance, not a schema gate.
29
+ // Transport retains malformed candidates on ComplianceCandidateUnreadableError so
30
+ // the existing failure channel can publish observation + candidate (#475).
31
+ // Status values are guidance, not a schema gate.
10
32
  export const complianceDecisionSchema = Type.Object({ status: Type.Unknown({ description: "Auditor decision status." }), violations: Type.Array(nonblank, { description: "Observed compliance violations." }), conflicts: Type.Array(nonblank, { description: "Unresolved authority or execution conflicts." }), decisionGate: Type.Union([decisionGateSchema, Type.Null()], { description: "Escalation question and available options." }) }, { additionalProperties: true, required: [] });
11
33
  export function createComplianceDecisionTool(name, description) {
12
34
  return { name, description, parameters: complianceDecisionSchema, async execute(_id, params) { return { content: [{ type: "text", text: "Compliance decision received" }], details: params, terminate: true }; } };
@@ -48,8 +70,9 @@ function retainComplianceResponse(context, response) {
48
70
  }
49
71
  function readListField(value) { return Array.isArray(value) ? value : value === undefined ? [] : [value]; }
50
72
  export function readComplianceCandidate(arguments_, usage) {
51
- if (typeof arguments_ !== "object" || arguments_ === null || Array.isArray(arguments_))
52
- return { status: "audit-incomplete", observation: { kind: "non-object-arguments", type: arguments_ === null ? "null" : Array.isArray(arguments_) ? "array" : typeof arguments_ }, candidate: arguments_, ...(usage === undefined ? {} : { usage }) };
73
+ if (typeof arguments_ !== "object" || arguments_ === null || Array.isArray(arguments_)) {
74
+ throw new ComplianceCandidateUnreadableError({ kind: "non-object-arguments", type: arguments_ === null ? "null" : Array.isArray(arguments_) ? "array" : typeof arguments_ }, arguments_, usage);
75
+ }
53
76
  const args = arguments_;
54
77
  const status = args.status;
55
78
  if (status === "pass")
@@ -58,7 +81,7 @@ export function readComplianceCandidate(arguments_, usage) {
58
81
  return { status, violations: readListField(args.violations), ...(usage === undefined ? {} : { usage }) };
59
82
  if (status === "escalate")
60
83
  return { status, ...(Object.hasOwn(args, "conflicts") ? { conflicts: args.conflicts } : {}), ...(Object.hasOwn(args, "decisionGate") ? { decisionGate: args.decisionGate } : {}), ...(usage === undefined ? {} : { usage }) };
61
- return { status: "audit-incomplete", observation: { kind: "object-status-unreadable", status: status === undefined ? "missing" : "unknown" }, candidate: arguments_, ...(usage === undefined ? {} : { usage }) };
84
+ throw new ComplianceCandidateUnreadableError({ kind: "object-status-unreadable", status: status === undefined ? "missing" : "unknown" }, arguments_, usage);
62
85
  }
63
86
  export async function runComplianceAudit(options) {
64
87
  const prompt = options.serializedInput ?? AUDITOR_DOSSIER_PROMPT;
@@ -98,6 +98,25 @@ export function readDoctorAuditSubjects(context) {
98
98
  }
99
99
  return { status: "incomplete", observation: { kind: "missing-subject", subject: "candidate-testimony" } };
100
100
  }
101
- export function toAuditIncomplete(observation) {
102
- return { status: "audit-incomplete", observation, candidate: undefined };
101
+ /**
102
+ * Missing dossier/subject is infrastructure failure, not a judgment status (#475).
103
+ * Observation + empty candidate ride the existing failInfrastructure → error artifact path.
104
+ */
105
+ export class AuditMaterialsUnavailableError extends Error {
106
+ observation;
107
+ candidate;
108
+ constructor(observation) {
109
+ const detail = observation.kind === "missing-subject"
110
+ ? `${observation.kind}:${observation.subject}`
111
+ : observation.kind;
112
+ super(`Audit materials unavailable: ${detail}`);
113
+ this.name = "AuditMaterialsUnavailableError";
114
+ this.observation = observation;
115
+ this.candidate = undefined;
116
+ }
117
+ }
118
+ export function requireAuditMaterials(resolution) {
119
+ if (resolution.status === "incomplete") {
120
+ throw new AuditMaterialsUnavailableError(resolution.observation);
121
+ }
103
122
  }
@@ -756,7 +756,7 @@ export async function executeAuditorChild(options) {
756
756
  decision = part.arguments;
757
757
  decisionCallId = part.id;
758
758
  // Pi can reject malformed root arguments before invoking execute;
759
- // that remains the existing typed audit-incomplete candidate path.
759
+ // that remains the existing unreadable-candidate failure path.
760
760
  if (part.arguments === undefined)
761
761
  decisionSubmitted = true;
762
762
  }
@@ -8,6 +8,13 @@ const NAVIGATOR_INFRASTRUCTURE_FAILURE_KEYS = [
8
8
  "source",
9
9
  "reasonCode"
10
10
  ];
11
+ const NAVIGATOR_INFRASTRUCTURE_FAILURE_EVIDENCE_KEYS = [
12
+ "observation",
13
+ "candidate",
14
+ "submission",
15
+ "stage",
16
+ "reason"
17
+ ];
11
18
  function buildNavigatorInfrastructureFailureFact() {
12
19
  return {
13
20
  kind: NAVIGATOR_INFRASTRUCTURE_FAILURE_KIND,
@@ -15,16 +22,18 @@ function buildNavigatorInfrastructureFailureFact() {
15
22
  reasonCode: "host_failure"
16
23
  };
17
24
  }
18
- function isNavigatorInfrastructureFailureFact(value) {
25
+ function hasNavigatorInfrastructureFailureBase(value) {
19
26
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
20
27
  const record = value;
21
- const keys = Object.keys(record);
22
- if (keys.length !== NAVIGATOR_INFRASTRUCTURE_FAILURE_KEYS.length) return false;
23
28
  for (const key of NAVIGATOR_INFRASTRUCTURE_FAILURE_KEYS) {
24
29
  if (!Object.hasOwn(record, key)) return false;
25
30
  }
26
31
  return record.kind === NAVIGATOR_INFRASTRUCTURE_FAILURE_KIND && record.source === "shared-role-lifecycle" && record.reasonCode === "host_failure";
27
32
  }
33
+ function isNavigatorInfrastructureFailureFact(value) {
34
+ if (!hasNavigatorInfrastructureFailureBase(value)) return false;
35
+ return Object.keys(value).length === NAVIGATOR_INFRASTRUCTURE_FAILURE_KEYS.length;
36
+ }
28
37
  const PACKAGED_ROLE_OUTPUT_TOOLS = new Map(
29
38
  PACKAGED_ROLE_REGISTRY.map((entry) => [entry.outputTool, entry.role])
30
39
  );
@@ -68,7 +77,8 @@ function markerMatchesExpectedIdentity(marker, expected) {
68
77
  function classifyPackagedRoleTerminalResult(message) {
69
78
  if (typeof message.toolName !== "string") return { kind: "nonterminal" };
70
79
  if (!PACKAGED_ROLE_OUTPUT_TOOLS.has(message.toolName)) return { kind: "nonterminal" };
71
- const infraFact = isNavigatorInfrastructureFailureFact(message.details) ? message.details : void 0;
80
+ const hasInfraBase = hasNavigatorInfrastructureFailureBase(message.details);
81
+ const infraFact = hasInfraBase ? buildNavigatorInfrastructureFailureFact() : void 0;
72
82
  if (message.isError === true) {
73
83
  if (infraFact === void 0) return { kind: "nonterminal" };
74
84
  return { kind: "infrastructure", fact: infraFact };
@@ -201,6 +211,7 @@ function currentInvocationMarkerFromSession(entries, beforeIndex = entries.lengt
201
211
  return void 0;
202
212
  }
203
213
  export {
214
+ NAVIGATOR_INFRASTRUCTURE_FAILURE_EVIDENCE_KEYS,
204
215
  NAVIGATOR_INFRASTRUCTURE_FAILURE_KIND,
205
216
  NAVIGATOR_INVOCATION_ENTRY,
206
217
  bindCurrentDurableTerminalToMarker,
@@ -209,6 +220,7 @@ export {
209
220
  currentInvocationMarkerFromSession,
210
221
  currentInvocationPrincipalFromSession,
211
222
  findLatestDurablePackagedRoleTerminal,
223
+ hasNavigatorInfrastructureFailureBase,
212
224
  isAcceptedPackagedRoleTerminalResult,
213
225
  isDurablePackagedRoleTerminalResult,
214
226
  isNavigatorInfrastructureFailureFact,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Public Notary (符宝郎) terminating receipt contracts.
3
- * Lawful explicit releases: pass | bounce | incomplete(with non-empty reason).
4
- * Residual incomplete (no explicit release) is projected by public settlement, not here.
3
+ * Lawful explicit releases: pass | bounce.
4
+ * No usable result is infrastructure failure via public settlement, not a judgment status (#475).
5
5
  */
6
6
  import { Type } from "typebox";
7
7
  import { readActivationEngineLaborFallbackField, seatFallbackBaseStatus, seatFallbackStatusHasLawfulEvidence, withEngineLaborFallbackField, } from "./engine-labor-fallback.js";
@@ -19,14 +19,11 @@ export const NOTARY_SOURCE_RUN_FLAG = {
19
19
  export const NOTARY_FIXED_KICKOFF = "Notary review. Bound source-run locator is on the session materials; fetch authoritative ticket, git, and dossier evidence yourself; submit one typed decision.";
20
20
  export const notaryOutputSchema = openToolObject(Type.Object({
21
21
  status: Type.Unknown({
22
- description: "pass | bounce | incomplete — guidance, not a schema gate.",
22
+ description: "pass | bounce — guidance, not a schema gate.",
23
23
  }),
24
24
  findings: Type.Unknown({
25
25
  description: "string[] findings retained with pass or bounce.",
26
26
  }),
27
- reason: Type.Unknown({
28
- description: "Why the notary decision is incomplete.",
29
- }),
30
27
  }));
31
28
  function isRecord(value) {
32
29
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -38,7 +35,7 @@ function asStringArray(value) {
38
35
  }
39
36
  /**
40
37
  * Project one explicit Notary release. Throws when there is no lawful explicit
41
- * pass / bounce / incomplete(reason) — callers map that to residual incomplete.
38
+ * pass / bounce — callers map that to the existing non-zero failure channel.
42
39
  */
43
40
  export function validateNotaryOutput(value) {
44
41
  if (!isRecord(value)) {
@@ -49,13 +46,6 @@ export function validateNotaryOutput(value) {
49
46
  throw new Error("Notary output has no recognized execution discriminator");
50
47
  }
51
48
  const status = seatFallbackBaseStatus(statusRaw);
52
- if (status === "incomplete") {
53
- const reason = value.reason;
54
- if (typeof reason !== "string" || reason.trim() === "") {
55
- throw new Error("Notary incomplete requires a non-empty reason");
56
- }
57
- return structuredClone(value);
58
- }
59
49
  if (status === "bounce") {
60
50
  const clone = structuredClone(value);
61
51
  if (clone.disposition === undefined)
@@ -89,8 +79,5 @@ export function notaryDecisiveFacts(output) {
89
79
  if (status === "bounce") {
90
80
  facts.disposition = "rewrite";
91
81
  }
92
- if (status === "incomplete") {
93
- facts.reason = output.reason;
94
- }
95
82
  return facts;
96
83
  }
@@ -82,7 +82,7 @@ export function validateAcceptedDetails(toolName, details) {
82
82
  [COLLECTOR_OUTPUT_TOOL]: [],
83
83
  [DOCTOR_OUTPUT_TOOL_NAME]: ["completed", "refused"],
84
84
  [MERGER_OUTPUT_TOOL_NAME]: ["completed", "escalate"],
85
- [NOTARY_OUTPUT_TOOL_NAME]: ["pass", "bounce", "incomplete"],
85
+ [NOTARY_OUTPUT_TOOL_NAME]: ["pass", "bounce"],
86
86
  };
87
87
  const collectorDiscriminator = toolName === COLLECTOR_OUTPUT_TOOL && Array.isArray(candidate?.groups);
88
88
  const baseDiscriminator = typeof discriminator === "string" ? seatFallbackBaseStatus(discriminator) : discriminator;