@akagilnc/pi-workflow-roles 0.1.2383 → 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.
@@ -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;