@akagilnc/pi-workflow-roles 0.1.2331 → 0.1.2336

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.
@@ -15,12 +15,32 @@ function read(value, key) {
15
15
  return undefined;
16
16
  }
17
17
  }
18
+ /** Canonical per-axis string slots only — no shape reject, no prose parse (ADR 0057). */
19
+ function readAmendments(output) {
20
+ const amendments = read(output, "amendments");
21
+ if (!isRecord(amendments))
22
+ return undefined;
23
+ const slots = {};
24
+ for (const axis of ["standards", "spec"]) {
25
+ const value = read(amendments, axis);
26
+ if (typeof value === "string")
27
+ slots[axis] = value;
28
+ }
29
+ if (slots.standards === undefined && slots.spec === undefined)
30
+ return undefined;
31
+ return Object.freeze(slots);
32
+ }
18
33
  export function validateReviewerIntent(output) {
19
34
  const status = read(output, "status");
20
- if (status === "completed")
21
- return { status };
22
- if (status === "refused")
23
- return { status, diagnostic: read(output, "diagnostic") };
35
+ const amendments = readAmendments(output);
36
+ if (status === "completed") {
37
+ return amendments === undefined ? { status } : { status, amendments };
38
+ }
39
+ if (status === "refused") {
40
+ return amendments === undefined
41
+ ? { status, diagnostic: read(output, "diagnostic") }
42
+ : { status, diagnostic: read(output, "diagnostic"), amendments };
43
+ }
24
44
  throw new Error("Reviewer output has no recognized execution intent");
25
45
  }
26
46
  /** Validate runtime-owned facts at their real identity seams (target pins + plain text). */
