@akagilnc/pi-workflow-roles 0.1.1751

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 (192) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +104 -0
  3. package/README.zh-CN.md +133 -0
  4. package/THIRD_PARTY_NOTICES.md +60 -0
  5. package/dist/activation-ledger-git.js +68 -0
  6. package/dist/activation-ledger-session.js +120 -0
  7. package/dist/activation-ledger-topology.js +239 -0
  8. package/dist/activation-reconciliation.js +61 -0
  9. package/dist/audit-escalation.js +108 -0
  10. package/dist/auditor-dossier-tool.js +35 -0
  11. package/dist/canonical-json.js +78 -0
  12. package/dist/compliance-transport.js +77 -0
  13. package/dist/doctor-contracts.js +172 -0
  14. package/dist/dossier-resolution.js +103 -0
  15. package/dist/evidence-child-executor.js +661 -0
  16. package/dist/exact-utf8.js +12 -0
  17. package/dist/git-object-id.js +7 -0
  18. package/dist/in-process-session.js +50 -0
  19. package/dist/merger-contracts.js +76 -0
  20. package/dist/navigator-attendance.js +995 -0
  21. package/dist/navigator-invocation-identity.js +220 -0
  22. package/dist/open-tool-schema.js +39 -0
  23. package/dist/package-contracts/collector-output.js +50 -0
  24. package/dist/package-contracts/fixer-output.js +72 -0
  25. package/dist/package-contracts/fixer-packet.js +77 -0
  26. package/dist/package-contracts/judge-output.js +17 -0
  27. package/dist/package-contracts/reviewer-output.js +82 -0
  28. package/dist/package-contracts/terminating-tools.js +173 -0
  29. package/dist/package-contracts/worker-output.js +13 -0
  30. package/dist/package-owned-tool-idle.js +104 -0
  31. package/dist/packaged-role-registry.js +34 -0
  32. package/dist/public-cli/main.js +23867 -0
  33. package/dist/public-command-renderer.js +20 -0
  34. package/dist/reviewer-agent.js +93 -0
  35. package/dist/reviewer-child-executor.js +23 -0
  36. package/dist/reviewer-construction.js +95 -0
  37. package/dist/reviewer-dispatch.js +77 -0
  38. package/dist/reviewer-execution-ledger.js +160 -0
  39. package/dist/reviewer-failure-diagnostic.js +17 -0
  40. package/dist/reviewer-git-snapshot.js +38 -0
  41. package/dist/reviewer-pinned-git.js +146 -0
  42. package/dist/reviewer-preflight-error.js +15 -0
  43. package/dist/reviewer-prompt-identity.js +10 -0
  44. package/dist/reviewer-scope-prompt.js +21 -0
  45. package/dist/reviewer-workspace.js +151 -0
  46. package/dist/sha256.js +5 -0
  47. package/dist/sitian-record-entry.js +33 -0
  48. package/dist/stderr-jsonl.js +26 -0
  49. package/dist/stream-idle-guard.js +75 -0
  50. package/dist/tool-execution-observation.js +141 -0
  51. package/dist/uuidv7.js +21 -0
  52. package/dist/work-subject-identity.js +53 -0
  53. package/extensions/role-runtime.ts +303 -0
  54. package/package.json +69 -0
  55. package/packets/fixer-prerequisites.json +6 -0
  56. package/packets/fixer-repair.md +5 -0
  57. package/packets/judge-apply.md +77 -0
  58. package/packets/judge-authority.md +64 -0
  59. package/packets/judge-plan.md +55 -0
  60. package/packets/judge-review.md +49 -0
  61. package/packets/judge-submission.md +34 -0
  62. package/resources/methods/code-review/SKILL.md +92 -0
  63. package/resources/methods/code-review/agents/openai.yaml +3 -0
  64. package/resources/methods/code-review/provenance.json +26 -0
  65. package/resources/methods/diagnosing-bugs/SKILL.md +134 -0
  66. package/resources/methods/diagnosing-bugs/agents/openai.yaml +3 -0
  67. package/resources/methods/diagnosing-bugs/provenance.json +31 -0
  68. package/resources/methods/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
  69. package/resources/methods/resolving-merge-conflicts/SKILL.md +14 -0
  70. package/resources/methods/resolving-merge-conflicts/agents/openai.yaml +3 -0
  71. package/resources/methods/resolving-merge-conflicts/provenance.json +26 -0
  72. package/resources/methods/tdd/SKILL.md +38 -0
  73. package/resources/methods/tdd/agents/openai.yaml +3 -0
  74. package/resources/methods/tdd/mocking.md +59 -0
  75. package/resources/methods/tdd/provenance.json +36 -0
  76. package/resources/methods/tdd/tests.md +77 -0
  77. package/resources/navigator-route-playbook.md +32 -0
  78. package/schemas/tool-execution-observation.schema.json +107 -0
  79. package/scripts/build-package.mjs +65 -0
  80. package/scripts/generate-tool-execution-observation-schema.ts +7 -0
  81. package/souls/coder.md +10 -0
  82. package/souls/collector.md +11 -0
  83. package/souls/doctor-auditor.md +23 -0
  84. package/souls/doctor.md +8 -0
  85. package/souls/fixer-auditor.md +33 -0
  86. package/souls/fixer.md +13 -0
  87. package/souls/judge-auditor.md +33 -0
  88. package/souls/judge.md +74 -0
  89. package/souls/merger.md +5 -0
  90. package/souls/navigator.md +5 -0
  91. package/souls/reviewer-auditor.md +25 -0
  92. package/souls/reviewer.md +11 -0
  93. package/src/activation-ledger-git.ts +96 -0
  94. package/src/activation-ledger-session.ts +188 -0
  95. package/src/activation-ledger-topology.ts +301 -0
  96. package/src/activation-ledger.ts +240 -0
  97. package/src/activation-reconciliation.ts +163 -0
  98. package/src/activation-trace.ts +38 -0
  99. package/src/audit-escalation.ts +177 -0
  100. package/src/auditor-dossier-tool.ts +48 -0
  101. package/src/auditor-soul.ts +28 -0
  102. package/src/canonical-json.ts +74 -0
  103. package/src/canonical-skill-binding.ts +107 -0
  104. package/src/collector-config.ts +89 -0
  105. package/src/collector-evidence.ts +461 -0
  106. package/src/collector-github.ts +656 -0
  107. package/src/collector-identity.ts +161 -0
  108. package/src/collector-ledger.ts +827 -0
  109. package/src/collector-receipt.ts +87 -0
  110. package/src/collector-role.ts +592 -0
  111. package/src/collector-tool-schemas.ts +19 -0
  112. package/src/compliance-transport.ts +130 -0
  113. package/src/doctor-auditor.ts +53 -0
  114. package/src/doctor-contracts.ts +166 -0
  115. package/src/doctor-evidence.ts +47 -0
  116. package/src/doctor-role.ts +18 -0
  117. package/src/dossier-resolution.ts +137 -0
  118. package/src/evidence-child-executor.ts +775 -0
  119. package/src/exact-utf8.ts +9 -0
  120. package/src/factory-board.ts +1822 -0
  121. package/src/git-object-id.ts +11 -0
  122. package/src/human-format.ts +65 -0
  123. package/src/in-process-session.ts +78 -0
  124. package/src/judge-auditor.ts +55 -0
  125. package/src/judge-recording-anti-forge.ts +53 -0
  126. package/src/judge-role.ts +160 -0
  127. package/src/merger-contracts.ts +71 -0
  128. package/src/merger-git-state.ts +76 -0
  129. package/src/merger-role.ts +60 -0
  130. package/src/navigator-attendance.ts +1254 -0
  131. package/src/navigator-invocation-identity.ts +446 -0
  132. package/src/open-tool-schema.ts +46 -0
  133. package/src/package-contracts/collector-output.ts +109 -0
  134. package/src/package-contracts/fixer-output.ts +81 -0
  135. package/src/package-contracts/fixer-packet.ts +93 -0
  136. package/src/package-contracts/judge-output.ts +39 -0
  137. package/src/package-contracts/reviewer-output.ts +115 -0
  138. package/src/package-contracts/terminating-tools.ts +259 -0
  139. package/src/package-contracts/worker-output.ts +36 -0
  140. package/src/package-owned-tool-idle.ts +134 -0
  141. package/src/package-resources/method-skill-binding.ts +87 -0
  142. package/src/package-resources/method-skill.ts +358 -0
  143. package/src/packaged-role-registry.ts +36 -0
  144. package/src/public-cli/cli-errors.ts +11 -0
  145. package/src/public-cli/cli-io.ts +4 -0
  146. package/src/public-cli/cli.ts +912 -0
  147. package/src/public-cli/coder-run.ts +575 -0
  148. package/src/public-cli/collector-run.ts +375 -0
  149. package/src/public-cli/command-renderer.ts +8 -0
  150. package/src/public-cli/config.ts +346 -0
  151. package/src/public-cli/doctor-run.ts +355 -0
  152. package/src/public-cli/explicit-internal.ts +274 -0
  153. package/src/public-cli/fixer-run.ts +587 -0
  154. package/src/public-cli/host-pi-runtime.ts +112 -0
  155. package/src/public-cli/invocation.ts +1958 -0
  156. package/src/public-cli/judge-run.ts +507 -0
  157. package/src/public-cli/main.ts +15 -0
  158. package/src/public-cli/merger-run.ts +681 -0
  159. package/src/public-cli/public-run-credentials.ts +71 -0
  160. package/src/public-cli/registry.ts +153 -0
  161. package/src/public-cli/reviewer-run.ts +561 -0
  162. package/src/public-cli/run-lifecycle.ts +884 -0
  163. package/src/public-cli/settlement.ts +3765 -0
  164. package/src/public-cli/terminal.ts +325 -0
  165. package/src/public-command-renderer.ts +43 -0
  166. package/src/reviewer-agent.ts +94 -0
  167. package/src/reviewer-auditor.ts +53 -0
  168. package/src/reviewer-child-executor.ts +31 -0
  169. package/src/reviewer-construction.ts +137 -0
  170. package/src/reviewer-dispatch.ts +94 -0
  171. package/src/reviewer-execution-ledger.ts +206 -0
  172. package/src/reviewer-failure-diagnostic.ts +18 -0
  173. package/src/reviewer-git-snapshot.ts +53 -0
  174. package/src/reviewer-pinned-git.ts +144 -0
  175. package/src/reviewer-preflight-error.ts +14 -0
  176. package/src/reviewer-prompt-identity.ts +17 -0
  177. package/src/reviewer-role.ts +193 -0
  178. package/src/reviewer-scope-prompt.ts +24 -0
  179. package/src/reviewer-settlement.ts +63 -0
  180. package/src/reviewer-workspace.ts +111 -0
  181. package/src/role-runtime.ts +884 -0
  182. package/src/sha256.ts +6 -0
  183. package/src/sitian-record-entry.ts +57 -0
  184. package/src/stderr-jsonl.ts +28 -0
  185. package/src/stream-idle-guard.ts +98 -0
  186. package/src/ticket-snapshot.ts +662 -0
  187. package/src/ticket-trajectory.ts +1000 -0
  188. package/src/tool-execution-observation.ts +168 -0
  189. package/src/uuidv7.ts +1 -0
  190. package/src/work-subject-identity.ts +94 -0
  191. package/src/worker-role.ts +434 -0
  192. package/src/worker-submission-gates.ts +225 -0
@@ -0,0 +1,20 @@
1
+ import {
2
+ PACKAGED_ROLE_REGISTRY
3
+ } from "./packaged-role-registry.js";
4
+ const PUBLIC_CALLABLE_ROLES = new Set(
5
+ PACKAGED_ROLE_REGISTRY.map((entry) => entry.role)
6
+ );
7
+ function renderPublicAkRoleCommand(target) {
8
+ if (!PUBLIC_CALLABLE_ROLES.has(target.role)) return void 0;
9
+ const role = target.role;
10
+ if (target.phase === null || target.phase === void 0) {
11
+ return `ak-role ${role}`;
12
+ }
13
+ if (role === "coder" || role === "fixer") {
14
+ return `ak-role ${role} ${target.phase}`;
15
+ }
16
+ return `ak-role ${role}`;
17
+ }
18
+ export {
19
+ renderPublicAkRoleCommand
20
+ };
@@ -0,0 +1,93 @@
1
+ import { isReviewerPromptText } from "./reviewer-prompt-identity.js";
2
+ import { executeReviewerChild } from "./reviewer-child-executor.js";
3
+ import { createReviewerWorkspaceOwner } from "./reviewer-workspace.js";
4
+ import { normalizeReviewerFailureDiagnostic } from "./reviewer-failure-diagnostic.js";
5
+ function reviewerDispatchFailureMessage(outcome) {
6
+ const diagnostics = [...new Set(Object.values(outcome.legs)
7
+ .filter((leg) => leg?.status === "failed")
8
+ .map((leg) => leg.diagnostic.trim())
9
+ .filter((diagnostic) => diagnostic.length > 0))];
10
+ if (diagnostics.length === 0)
11
+ return "Reviewer dispatch execution failed";
12
+ return diagnostics.length === 1 ? diagnostics[0] : diagnostics.join("; ");
13
+ }
14
+ export class ReviewerDispatchExecutionError extends Error {
15
+ outcome;
16
+ constructor(outcome) {
17
+ super(reviewerDispatchFailureMessage(outcome));
18
+ this.outcome = outcome;
19
+ this.name = "ReviewerDispatchExecutionError";
20
+ }
21
+ }
22
+ function classify(error, signal) { if (signal?.aborted)
23
+ return "cancelled"; if (typeof error === "object" && error !== null && "reviewerFailure" in error)
24
+ return error.reviewerFailure; return "unknown"; }
25
+ function failed(error, target, prompt, signal, retained) {
26
+ const attached = typeof error === "object" && error !== null ? error : {};
27
+ const failure = classify(error, signal);
28
+ const diagnostic = normalizeReviewerFailureDiagnostic(error, failure);
29
+ return Object.freeze({
30
+ status: "failed",
31
+ failure,
32
+ diagnostic,
33
+ cause: error,
34
+ target: attached.targetSnapshot ?? target,
35
+ prompt,
36
+ workspaceDisposition: retained === undefined ? attached.workspaceDisposition ?? "not-created" : { retained },
37
+ });
38
+ }
39
+ export function createReviewerAgentRunner(dependencies = {}) {
40
+ const workspaceOwner = createReviewerWorkspaceOwner(dependencies.fault === undefined ? {} : { fault: dependencies.fault });
41
+ let accepted = false;
42
+ return {
43
+ async run(dispatch, options) {
44
+ if (dispatch.recipe !== "reviewer-common-bundle-v1" || dispatch.legs.length < 1 || dispatch.legs.length > 2 || dispatch.legs[0]?.axis !== "standards" || (dispatch.legs.length === 2 && dispatch.legs[1]?.axis !== "spec"))
45
+ throw new Error("Invalid accepted Reviewer dispatch cardinality or axes");
46
+ if (accepted)
47
+ throw new Error("Reviewer runner accepts exactly one dispatch");
48
+ accepted = true;
49
+ for (const leg of dispatch.legs)
50
+ if (!isReviewerPromptText(leg.prompt))
51
+ throw new Error("Accepted Reviewer prompt evidence mismatch");
52
+ let batch;
53
+ try {
54
+ batch = await workspaceOwner.prepare(dispatch.targetSnapshot, dispatch.legs.map(l => l.axis), options.signal);
55
+ }
56
+ catch (error) {
57
+ const prepared = typeof error === "object" && error !== null && "preparedWorkspaces" in error ? error.preparedWorkspaces ?? [] : [];
58
+ const failedIndex = prepared.length;
59
+ const legs = Object.fromEntries(dispatch.legs.map((leg, index) => [leg.axis, failed(error, dispatch.targetSnapshot, leg.prompt, options.signal, index < failedIndex ? prepared[index].path : undefined)]));
60
+ // Siblings not yet attempted have no workspace; do not borrow the failed leg's retained path.
61
+ for (let index = failedIndex + 1; index < dispatch.legs.length; index += 1) {
62
+ const axis = dispatch.legs[index].axis;
63
+ legs[axis] = Object.freeze({ ...legs[axis], workspaceDisposition: "not-created" });
64
+ }
65
+ const target = Object.values(legs)[0].target;
66
+ throw new ReviewerDispatchExecutionError(Object.freeze({ identity: dispatch.identity, target, legs: Object.freeze(legs) }));
67
+ }
68
+ const settled = await Promise.allSettled(batch.workspaces.map(async (workspace) => {
69
+ const leg = dispatch.legs.find(candidate => candidate.axis === workspace.axis);
70
+ try {
71
+ const child = await executeReviewerChild(workspace.path, leg, options.context, {
72
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
73
+ ...(dependencies.credentialScratchParent === undefined
74
+ ? {}
75
+ : { credentialScratchParent: dependencies.credentialScratchParent }),
76
+ });
77
+ const disposition = await workspaceOwner.dispose(workspace);
78
+ return [leg.axis, Object.freeze({ status: "successful", report: child.report, usage: child.usage, target: batch.target, prompt: child.prompt, workspaceDisposition: disposition })];
79
+ }
80
+ catch (error) {
81
+ // Failed legs retain their workspace for the durable failure evidence and caller cleanup.
82
+ return [leg.axis, failed(error, batch.target, leg.prompt, options.signal, workspace.path)];
83
+ }
84
+ }));
85
+ const pairs = settled.map(item => item.status === "fulfilled" ? item.value : (() => { throw item.reason; })());
86
+ const outcome = Object.freeze({ identity: dispatch.identity, target: batch.target, legs: Object.freeze(Object.fromEntries(pairs)) });
87
+ if (Object.values(outcome.legs).some(leg => leg?.status === "failed"))
88
+ throw new ReviewerDispatchExecutionError(outcome);
89
+ return outcome;
90
+ },
91
+ shutdown: () => workspaceOwner.shutdown(),
92
+ };
93
+ }
@@ -0,0 +1,23 @@
1
+ import { executeEvidenceChild } from "./evidence-child-executor.js";
2
+ /**
3
+ * Single conversion at the Reviewer adapter boundary: shared child classifications
4
+ * become Reviewer failure classifications without a second error taxonomy.
5
+ */
6
+ export function projectSharedChildFailure(error) {
7
+ if (typeof error === "object" && error !== null && "evidenceChildFailure" in error) {
8
+ const classification = error.evidenceChildFailure;
9
+ if (classification === "provider" || classification === "child") {
10
+ Object.assign(error, { reviewerFailure: classification });
11
+ }
12
+ }
13
+ return error;
14
+ }
15
+ /** Reviewer policy adapter over the shared evidence-child lifecycle seam. */
16
+ export async function executeReviewerChild(workspace, leg, context, options = {}) {
17
+ try {
18
+ return await executeEvidenceChild(workspace, leg.prompt, context, options);
19
+ }
20
+ catch (error) {
21
+ throw projectSharedChildFailure(error);
22
+ }
23
+ }
@@ -0,0 +1,95 @@
1
+ import { sha256Hex } from "./sha256.js";
2
+ import { reviewerScopePrompt } from "./reviewer-scope-prompt.js";
3
+ export const REVIEWER_CONSTRUCTION_RECIPE = Object.freeze({
4
+ recipeId: "reviewer-common-bundle",
5
+ version: 1,
6
+ runtimeVersion: "1",
7
+ implementationSha256: sha256Hex("reviewer-common-bundle:v1:direct-text-prompts"),
8
+ });
9
+ export const REVIEWER_AXIS_OUTPUT_ADAPTER = Object.freeze({
10
+ adapterId: "reviewer-axis-output",
11
+ version: 1,
12
+ implementationSha256: sha256Hex("reviewer-axis-output:v1:single-axis-verbatim-report+standards-three-priorities"),
13
+ });
14
+ /** Typed Standards conclusion keys owned by reviewer construction (presentation labels are not the contract). */
15
+ export const REVIEWER_STANDARDS_CONCLUSION_KEYS = Object.freeze([
16
+ "constitutionality",
17
+ "minimum-necessary-test-cost",
18
+ "complexity",
19
+ ]);
20
+ const REVIEWER_STANDARDS_CONCLUSION_LABELS = Object.freeze({
21
+ constitutionality: "constitutionality",
22
+ "minimum-necessary-test-cost": "minimum-necessary test cost",
23
+ complexity: "complexity",
24
+ });
25
+ export function reviewerAxisOutputContract(axis) {
26
+ const three = REVIEWER_STANDARDS_CONCLUSION_KEYS;
27
+ return axis === "standards"
28
+ ? Object.freeze({ axis, requiredConclusions: three, excludedConclusions: Object.freeze([]) })
29
+ : Object.freeze({ axis, requiredConclusions: Object.freeze([]), excludedConclusions: three });
30
+ }
31
+ function renderConclusionList(keys, finalJoiner) {
32
+ const labels = keys.map((key) => REVIEWER_STANDARDS_CONCLUSION_LABELS[key]);
33
+ if (labels.length === 0)
34
+ return "";
35
+ if (labels.length === 1)
36
+ return labels[0];
37
+ if (labels.length === 2)
38
+ return `${labels[0]} ${finalJoiner} ${labels[1]}`;
39
+ return `${labels.slice(0, -1).join(", ")}, ${finalJoiner} ${labels[labels.length - 1]}`;
40
+ }
41
+ function renderAxisPriorityClause(contract) {
42
+ if (contract.requiredConclusions.length > 0) {
43
+ return `Inside that single Standards report, explicitly conclude on ${renderConclusionList(contract.requiredConclusions, "and")}; a finding or clear no-finding on each counts as a conclusion.`;
44
+ }
45
+ return `Do not discuss ${renderConclusionList(contract.excludedConclusions, "or")}; those are Standards judgements.`;
46
+ }
47
+ /** Package-owned mechanics layered over the unchanged canonical Skill semantics. */
48
+ export function reviewerAxisMethodAdapter(axis) {
49
+ const contract = reviewerAxisOutputContract(axis);
50
+ const question = axis === "standards"
51
+ ? "Answer only the canonical Standards question, including its complete smell baseline and burden."
52
+ : "Answer only the canonical Spec question.";
53
+ return [
54
+ `Axis-Output-Adapter: ${REVIEWER_AXIS_OUTPUT_ADAPTER.adapterId}@${REVIEWER_AXIS_OUTPUT_ADAPTER.version}:${axis}`,
55
+ "The complete canonical Skill text below remains authoritative semantic input.",
56
+ "For this already-isolated leg, this package adapter supersedes that Skill's dual-agent orchestration, dual-axis aggregation, and dual-section presentation mechanics.",
57
+ question,
58
+ `Emit one substantive ${axis === "standards" ? "Standards" : "Spec"} report. Incidental cross-axis content, headings, and finding-count annotations are presentation matters, not defects.`,
59
+ renderAxisPriorityClause(contract),
60
+ "Before making any substantive claim, actually use the canonical Skill and fixed range facts; a citation without reading those facts is not evidence.",
61
+ "Independently acquire issue, authority, and context; do not treat caller prose as controlling authority or as the Spec source.",
62
+ "You may use any supplied common fact, including facts relevant to the other axis; access and citation do not change the assigned question.",
63
+ "The returned report is the complete output envelope and its UTF-8 bytes are preserved verbatim; no heading parser, sanitizer, section splitter, rewrite, aggregation, or replacement leg follows.",
64
+ ].join("\n");
65
+ }
66
+ /** Deterministic compiler: fixed target/range plus packaged Skill in, dispatch text out. */
67
+ export function constructReviewerDispatch(input) {
68
+ const common = [
69
+ `Target: ${input.range.target}`,
70
+ `Base: ${input.range.base}`,
71
+ `Diff: ${input.range.diffCommand}`,
72
+ reviewerScopePrompt(input.reviewScopeKeys),
73
+ `Recipe: ${REVIEWER_CONSTRUCTION_RECIPE.recipeId}@${REVIEWER_CONSTRUCTION_RECIPE.version}`,
74
+ "Canonical-Skill:",
75
+ input.canonicalSkill,
76
+ "Fixed-Range:",
77
+ JSON.stringify(input.range, null, 2),
78
+ ].join("\n");
79
+ const axes = [{ axis: "standards" }, { axis: "spec" }];
80
+ const legs = axes.map((x) => Object.freeze({
81
+ axis: x.axis,
82
+ prompt: `${common}\n${reviewerAxisMethodAdapter(x.axis)}\n`,
83
+ }));
84
+ return Object.freeze({
85
+ identity: input.identity,
86
+ recipe: "reviewer-common-bundle-v1",
87
+ input: Object.freeze({
88
+ canonicalSkill: input.canonicalSkill,
89
+ construction: REVIEWER_CONSTRUCTION_RECIPE,
90
+ }),
91
+ targetSnapshot: input.target,
92
+ range: input.range,
93
+ legs: Object.freeze(legs),
94
+ });
95
+ }
@@ -0,0 +1,77 @@
1
+ import { sameReviewerPinnedTarget } from "./reviewer-git-snapshot.js";
2
+ import { immutableReviewerPin } from "./reviewer-pinned-git.js";
3
+ export { createReviewerPinnedGitReader, immutableReviewerPin } from "./reviewer-pinned-git.js";
4
+ import { isReviewerPromptText, sameReviewerPromptText } from "./reviewer-prompt-identity.js";
5
+ import { sha256Hex } from "./sha256.js";
6
+ import { constructReviewerDispatch } from "./reviewer-construction.js";
7
+ import { ReviewerCorrectablePreflightError } from "./reviewer-preflight-error.js";
8
+ export { sha256Hex } from "./sha256.js";
9
+ export { isReviewerPromptText as isReviewerPromptIdentity, sameReviewerPromptText as sameReviewerPromptIdentity } from "./reviewer-prompt-identity.js";
10
+ export const REVIEWER_PREFLIGHT_VIOLATIONS = ["base-invalid", "range-invalid", "prompt-identity-invalid", "target-drift"];
11
+ export class ReviewerPreflightError extends Error {
12
+ code;
13
+ diagnostic;
14
+ constructor(code, diagnostic = `${code} constraint failed`) {
15
+ super(`${code}: ${diagnostic}`);
16
+ this.code = code;
17
+ this.diagnostic = diagnostic;
18
+ }
19
+ }
20
+ export function toReviewerExecution(dispatch) {
21
+ return Object.freeze({
22
+ identity: dispatch.identity,
23
+ recipe: dispatch.recipe,
24
+ targetSnapshot: immutableReviewerPin(dispatch.targetSnapshot),
25
+ legs: Object.freeze(dispatch.legs.map((l) => Object.freeze({ axis: l.axis, prompt: l.prompt }))),
26
+ });
27
+ }
28
+ const preflight = (error) => {
29
+ if (error instanceof ReviewerPreflightError)
30
+ return error;
31
+ if (error instanceof ReviewerCorrectablePreflightError) {
32
+ return new ReviewerPreflightError(error.code, error.diagnostic);
33
+ }
34
+ return undefined;
35
+ };
36
+ export function createReviewerDispatcher(d) {
37
+ const target = immutableReviewerPin(d.reader.pin);
38
+ let started = false;
39
+ return Object.freeze({
40
+ async dispatch(baseRevision, invocation) {
41
+ if (started)
42
+ throw new Error("Reviewer fixed dispatch can start exactly once");
43
+ const identity = sha256Hex(JSON.stringify({
44
+ baseRevision,
45
+ target: target.targetHead,
46
+ canonicalSkill: sha256Hex(d.canonicalSkill),
47
+ }));
48
+ let dispatch;
49
+ try {
50
+ const base = await d.reader.resolve(baseRevision);
51
+ const range = await d.reader.range(base);
52
+ dispatch = constructReviewerDispatch({
53
+ identity,
54
+ canonicalSkill: d.canonicalSkill,
55
+ target,
56
+ range,
57
+ ...(d.reviewScopeKeys === undefined ? {} : { reviewScopeKeys: d.reviewScopeKeys }),
58
+ });
59
+ if (!sameReviewerPinnedTarget(await d.reader.snapshot(), target)) {
60
+ throw new ReviewerPreflightError("target-drift", "pinned target snapshot changed before child execution");
61
+ }
62
+ }
63
+ catch (error) {
64
+ const p = preflight(error);
65
+ if (!p)
66
+ throw error;
67
+ d.decisionEvidence?.(Object.freeze({ disposition: "rejected", identity, violations: Object.freeze([p.code]), started: false }));
68
+ return Object.freeze({ status: "rejected", identity, violations: Object.freeze([p.code]), diagnostic: p.diagnostic });
69
+ }
70
+ started = true;
71
+ d.decisionEvidence?.(Object.freeze({ disposition: "accepted", identity, dispatch }));
72
+ const results = await d.run(toReviewerExecution(dispatch), invocation);
73
+ return Object.freeze({ status: "accepted", dispatch, results });
74
+ },
75
+ });
76
+ }
77
+ export { isReviewerPromptText, sameReviewerPromptText };
@@ -0,0 +1,160 @@
1
+ import { sameReviewerPinnedTarget } from "./reviewer-git-snapshot.js";
2
+ import { isReviewerPromptText, sameReviewerPromptText } from "./reviewer-prompt-identity.js";
3
+ import { REVIEWER_PREFLIGHT_VIOLATIONS, } from "./reviewer-dispatch.js";
4
+ export function projectAcceptedDispatch(dispatch) {
5
+ return {
6
+ source: "reviewer-dispatch", type: "accepted", identity: dispatch.identity,
7
+ recipe: dispatch.recipe, input: dispatch.input, target: dispatch.targetSnapshot,
8
+ range: dispatch.range, legs: dispatch.legs,
9
+ };
10
+ }
11
+ export function projectReviewerDispatchOutcome(ledger, dispatch, result) {
12
+ if (result.identity !== dispatch.identity)
13
+ throw new Error("Reviewer runner identity does not match accepted dispatch");
14
+ if (!sameReviewerPinnedTarget(result.target, dispatch.targetSnapshot))
15
+ throw new Error("Reviewer runner target does not match accepted pinned target");
16
+ const expectedAxes = dispatch.legs.map(({ axis }) => axis).sort();
17
+ const actualAxes = Object.keys(result.legs).sort();
18
+ if (actualAxes.length !== expectedAxes.length || actualAxes.some((axis, index) => axis !== expectedAxes[index])) {
19
+ throw new Error(`Reviewer runner result axes do not match accepted dispatch: expected ${expectedAxes.join(",")}; received ${actualAxes.join(",")}`);
20
+ }
21
+ for (const leg of dispatch.legs) {
22
+ const actual = result.legs[leg.axis];
23
+ if (actual === undefined)
24
+ throw new Error(`Reviewer runner omitted ${leg.axis} result`);
25
+ ledger.append(actual.status === "failed"
26
+ ? { source: "reviewer-agent", type: "leg-settled", dispatchIdentity: dispatch.identity, axis: leg.axis, status: "failed", prompt: actual.prompt, target: actual.target, failure: actual.failure, diagnostic: actual.diagnostic, workspaceDisposition: actual.workspaceDisposition }
27
+ : { source: "reviewer-agent", type: "leg-settled", dispatchIdentity: dispatch.identity, axis: leg.axis, status: "successful", prompt: actual.prompt, target: actual.target, report: actual.report, usage: actual.usage, workspaceDisposition: actual.workspaceDisposition });
28
+ }
29
+ }
30
+ function cloneFreeze(value) {
31
+ if (Array.isArray(value))
32
+ return Object.freeze(value.map(cloneFreeze));
33
+ if (typeof value === "object" && value !== null) {
34
+ const copy = {};
35
+ for (const [key, item] of Object.entries(value))
36
+ copy[key] = cloneFreeze(item);
37
+ return Object.freeze(copy);
38
+ }
39
+ return value;
40
+ }
41
+ function hasExactEventShape(event, keys) {
42
+ const actual = Object.keys(event);
43
+ return actual.length === keys.length && actual.every((key) => keys.includes(key));
44
+ }
45
+ function fatal(error) {
46
+ const record = typeof error === "object" && error !== null ? error : undefined;
47
+ return cloneFreeze({
48
+ diagnostics: "infrastructure-failure",
49
+ cause: error,
50
+ ...(record?.targetSnapshot === undefined ? {} : { targetSnapshot: record.targetSnapshot }),
51
+ ...(record?.workspaceDisposition === undefined ? {} : { workspaceDisposition: record.workspaceDisposition }),
52
+ });
53
+ }
54
+ export function createReviewerExecutionLedger() {
55
+ const rejections = [];
56
+ const closedAttempts = [];
57
+ let accepted;
58
+ let started;
59
+ const results = {};
60
+ let infrastructureFailure;
61
+ function append(raw) {
62
+ const event = cloneFreeze(raw);
63
+ if (event.source === "reviewer-dispatch" && event.type === "rejected") {
64
+ if (!hasExactEventShape(event, ["source", "type", "identity", "violations", "started"]) || event.started !== false ||
65
+ event.violations.length === 0 || event.violations.some((code) => !REVIEWER_PREFLIGHT_VIOLATIONS.includes(code)))
66
+ throw new Error("Rejected dispatch must contain only closed bounded non-start evidence");
67
+ if (accepted !== undefined || started !== undefined)
68
+ throw new Error("Rejection cannot follow an accepted dispatch");
69
+ rejections.push(cloneFreeze({ identity: event.identity, violations: event.violations, started: false }));
70
+ return;
71
+ }
72
+ if (event.source === "reviewer-dispatch" && event.type === "closed-attempt") {
73
+ if (!hasExactEventShape(event, ["source", "type", "identity", "reason", "started"]) || event.reason !== "acceptance-closed" || event.started !== false)
74
+ throw new Error("Closed attempt must contain only immutable non-start outcome evidence");
75
+ if (accepted === undefined)
76
+ throw new Error("Closed attempt requires a closed acceptance lifecycle");
77
+ closedAttempts.push(cloneFreeze({ identity: event.identity, reason: event.reason, started: false }));
78
+ return;
79
+ }
80
+ if (event.source === "reviewer-dispatch" && event.type === "accepted") {
81
+ if (accepted !== undefined)
82
+ throw new Error("Projection permits exactly one accepted dispatch");
83
+ const axes = event.legs.map((leg) => leg.axis);
84
+ if (axes[0] !== "standards" || (axes.length !== 1 && (axes.length !== 2 || axes[1] !== "spec")))
85
+ throw new Error("Accepted dispatch sibling axes disagree");
86
+ if (!isReviewerPromptText(event.input.canonicalSkill))
87
+ throw new Error("Accepted canonical Skill must be plain text");
88
+ for (const leg of event.legs) {
89
+ if (!isReviewerPromptText(leg.prompt))
90
+ throw new Error("Accepted compiled prompt must be plain text");
91
+ }
92
+ accepted = event;
93
+ return;
94
+ }
95
+ if (event.source === "reviewer-runtime" && event.type === "fatal") {
96
+ if (infrastructureFailure === undefined)
97
+ infrastructureFailure = cloneFreeze({
98
+ diagnostics: event.diagnostics,
99
+ cause: event.cause,
100
+ ...(event.targetSnapshot === undefined ? {} : { targetSnapshot: event.targetSnapshot }),
101
+ ...(event.workspaceDisposition === undefined ? {} : { workspaceDisposition: event.workspaceDisposition }),
102
+ });
103
+ return;
104
+ }
105
+ if (event.type === "dispatch-started") {
106
+ if (accepted === undefined || event.dispatchIdentity !== accepted.identity)
107
+ throw new Error("Start requires its accepted dispatch");
108
+ if (started !== undefined)
109
+ throw new Error("Accepted dispatch can start exactly once");
110
+ if (event.cardinality !== accepted.legs.length)
111
+ throw new Error("Dispatch start cardinality disagrees with acceptance");
112
+ started = cloneFreeze({ dispatchIdentity: event.dispatchIdentity, cardinality: event.cardinality });
113
+ return;
114
+ }
115
+ if (accepted === undefined || started === undefined || event.dispatchIdentity !== accepted.identity)
116
+ throw new Error("Runner result requires its irreversible accepted dispatch start");
117
+ if (results[event.axis] !== undefined)
118
+ throw new Error(`Reviewer ${event.axis} result can settle exactly once`);
119
+ const compiled = accepted.legs.find((leg) => leg.axis === event.axis);
120
+ if (compiled === undefined)
121
+ throw new Error(`Reviewer ${event.axis} was not an accepted leg`);
122
+ if (!sameReviewerPromptText(event.prompt, compiled.prompt) || !isReviewerPromptText(event.prompt))
123
+ throw new Error("Actual runner prompt does not exactly match compiled prompt text");
124
+ if (!sameReviewerPinnedTarget(event.target, accepted.target))
125
+ throw new Error("Runner target does not match shared pinned target");
126
+ if (event.status === "successful") {
127
+ if (typeof event.report !== "string" || event.report.length === 0 || event.failure !== undefined)
128
+ throw new Error("Successful settlement requires a report");
129
+ }
130
+ else if (event.failure === undefined || event.report !== undefined) {
131
+ throw new Error("Failed settlement requires a bounded failure classification and no report");
132
+ }
133
+ results[event.axis] = event;
134
+ }
135
+ function recordInfrastructureFailure(error) {
136
+ if (infrastructureFailure === undefined) {
137
+ const evidence = fatal(error);
138
+ append({ source: "reviewer-runtime", type: "fatal", ...evidence });
139
+ }
140
+ return error;
141
+ }
142
+ function recordForAudit(status) {
143
+ if (infrastructureFailure !== undefined)
144
+ throw Object.assign(new Error(`Reviewer infrastructure previously failed: ${infrastructureFailure.diagnostics}`), { fatalReviewerInfrastructure: true });
145
+ if (status === "completed") {
146
+ if (accepted === undefined || started === undefined)
147
+ throw new Error("Reviewer completed requires exactly one accepted and started dispatch");
148
+ const expected = accepted.legs.map((leg) => leg.axis);
149
+ if (expected.some((axis) => results[axis]?.status !== "successful") || Object.keys(results).length !== expected.length)
150
+ throw new Error(expected.length === 2 ? "Reviewer completed requires both axes settled successfully" : "Reviewer completed requires Standards settled successfully and no Spec evidence");
151
+ }
152
+ else if (accepted !== undefined) {
153
+ const expected = accepted.legs.map((leg) => leg.axis);
154
+ if (started === undefined || expected.some((axis) => results[axis] === undefined) || Object.keys(results).length !== expected.length)
155
+ throw new Error("Reviewer refused after acceptance requires every expected leg terminal outcome");
156
+ }
157
+ return cloneFreeze({ rejections, closedAttempts, ...(accepted === undefined ? {} : { accepted }), ...(started === undefined ? {} : { started }), results });
158
+ }
159
+ return Object.freeze({ append, recordInfrastructureFailure, recordForAudit });
160
+ }
@@ -0,0 +1,17 @@
1
+ export function normalizeReviewerFailureDiagnostic(error, failure) {
2
+ const original = error instanceof Error && Object.hasOwn(error, "reviewerOriginal")
3
+ ? error.reviewerOriginal
4
+ : error;
5
+ const message = original instanceof Error
6
+ ? original.message.trim()
7
+ : typeof original === "string"
8
+ ? original.trim()
9
+ : typeof original === "object" && original !== null && typeof original.errorMessage === "string"
10
+ ? original.errorMessage.trim()
11
+ : "";
12
+ if (message !== "")
13
+ return message;
14
+ return failure === "provider"
15
+ ? "Reviewer Agent provider supplied no diagnostic details"
16
+ : "Reviewer Agent failed without diagnostic details";
17
+ }
@@ -0,0 +1,38 @@
1
+ export const REVIEW_REF_PREFIXES = ["refs/heads", "refs/tags", "refs/remotes"];
2
+ export function parseReviewerRefSnapshot(raw) {
3
+ const refs = {};
4
+ for (const line of raw.split("\n")) {
5
+ if (!line)
6
+ continue;
7
+ const fields = line.split("\0");
8
+ if (fields.length !== 5 || !fields[0] || !fields[1] || !fields[3]) {
9
+ throw new Error(`Malformed Git ref snapshot line: ${line}`);
10
+ }
11
+ const peeledCommitId = fields[3] === "commit"
12
+ ? fields[1]
13
+ : fields[4] === "commit" && fields[2] ? fields[2] : null;
14
+ refs[fields[0]] = Object.freeze({
15
+ objectId: fields[1],
16
+ peeledCommitId,
17
+ });
18
+ }
19
+ return refs;
20
+ }
21
+ export function reviewerRefSnapshotArgs() {
22
+ return ["for-each-ref", "--format=%(refname)%00%(objectname)%00%(*objectname)%00%(objecttype)%00%(*objecttype)", ...REVIEW_REF_PREFIXES];
23
+ }
24
+ export function immutableReviewerRefs(refs) {
25
+ return Object.freeze(Object.fromEntries(Object.entries(refs)
26
+ .sort(([a], [b]) => a.localeCompare(b))
27
+ .map(([name, entry]) => [name, Object.freeze({ objectId: entry.objectId, peeledCommitId: entry.peeledCommitId })])));
28
+ }
29
+ export function sameReviewerPinnedTarget(actual, expected) {
30
+ return actual.repositoryRoot === expected.repositoryRoot && actual.objectFormat === expected.objectFormat &&
31
+ actual.targetHead === expected.targetHead && sameReviewerRefs(actual.refs, expected.refs);
32
+ }
33
+ export function sameReviewerRefs(actual, expected) {
34
+ const actualEntries = Object.entries(actual).sort(([left], [right]) => left.localeCompare(right));
35
+ const expectedEntries = Object.entries(expected).sort(([left], [right]) => left.localeCompare(right));
36
+ return actualEntries.length === expectedEntries.length && actualEntries.every(([name, value], index) => name === expectedEntries[index]?.[0] && value.objectId === expectedEntries[index]?.[1].objectId &&
37
+ value.peeledCommitId === expectedEntries[index]?.[1].peeledCommitId);
38
+ }