@@ -21190,13 +21190,16 @@ function reviewerDecisiveFacts(output) {
21190
21190
  const status = safelyRead(candidate, "status");
21191
21191
  const outcomes = safelyRead(candidate, "outcomes");
21192
21192
  const reports = safelyRead(candidate, "reports");
21193
+ const amendments = safelyRead(candidate, "amendments");
21193
21194
  const axes = reviewerAxes(outcomes.readable ? outcomes.value : void 0);
21194
21195
  const reportAxes = reviewerAxes(reports.readable ? reports.value : void 0);
21196
+ const amendmentAxes = reviewerAxes(amendments.readable ? amendments.value : void 0);
21195
21197
  const acceptedBatch = safelyRead(candidate, "acceptedBatch");
21196
21198
  const specDisposition = safelyRead(candidate, "specDisposition");
21197
21199
  const facts = {
21198
21200
  axes,
21199
21201
  reportAxes,
21202
+ amendmentAxes,
21200
21203
  acceptedBatchPresent: acceptedBatch.readable && acceptedBatch.value !== void 0,
21201
21204
  ...auditNoReceiptDecisiveFact(candidate),
21202
21205
  // #380: spread sole-built field; do not re-key here (S1).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.2331",
3
+ "version": "0.1.2336",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -11,9 +11,11 @@ import type { ReviewerAcceptedEvidence, ReviewerFailureClassification, ReviewerW
11
11
  export const REVIEWER_OUTPUT_TOOL_NAME = "ak_reviewer_output";
12
12
  export const REVIEWER_ACCEPTED_TEXT = "Reviewer report accepted";
13
13
 
14
+ /** Seat-owned per-axis delta relative to child reports — not a replacement report. */
15
+ export type ReviewerAmendments = Readonly<Partial<Record<"standards" | "spec", string>>>;
14
16
  export type ReviewerIntent =
15
- | Readonly<{ status: "completed" }>
16
- | Readonly<{ status: "refused"; diagnostic: string }>;
17
+ | Readonly<{ status: "completed"; amendments?: ReviewerAmendments }>
18
+ | Readonly<{ status: "refused"; diagnostic: string; amendments?: ReviewerAmendments }>;
17
19
  /** Child report is plain text — no length/digest identity shell (ADR 0031). */
18
20
  export type VerbatimChildReport = Readonly<{ text: string }>;
19
21
  /** Prompt projection on the receipt face is plain text (ADR 0031). */
@@ -44,6 +46,8 @@ type RuntimeReviewerReceiptV2Clean = Readonly<{
44
46
  /** Self-fetch bytes + source annotation when Spec primary path produced material (#343). */
45
47
  specFetchedMaterial?: ReviewerAcceptedEvidence["specFetchedMaterial"];
46
48
  reports: Readonly<Partial<Record<"standards" | "spec", VerbatimChildReport>>>;
49
+ /** Seat-owned per-axis deltas; separate from runtime-owned child reports. */
50
+ amendments?: ReviewerAmendments;
47
51
  outcomes: Readonly<Partial<Record<"standards" | "spec", RuntimeReviewerOutcome>>>;
48
52
  identities: Readonly<{
49
53
  canonicalSkill: ReviewerReceiptSkillContent;
@@ -68,10 +72,30 @@ function read(value: unknown, key: string): unknown {
68
72
  try { return value[key]; } catch { return undefined; }
69
73
  }
70
74
 
75
+ /** Canonical per-axis string slots only — no shape reject, no prose parse (ADR 0057). */
76
+ function readAmendments(output: unknown): ReviewerAmendments | undefined {
77
+ const amendments = read(output, "amendments");
78
+ if (!isRecord(amendments)) return undefined;
79
+ const slots: Partial<Record<"standards" | "spec", string>> = {};
80
+ for (const axis of ["standards", "spec"] as const) {
81
+ const value = read(amendments, axis);
82
+ if (typeof value === "string") slots[axis] = value;
83
+ }
84
+ if (slots.standards === undefined && slots.spec === undefined) return undefined;
85
+ return Object.freeze(slots);
86
+ }
87
+
71
88
  export function validateReviewerIntent(output: unknown): ReviewerIntent {
72
89
  const status = read(output, "status");
73
- if (status === "completed") return { status };
74
- if (status === "refused") return { status, diagnostic: read(output, "diagnostic") as string };
90
+ const amendments = readAmendments(output);
91
+ if (status === "completed") {
92
+ return amendments === undefined ? { status } : { status, amendments };
93
+ }
94
+ if (status === "refused") {
95
+ return amendments === undefined
96
+ ? { status, diagnostic: read(output, "diagnostic") as string }
97
+ : { status, diagnostic: read(output, "diagnostic") as string, amendments };
98
+ }
75
99
  throw new Error("Reviewer output has no recognized execution intent");
76
100
  }
77
101
 
@@ -1309,13 +1309,17 @@ function reviewerDecisiveFacts(
1309
1309
  const status = safelyRead(candidate, "status");
1310
1310
  const outcomes = safelyRead(candidate, "outcomes");
1311
1311
  const reports = safelyRead(candidate, "reports");
1312
+ const amendments = safelyRead(candidate, "amendments");
1312
1313
  const axes = reviewerAxes(outcomes.readable ? outcomes.value : undefined);
1313
1314
  const reportAxes = reviewerAxes(reports.readable ? reports.value : undefined);
1315
+ // Typed presence only — never copy amendment prose into public facts.
1316
+ const amendmentAxes = reviewerAxes(amendments.readable ? amendments.value : undefined);
1314
1317
  const acceptedBatch = safelyRead(candidate, "acceptedBatch");
1315
1318
  const specDisposition = safelyRead(candidate, "specDisposition");
1316
1319
  const facts: Record<string, unknown> = {
1317
1320
  axes,
1318
1321
  reportAxes,
1322
+ amendmentAxes,
1319
1323
  acceptedBatchPresent: acceptedBatch.readable && acceptedBatch.value !== undefined,
1320
1324
  ...auditNoReceiptDecisiveFact(candidate),
1321
1325
  // #380: spread sole-built field; do not re-key here (S1).
@@ -32,9 +32,20 @@ export type ReviewerAdmittedInputs = Readonly<{
32
32
  ticketNumber?: number;
33
33
  }>;
34
34
 
35
+ const reviewerAmendmentsSchema = Type.Object({
36
+ standards: Type.Optional(Type.String({ description: "Delta relative to the Standards child report: added finding, withdrawal, or factual correction." })),
37
+ spec: Type.Optional(Type.String({ description: "Delta relative to the Spec child report: added finding, withdrawal, or factual correction." })),
38
+ }, { additionalProperties: true, description: "Optional per-axis deltas relative to child reports; not a replacement report. Omit axes with no delta." });
35
39
  const reviewerOutputVariants = Type.Union([
36
- Type.Object({ status: Type.Literal("completed", { description: "Reviewer dispatch completed." }) }, { additionalProperties: false }),
37
- Type.Object({ status: Type.Literal("refused", { description: "Reviewer dispatch was lawfully refused." }), diagnostic: Type.String({ minLength: 1, description: "Diagnostic explaining the refusal." }) }, { additionalProperties: false }),
40
+ Type.Object({
41
+ status: Type.Literal("completed", { description: "Reviewer dispatch completed." }),
42
+ amendments: Type.Optional(reviewerAmendmentsSchema),
43
+ }, { additionalProperties: false }),
44
+ Type.Object({
45
+ status: Type.Literal("refused", { description: "Reviewer dispatch was lawfully refused." }),
46
+ diagnostic: Type.String({ minLength: 1, description: "Diagnostic explaining the refusal." }),
47
+ amendments: Type.Optional(reviewerAmendmentsSchema),
48
+ }, { additionalProperties: false }),
38
49
  ]);
39
50
  const reviewerOutputSchema = openToolObjectFromUnion(reviewerOutputVariants);
40
51
  export type ReviewerRoleDependencies = {
@@ -43,6 +43,8 @@ export function assembleRuntimeReviewerReceipt(input: {
43
43
  }
44
44
  const accepted = input.record.accepted;
45
45
  const skillText = accepted?.input.canonicalSkill ?? input.canonicalSkillText;
46
+ // Seat-owned amendments sit beside reports; never rewrite runtime-owned child bytes.
47
+ const amendments = input.intent.amendments;
46
48
  return freeze({
47
49
  version: 2,
48
50
  status: input.intent.status,
@@ -62,6 +64,7 @@ export function assembleRuntimeReviewerReceipt(input: {
62
64
  : { specFetchedMaterial: accepted.specFetchedMaterial }),
63
65
  }),
64
66
  reports,
67
+ ...(amendments === undefined ? {} : { amendments }),
65
68
  outcomes,
66
69
  identities: {
67
70
  canonicalSkill: { text: skillText },