codex-orchestrator 2.0.11 → 2.0.12

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 (262) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +25 -51
  3. package/dist/src/index.d.ts +2 -8
  4. package/dist/src/index.d.ts.map +1 -1
  5. package/dist/src/index.js +1 -4
  6. package/dist/src/index.js.map +1 -1
  7. package/dist/src/v2/acceptance-proof.d.ts +46 -31
  8. package/dist/src/v2/acceptance-proof.d.ts.map +1 -1
  9. package/dist/src/v2/acceptance-proof.js +157 -195
  10. package/dist/src/v2/acceptance-proof.js.map +1 -1
  11. package/dist/src/v2/active-attempt.d.ts +94 -0
  12. package/dist/src/v2/active-attempt.d.ts.map +1 -0
  13. package/dist/src/v2/active-attempt.js +200 -0
  14. package/dist/src/v2/active-attempt.js.map +1 -0
  15. package/dist/src/v2/adapters/command.d.ts +6 -0
  16. package/dist/src/v2/adapters/command.d.ts.map +1 -1
  17. package/dist/src/v2/adapters/command.js +43 -2
  18. package/dist/src/v2/adapters/command.js.map +1 -1
  19. package/dist/src/v2/candidate.d.ts +15 -31
  20. package/dist/src/v2/candidate.d.ts.map +1 -1
  21. package/dist/src/v2/candidate.js +7 -29
  22. package/dist/src/v2/candidate.js.map +1 -1
  23. package/dist/src/v2/checked-change.d.ts +3 -2
  24. package/dist/src/v2/checked-change.d.ts.map +1 -1
  25. package/dist/src/v2/checked-change.js +4 -3
  26. package/dist/src/v2/checked-change.js.map +1 -1
  27. package/dist/src/v2/cli-contract.d.ts +1 -1
  28. package/dist/src/v2/cli-contract.d.ts.map +1 -1
  29. package/dist/src/v2/cli-contract.js +4 -6
  30. package/dist/src/v2/cli-contract.js.map +1 -1
  31. package/dist/src/v2/cli.d.ts +8 -0
  32. package/dist/src/v2/cli.d.ts.map +1 -1
  33. package/dist/src/v2/cli.js +13 -0
  34. package/dist/src/v2/cli.js.map +1 -1
  35. package/dist/src/v2/code-review-report.d.ts +10 -18
  36. package/dist/src/v2/code-review-report.d.ts.map +1 -1
  37. package/dist/src/v2/code-review-report.js +63 -60
  38. package/dist/src/v2/code-review-report.js.map +1 -1
  39. package/dist/src/v2/codex-process.d.ts +6 -2
  40. package/dist/src/v2/codex-process.d.ts.map +1 -1
  41. package/dist/src/v2/codex-process.js +25 -9
  42. package/dist/src/v2/codex-process.js.map +1 -1
  43. package/dist/src/v2/config.d.ts +0 -2
  44. package/dist/src/v2/config.d.ts.map +1 -1
  45. package/dist/src/v2/config.js +3 -6
  46. package/dist/src/v2/config.js.map +1 -1
  47. package/dist/src/v2/contained-report-operation.d.ts +41 -196
  48. package/dist/src/v2/contained-report-operation.d.ts.map +1 -1
  49. package/dist/src/v2/contained-report-operation.js +139 -466
  50. package/dist/src/v2/contained-report-operation.js.map +1 -1
  51. package/dist/src/v2/containment.d.ts +1 -0
  52. package/dist/src/v2/containment.d.ts.map +1 -1
  53. package/dist/src/v2/containment.js +12 -2
  54. package/dist/src/v2/containment.js.map +1 -1
  55. package/dist/src/v2/delivery-authority.d.ts +26 -0
  56. package/dist/src/v2/delivery-authority.d.ts.map +1 -0
  57. package/dist/src/v2/delivery-authority.js +44 -0
  58. package/dist/src/v2/delivery-authority.js.map +1 -0
  59. package/dist/src/v2/direct-delivery.d.ts +16 -36
  60. package/dist/src/v2/direct-delivery.d.ts.map +1 -1
  61. package/dist/src/v2/direct-delivery.js +135 -122
  62. package/dist/src/v2/direct-delivery.js.map +1 -1
  63. package/dist/src/v2/immutable-workflow-publisher.d.ts.map +1 -1
  64. package/dist/src/v2/immutable-workflow-publisher.js +3 -1
  65. package/dist/src/v2/immutable-workflow-publisher.js.map +1 -1
  66. package/dist/src/v2/implementation-report.d.ts +3 -1
  67. package/dist/src/v2/implementation-report.d.ts.map +1 -1
  68. package/dist/src/v2/implementation-report.js +17 -4
  69. package/dist/src/v2/implementation-report.js.map +1 -1
  70. package/dist/src/v2/implementation-reviewer.d.ts +41 -12
  71. package/dist/src/v2/implementation-reviewer.d.ts.map +1 -1
  72. package/dist/src/v2/implementation-reviewer.js +114 -42
  73. package/dist/src/v2/implementation-reviewer.js.map +1 -1
  74. package/dist/src/v2/pending-effect-settlement.d.ts +44 -0
  75. package/dist/src/v2/pending-effect-settlement.d.ts.map +1 -0
  76. package/dist/src/v2/pending-effect-settlement.js +69 -0
  77. package/dist/src/v2/pending-effect-settlement.js.map +1 -0
  78. package/dist/src/v2/process-identity.d.ts +45 -0
  79. package/dist/src/v2/process-identity.d.ts.map +1 -0
  80. package/dist/src/v2/process-identity.js +118 -0
  81. package/dist/src/v2/process-identity.js.map +1 -0
  82. package/dist/src/v2/proof-report.d.ts +2 -1
  83. package/dist/src/v2/proof-report.d.ts.map +1 -1
  84. package/dist/src/v2/proof-report.js +10 -4
  85. package/dist/src/v2/proof-report.js.map +1 -1
  86. package/dist/src/v2/review-feedback-coordinator.d.ts +1 -1
  87. package/dist/src/v2/review-feedback-coordinator.d.ts.map +1 -1
  88. package/dist/src/v2/review-feedback-coordinator.js +1 -1
  89. package/dist/src/v2/review-feedback-coordinator.js.map +1 -1
  90. package/dist/src/v2/review-feedback.d.ts +14 -20
  91. package/dist/src/v2/review-feedback.d.ts.map +1 -1
  92. package/dist/src/v2/review-feedback.js +45 -87
  93. package/dist/src/v2/review-feedback.js.map +1 -1
  94. package/dist/src/v2/run-issue.d.ts +129 -88
  95. package/dist/src/v2/run-issue.d.ts.map +1 -1
  96. package/dist/src/v2/run-issue.js +1965 -2381
  97. package/dist/src/v2/run-issue.js.map +1 -1
  98. package/dist/src/v2/run-state-projections.d.ts +84 -0
  99. package/dist/src/v2/run-state-projections.d.ts.map +1 -0
  100. package/dist/src/v2/run-state-projections.js +142 -0
  101. package/dist/src/v2/run-state-projections.js.map +1 -0
  102. package/dist/src/v2/run-store.d.ts +99 -81
  103. package/dist/src/v2/run-store.d.ts.map +1 -1
  104. package/dist/src/v2/run-store.js +245 -542
  105. package/dist/src/v2/run-store.js.map +1 -1
  106. package/dist/src/v2/runtime-assets.d.ts +3 -0
  107. package/dist/src/v2/runtime-assets.d.ts.map +1 -1
  108. package/dist/src/v2/runtime-assets.js +104 -0
  109. package/dist/src/v2/runtime-assets.js.map +1 -1
  110. package/dist/src/v2/runtime.d.ts +56 -44
  111. package/dist/src/v2/runtime.d.ts.map +1 -1
  112. package/dist/src/v2/runtime.js +383 -503
  113. package/dist/src/v2/runtime.js.map +1 -1
  114. package/dist/src/v2/setup.js +0 -2
  115. package/dist/src/v2/setup.js.map +1 -1
  116. package/dist/src/v2/validation-progression.d.ts +70 -0
  117. package/dist/src/v2/validation-progression.d.ts.map +1 -0
  118. package/dist/src/v2/validation-progression.js +247 -0
  119. package/dist/src/v2/validation-progression.js.map +1 -0
  120. package/dist/src/v2/workflow-assets.d.ts +9 -3
  121. package/dist/src/v2/workflow-assets.d.ts.map +1 -1
  122. package/dist/src/v2/workflow-assets.js +256 -43
  123. package/dist/src/v2/workflow-assets.js.map +1 -1
  124. package/internal-workflow/docs/agents/bug-workflow-routing.md +9 -7
  125. package/internal-workflow/docs/agents/coding-skill-routing.md +170 -120
  126. package/internal-workflow/docs/agents/tool-usage.md +23 -12
  127. package/internal-workflow/manifest.json +1 -1
  128. package/internal-workflow/operations/code-review/SKILL.md +34 -15
  129. package/internal-workflow/operations/implementation/SKILL.md +21 -16
  130. package/internal-workflow/profiles/implementer.toml +9 -0
  131. package/internal-workflow/profiles/review_coordinator.toml +9 -0
  132. package/internal-workflow/profiles/spec_reviewer.toml +9 -0
  133. package/internal-workflow/profiles/standards_reviewer.toml +9 -0
  134. package/internal-workflow/schemas/code-review-v1.json +1 -1
  135. package/internal-workflow/schemas/implementation-report-v1.json +1 -1
  136. package/internal-workflow/schemas/proof-report-v1.json +1 -1
  137. package/internal-workflow/skills/bug-root-cause-explainer/SKILL.md +114 -0
  138. package/internal-workflow/skills/bug-root-cause-explainer/agents/openai.yaml +7 -0
  139. package/internal-workflow/skills/bug-root-cause-explainer/evals/evals.json +18 -0
  140. package/internal-workflow/skills/code-review/SKILL.md +84 -306
  141. package/internal-workflow/skills/code-review/agents/openai.yaml +5 -3
  142. package/internal-workflow/skills/code-review/evals/evals.json +83 -0
  143. package/internal-workflow/skills/code-review/references/standards-smells.md +41 -0
  144. package/internal-workflow/skills/diagnosing-bugs/SKILL.md +69 -32
  145. package/internal-workflow/skills/diagnosing-bugs/agents/openai.yaml +2 -2
  146. package/internal-workflow/skills/diagnosing-bugs/evals/evals.json +63 -0
  147. package/internal-workflow/skills/grilling/SKILL.md +51 -0
  148. package/internal-workflow/skills/grilling/agents/openai.yaml +6 -0
  149. package/internal-workflow/skills/grilling/evals/evals.json +47 -0
  150. package/internal-workflow/skills/implement/SKILL.md +135 -0
  151. package/internal-workflow/skills/implement/agents/openai.yaml +6 -0
  152. package/internal-workflow/skills/implement/evals/evals.json +150 -0
  153. package/internal-workflow/skills/plan/SKILL.md +59 -0
  154. package/internal-workflow/skills/plan/agents/openai.yaml +6 -0
  155. package/internal-workflow/skills/plan/evals/evals.json +36 -0
  156. package/internal-workflow/skills/prototype/LOGIC.md +130 -0
  157. package/internal-workflow/skills/prototype/SKILL.md +69 -0
  158. package/internal-workflow/skills/prototype/UI.md +157 -0
  159. package/internal-workflow/skills/prototype/agents/openai.yaml +6 -0
  160. package/internal-workflow/skills/prototype/evals/evals.json +67 -0
  161. package/internal-workflow/skills/research/SKILL.md +110 -0
  162. package/internal-workflow/skills/research/agents/openai.yaml +6 -0
  163. package/internal-workflow/skills/research/evals/evals.json +49 -0
  164. package/internal-workflow/skills/tdd/SKILL.md +72 -67
  165. package/internal-workflow/skills/tdd/agents/openai.yaml +2 -2
  166. package/internal-workflow/skills/tdd/evals/evals.json +12 -0
  167. package/internal-workflow/skills/tdd/mocking.md +48 -1
  168. package/internal-workflow/skills/tdd/refactoring.md +3 -3
  169. package/internal-workflow/skills/tickets-orchestrator/SKILL.md +199 -0
  170. package/internal-workflow/skills/tickets-orchestrator/agents/openai.yaml +6 -0
  171. package/internal-workflow/skills/tickets-orchestrator/evals/evals.json +126 -0
  172. package/internal-workflow/skills/tickets-orchestrator/references/delegate-integrate.md +83 -0
  173. package/internal-workflow/skills/tickets-orchestrator/references/finish-delivery.md +69 -0
  174. package/internal-workflow/skills/tickets-orchestrator/references/stop-completion.md +63 -0
  175. package/internal-workflow/skills/to-spec/SKILL.md +133 -0
  176. package/internal-workflow/skills/to-spec/agents/openai.yaml +6 -0
  177. package/internal-workflow/skills/to-spec/evals/evals.json +24 -0
  178. package/internal-workflow/skills/to-tickets/SKILL.md +189 -0
  179. package/internal-workflow/skills/to-tickets/agents/openai.yaml +6 -0
  180. package/internal-workflow/skills/to-tickets/evals/evals.json +79 -0
  181. package/internal-workflow/skills/to-tickets/references/publishing-details.md +117 -0
  182. package/package.json +1 -1
  183. package/dist/src/v2/proof-store.d.ts +0 -54
  184. package/dist/src/v2/proof-store.d.ts.map +0 -1
  185. package/dist/src/v2/proof-store.js +0 -301
  186. package/dist/src/v2/proof-store.js.map +0 -1
  187. package/dist/src/v2/route-continuations.d.ts +0 -32
  188. package/dist/src/v2/route-continuations.d.ts.map +0 -1
  189. package/dist/src/v2/route-continuations.js +0 -2
  190. package/dist/src/v2/route-continuations.js.map +0 -1
  191. package/dist/src/v2/route-coordinator.d.ts +0 -72
  192. package/dist/src/v2/route-coordinator.d.ts.map +0 -1
  193. package/dist/src/v2/route-coordinator.js +0 -275
  194. package/dist/src/v2/route-coordinator.js.map +0 -1
  195. package/dist/src/v2/route-decision.d.ts +0 -120
  196. package/dist/src/v2/route-decision.d.ts.map +0 -1
  197. package/dist/src/v2/route-decision.js +0 -380
  198. package/dist/src/v2/route-decision.js.map +0 -1
  199. package/dist/src/v2/spec-coordinator.d.ts +0 -73
  200. package/dist/src/v2/spec-coordinator.d.ts.map +0 -1
  201. package/dist/src/v2/spec-coordinator.js +0 -126
  202. package/dist/src/v2/spec-coordinator.js.map +0 -1
  203. package/dist/src/v2/spec-delivery.d.ts +0 -112
  204. package/dist/src/v2/spec-delivery.d.ts.map +0 -1
  205. package/dist/src/v2/spec-delivery.js +0 -336
  206. package/dist/src/v2/spec-delivery.js.map +0 -1
  207. package/dist/src/v2/triage-route.d.ts +0 -68
  208. package/dist/src/v2/triage-route.d.ts.map +0 -1
  209. package/dist/src/v2/triage-route.js +0 -223
  210. package/dist/src/v2/triage-route.js.map +0 -1
  211. package/dist/src/v2/waiting-human-coordinator.d.ts +0 -49
  212. package/dist/src/v2/waiting-human-coordinator.d.ts.map +0 -1
  213. package/dist/src/v2/waiting-human-coordinator.js +0 -509
  214. package/dist/src/v2/waiting-human-coordinator.js.map +0 -1
  215. package/dist/src/v2/waiting-human.d.ts +0 -143
  216. package/dist/src/v2/waiting-human.d.ts.map +0 -1
  217. package/dist/src/v2/waiting-human.js +0 -408
  218. package/dist/src/v2/waiting-human.js.map +0 -1
  219. package/internal-workflow/docs/agents/contract-test-ledger.md +0 -71
  220. package/internal-workflow/docs/agents/review-gates.md +0 -42
  221. package/internal-workflow/docs/agents/review-protocol.md +0 -98
  222. package/internal-workflow/evals/coding-skill-evals.json +0 -373
  223. package/internal-workflow/operations/ambiguity-review/SKILL.md +0 -5
  224. package/internal-workflow/operations/qualification-repair/SKILL.md +0 -17
  225. package/internal-workflow/operations/spec-author/SKILL.md +0 -12
  226. package/internal-workflow/operations/spec-review/SKILL.md +0 -12
  227. package/internal-workflow/operations/triage/SKILL.md +0 -12
  228. package/internal-workflow/profiles/analyst_deep.toml +0 -9
  229. package/internal-workflow/profiles/implementer_standard.toml +0 -9
  230. package/internal-workflow/profiles/proof_agent.toml +0 -8
  231. package/internal-workflow/profiles/reviewer_deep.toml +0 -9
  232. package/internal-workflow/profiles/reviewer_standard.toml +0 -9
  233. package/internal-workflow/schemas/ambiguity-review-v1.json +0 -1
  234. package/internal-workflow/schemas/spec-author-v1.json +0 -1
  235. package/internal-workflow/schemas/spec-review-v1.json +0 -30
  236. package/internal-workflow/schemas/triage-route-v1.json +0 -1
  237. package/internal-workflow/skills/agent-auto/SKILL.md +0 -19
  238. package/internal-workflow/skills/agent-auto/agents/openai.yaml +0 -6
  239. package/internal-workflow/skills/code-debugger/SKILL.md +0 -122
  240. package/internal-workflow/skills/code-debugger/agents/openai.yaml +0 -7
  241. package/internal-workflow/skills/code-review/references/bug-classes.md +0 -56
  242. package/internal-workflow/skills/code-review/references/cleanup-lens.md +0 -52
  243. package/internal-workflow/skills/code-review/references/framework-lenses.md +0 -34
  244. package/internal-workflow/skills/code-review/references/targeted-recipes.md +0 -49
  245. package/internal-workflow/skills/implementation-spec-maker/SKILL.md +0 -107
  246. package/internal-workflow/skills/implementation-spec-maker/agents/openai.yaml +0 -6
  247. package/internal-workflow/skills/implementation-spec-maker/references/source-modes.md +0 -32
  248. package/internal-workflow/skills/implementation-spec-maker/references/spec-template.md +0 -146
  249. package/internal-workflow/skills/implementation-spec-review/SKILL.md +0 -131
  250. package/internal-workflow/skills/implementation-spec-review/agents/openai.yaml +0 -6
  251. package/internal-workflow/skills/implementation-spec-review/evals/evals.json +0 -78
  252. package/internal-workflow/skills/implementation-spec-review/references/review-loop.md +0 -121
  253. package/internal-workflow/skills/small-task-implementer/SKILL.md +0 -112
  254. package/internal-workflow/skills/small-task-implementer/agents/openai.yaml +0 -6
  255. package/internal-workflow/skills/spec-implementer/SKILL.md +0 -133
  256. package/internal-workflow/skills/spec-implementer/agents/openai.yaml +0 -6
  257. package/internal-workflow/skills/spec-implementer/evals/evals.json +0 -30
  258. package/internal-workflow/skills/spec-implementer/references/review-loop.md +0 -100
  259. package/internal-workflow/skills/triage/AGENT-BRIEF.md +0 -192
  260. package/internal-workflow/skills/triage/OUT-OF-SCOPE.md +0 -101
  261. package/internal-workflow/skills/triage/SKILL.md +0 -134
  262. package/internal-workflow/skills/triage/agents/openai.yaml +0 -6
@@ -1,4 +1,7 @@
1
- import { canonicalJson, sha256 } from './containment.js';
1
+ import { canonicalJson, containsCredentialEvidence, sha256 } from './containment.js';
2
+ import { decodeAgentReportForValidation } from './report-envelope.js';
3
+ import { hashCodeReviewReport, validateCodeReviewReport, } from './code-review-report.js';
4
+ const MAX_STRING_LENGTH = 16 * 1024;
2
5
  const POLICY_KEYS = [
3
6
  'sandboxMode', 'cwdClass', 'worktreeAccess', 'writableRootClasses', 'runnerPostcondition',
4
7
  'network', 'networkHosts', 'mcpTools', 'approvalCeiling', 'externalWrite',
@@ -9,494 +12,160 @@ export class InjectedContainedReportOperation {
9
12
  this.dependencies = dependencies;
10
13
  }
11
14
  async run(input) {
12
- const existing = await input.invocationState.read();
13
- if (existing)
14
- return this.recover(input, validateDurableReportInvocation(existing));
15
- return this.launch(input);
16
- }
17
- async launch(input) {
18
- let baseline;
15
+ let before;
19
16
  try {
20
- baseline = requireReportSnapshot(await this.dependencies.snapshot(input.worktreePath, input.operation));
17
+ before = requireReportSnapshot(await this.dependencies.snapshot(input.worktreePath));
21
18
  }
22
19
  catch {
23
- return blocked('safety', 'report-operation-snapshot-failed');
20
+ return { status: 'blocked', kind: 'safety', code: 'report-operation-snapshot-failed' };
24
21
  }
25
- const attemptId = this.dependencies.createAttemptId();
26
- if (!attemptId || input.forbiddenAttemptIds?.includes(attemptId))
27
- return blocked('safety', 'report-operation-attempt-identity-invalid');
28
22
  let attempt;
29
23
  try {
30
- attempt = await this.prepare(input, attemptId);
31
- }
32
- catch (error) {
33
- return error instanceof ReportAuthorityError
34
- ? blocked('safety', 'report-operation-authority-drift')
35
- : blocked('external', 'report-operation-prepare-failed');
36
- }
37
- const prepared = {
38
- version: 1, operation: input.operation, attemptId, generationHash: input.workflowGeneration.generationHash,
39
- promptFactsSha256: promptFactsSha256(input.promptFacts),
40
- reportPath: attempt.reportPath, phase: 'prepared', host: this.dependencies.host, bootId: this.dependencies.bootId,
41
- preparedAt: this.dependencies.now(), launchedAt: null, pid: null, processStartIdentity: null,
42
- processGroupId: null, baseline,
43
- };
44
- if (!await input.invocationState.compareAndSwap(undefined, prepared))
45
- return blocked('safety', 'report-operation-state-conflict');
46
- let current = prepared;
47
- let result;
48
- try {
49
- await input.beforeLaunch?.();
50
- }
51
- catch (error) {
52
- const abandoned = await this.abandon(input, current, attempt);
53
- if (abandoned)
54
- return abandoned;
55
- throw error;
56
- }
57
- try {
58
- result = await this.dependencies.launch({
59
- ...input,
60
- attempt,
61
- onSpawned: async ({ pid, processGroupId }) => {
62
- const processStartIdentity = await this.dependencies.processStartIdentity(pid);
63
- if (!processStartIdentity)
64
- throw new Error('report process identity unavailable');
65
- const launched = {
66
- ...prepared, phase: 'launched', launchedAt: this.dependencies.now(), pid, processStartIdentity, processGroupId,
67
- };
68
- if (!await input.invocationState.compareAndSwap(prepared, launched))
69
- throw new Error('report launch state conflict');
70
- current = launched;
71
- },
24
+ attempt = await this.dependencies.prepare({
25
+ operation: input.operation,
26
+ attemptId: input.attemptId,
27
+ runId: input.runId,
28
+ workflowGeneration: input.workflowGeneration,
72
29
  });
73
30
  }
74
31
  catch {
75
- result = { status: 'retryable', code: 'report-operation-launch-failed' };
76
- }
77
- if (result.status === 'completed')
78
- return this.finish(input, current, attempt, result.reportBytes);
79
- if (result.status === 'safe-halt')
80
- return { status: 'safe-halt', code: 'report-operation-process-unresolved' };
81
- if (result.status === 'cancelled' || result.status === 'blocked') {
82
- const abandoned = await this.abandon(input, current, attempt);
83
- if (abandoned)
84
- return abandoned;
85
- return result;
86
- }
87
- const recovered = await this.observeReport(current.reportPath);
88
- if (recovered.status === 'unknown')
89
- return { status: 'safe-halt', code: 'report-operation-report-observation-unknown' };
90
- if (recovered.status === 'available')
91
- return this.finish(input, current, attempt, recovered.bytes);
92
- const abandoned = await this.abandon(input, current, attempt);
93
- if (abandoned)
94
- return abandoned;
95
- return result;
96
- }
97
- async recover(input, invocation) {
98
- if (invocation.operation !== input.operation || invocation.generationHash !== input.workflowGeneration.generationHash
99
- || invocation.host !== this.dependencies.host || invocation.bootId !== this.dependencies.bootId) {
100
- return { status: 'safe-halt', code: 'report-operation-owner-identity-unresolved' };
101
- }
102
- if (invocation.promptFactsSha256 !== promptFactsSha256(input.promptFacts)) {
103
- return { status: 'safe-halt', code: 'report-operation-prompt-facts-drift' };
104
- }
105
- let attempt;
106
- try {
107
- attempt = await this.prepare(input, invocation.attemptId);
108
- }
109
- catch (error) {
110
- return error instanceof ReportAuthorityError
111
- ? blocked('safety', 'report-operation-authority-drift')
112
- : blocked('external', 'report-operation-prepare-failed');
113
- }
114
- if (attempt.reportPath !== invocation.reportPath)
115
- return blocked('safety', 'report-operation-report-path-drift');
116
- const report = await this.observeReport(invocation.reportPath);
117
- if (report.status === 'unknown')
118
- return { status: 'safe-halt', code: 'report-operation-report-observation-unknown' };
119
- if (invocation.phase === 'prepared') {
120
- if (report.status === 'available')
121
- return this.finish(input, invocation, attempt, report.bytes);
122
- if (!await this.settleAttempt(attempt))
123
- return { status: 'safe-halt', code: 'report-operation-attempt-cleanup-failed' };
124
- if (!await input.invocationState.compareAndSwap(invocation, undefined))
125
- return blocked('safety', 'report-operation-state-conflict');
126
- return { status: 'retryable', code: 'report-operation-prepared-attempt-abandoned' };
127
- }
128
- const unresolved = await this.unresolvedProcess(invocation);
129
- if (unresolved)
130
- return unresolved;
131
- if (report.status === 'available')
132
- return this.finish(input, invocation, attempt, report.bytes);
133
- if (!await this.settleAttempt(attempt))
134
- return { status: 'safe-halt', code: 'report-operation-attempt-cleanup-failed' };
135
- if (!await input.invocationState.compareAndSwap(invocation, undefined))
136
- return blocked('safety', 'report-operation-state-conflict');
137
- return { status: 'retryable', code: 'report-operation-output-unavailable' };
138
- }
139
- async abandon(input, invocation, attempt) {
140
- if (invocation.phase === 'launched') {
141
- const unresolved = await this.unresolvedProcess(invocation);
142
- if (unresolved)
143
- return unresolved;
144
- }
145
- if (!await this.settleAttempt(attempt))
146
- return { status: 'safe-halt', code: 'report-operation-attempt-cleanup-failed' };
147
- if (!await input.invocationState.compareAndSwap(invocation, undefined))
148
- return blocked('safety', 'report-operation-state-conflict');
149
- return undefined;
150
- }
151
- async observeReport(path) {
152
- try {
153
- return await this.dependencies.readReport(path);
154
- }
155
- catch {
156
- return { status: 'unknown' };
157
- }
158
- }
159
- async unresolvedProcess(invocation) {
160
- const observed = await this.dependencies.inspectProcess(invocation).catch(() => ({ status: 'unknown' }));
161
- if (observed.status === 'unknown')
162
- return { status: 'safe-halt', code: 'report-operation-process-observation-unknown' };
163
- if (observed.status === 'present' && (observed.processStartIdentity === invocation.processStartIdentity || observed.processGroupAlive !== false)) {
164
- return { status: 'safe-halt', code: 'report-operation-process-active-or-uncertain' };
165
- }
166
- return observed.status === 'absent' && observed.processGroupAlive !== false
167
- ? { status: 'safe-halt', code: 'report-operation-process-group-unresolved' } : undefined;
168
- }
169
- async settleAttempt(attempt) {
170
- try {
171
- await this.dependencies.settleAttempt(attempt);
172
- return true;
173
- }
174
- catch {
175
- return false;
176
- }
177
- }
178
- async prepare(input, attemptId) {
179
- const attempt = await this.dependencies.prepare({
180
- operation: input.operation, attemptId, runId: input.runId, workflowGeneration: input.workflowGeneration,
181
- });
182
- if (!hasExactReportAuthority(attempt, input) || !attempt.reportPath)
183
- throw new ReportAuthorityError();
184
- return attempt;
185
- }
186
- async finish(input, invocation, attempt, reportBytes) {
187
- let after;
188
- try {
189
- after = requireReportSnapshot(await this.dependencies.snapshot(input.worktreePath, input.operation));
190
- }
191
- catch {
192
- return blocked('safety', 'report-operation-snapshot-failed');
193
- }
194
- if (canonicalJson(invocation.baseline) !== canonicalJson(after))
195
- return blocked('safety', 'report-operation-worktree-mutated');
196
- if (!await this.settleAttempt(attempt))
197
- return { status: 'safe-halt', code: 'report-operation-attempt-cleanup-failed' };
198
- return { status: 'completed', attemptId: invocation.attemptId, reportBytes: Buffer.from(reportBytes), reportSha256: sha256(reportBytes) };
199
- }
200
- }
201
- export class InjectedContainedMutableOperation {
202
- dependencies;
203
- constructor(dependencies) {
204
- this.dependencies = dependencies;
205
- }
206
- async run(input) {
207
- const existing = await input.invocationState.read();
208
- return existing ? this.recover(input, validateDurableMutableInvocation(existing)) : this.launch(input);
209
- }
210
- async settle(input) {
211
- const existing = await input.invocationState.read();
212
- if (!existing)
213
- return { status: 'settled' };
214
- const recovered = await this.recover(input, validateDurableMutableInvocation(existing));
215
- if (recovered.status === 'retryable') {
216
- return await input.invocationState.read() === undefined
217
- ? { status: 'settled' }
218
- : { status: 'safe-halt', code: 'mutable-operation-state-conflict' };
32
+ return this.finishWithSnapshot(input.worktreePath, before, {
33
+ status: 'blocked', kind: 'external', code: 'report-operation-prepare-failed',
34
+ });
219
35
  }
220
- if (recovered.status === 'completed') {
221
- const adopted = await input.invocationState.read();
222
- if (!adopted || adopted.attemptId !== recovered.attemptId || adopted.phase !== 'adopted') {
223
- return { status: 'safe-halt', code: 'mutable-operation-state-conflict' };
36
+ let launchResult;
37
+ if (!hasExactReadOnlyAuthority(attempt, input)) {
38
+ launchResult = { status: 'blocked', kind: 'safety', code: 'report-operation-authority-drift' };
39
+ }
40
+ else {
41
+ if (isImplementationReview(input.operation)) {
42
+ if (!input.reviewContext || !input.onPrepared || !input.onLaunched) {
43
+ return this.finishWithSnapshot(input.worktreePath, before, {
44
+ status: 'blocked', kind: 'safety', code: 'review-operation-launch-gate-missing',
45
+ });
46
+ }
47
+ try {
48
+ await input.onPrepared();
49
+ }
50
+ catch {
51
+ return this.finishWithSnapshot(input.worktreePath, before, {
52
+ status: 'blocked', kind: 'safety', code: 'review-operation-prepare-persistence-failed',
53
+ });
54
+ }
55
+ }
56
+ try {
57
+ launchResult = await this.dependencies.launch({ ...input, attempt });
58
+ }
59
+ catch {
60
+ launchResult = { status: 'blocked', kind: 'external', code: 'report-operation-launch-failed' };
224
61
  }
225
- return await input.invocationState.compareAndSwap(adopted, undefined)
226
- ? { status: 'settled' }
227
- : { status: 'safe-halt', code: 'mutable-operation-state-conflict' };
228
- }
229
- return { status: 'safe-halt', code: recovered.status === 'cancelled'
230
- ? 'mutable-operation-settlement-cancelled' : recovered.code };
231
- }
232
- async launch(input) {
233
- let baseline;
234
- try {
235
- baseline = requireReportSnapshot(await this.dependencies.snapshot(input.worktreePath));
236
- }
237
- catch {
238
- return blocked('safety', 'mutable-operation-snapshot-failed');
239
- }
240
- const attemptId = this.dependencies.createAttemptId();
241
- if (!attemptId)
242
- return blocked('safety', 'mutable-operation-attempt-identity-invalid');
243
- let attempt;
244
- try {
245
- attempt = await this.prepare(input, attemptId);
246
- }
247
- catch (error) {
248
- return error instanceof ReportAuthorityError
249
- ? blocked('safety', 'mutable-operation-authority-drift') : blocked('external', 'mutable-operation-prepare-failed');
250
62
  }
251
- const prepared = {
252
- version: 1, operation: input.operation, attemptId, generationHash: input.workflowGeneration.generationHash,
253
- promptFactsSha256: promptFactsSha256(input.promptFacts), worktreePath: input.worktreePath, reportPath: attempt.reportPath,
254
- context: structuredClone(input.context),
255
- phase: 'prepared', host: this.dependencies.host, bootId: this.dependencies.bootId, preparedAt: this.dependencies.now(),
256
- launchedAt: null, pid: null, processStartIdentity: null, processGroupId: null, baseline, reportSha256: null, resultSnapshot: null,
257
- };
258
- if (!await input.invocationState.compareAndSwap(undefined, prepared))
259
- return blocked('safety', 'mutable-operation-state-conflict');
260
- let current = prepared;
261
- let result;
262
- try {
263
- await input.beforeLaunch?.();
264
- result = await this.dependencies.launch({
265
- ...input, attempt,
266
- onSpawned: async ({ pid, processGroupId }) => {
267
- const processStartIdentity = await this.dependencies.processStartIdentity(pid);
268
- if (!processStartIdentity)
269
- throw new Error('mutable process identity unavailable');
270
- const launched = { ...prepared, phase: 'launched', launchedAt: this.dependencies.now(),
271
- pid, processStartIdentity, processGroupId };
272
- if (!await input.invocationState.compareAndSwap(prepared, launched))
273
- throw new Error('mutable launch state conflict');
274
- current = launched;
63
+ if (launchResult.status === 'safe-halt') {
64
+ return {
65
+ status: 'safe-halt',
66
+ process: {
67
+ pid: launchResult.pid,
68
+ processGroupId: launchResult.processGroupId,
69
+ startedAt: launchResult.startedAt,
70
+ baseline: structuredClone(before),
275
71
  },
276
- });
72
+ };
277
73
  }
278
- catch {
279
- return { status: 'retryable', code: 'mutable-operation-launch-failed' };
280
- }
281
- if (result.status === 'completed')
282
- return current.phase === 'launched'
283
- ? this.adopt(input, current, result.reportBytes)
284
- : { status: 'safe-halt', code: 'mutable-operation-launch-unfenced' };
285
- if (result.status === 'safe-halt')
286
- return { status: 'safe-halt', code: 'mutable-operation-process-unresolved' };
287
- if (result.status === 'cancelled' || result.status === 'blocked') {
288
- const unresolved = current.phase === 'launched' ? await this.unresolvedProcess(current) : undefined;
289
- if (unresolved)
290
- return unresolved;
291
- const snapshot = await this.stableSnapshot(input.worktreePath);
292
- if (!snapshot || canonicalJson(snapshot) !== canonicalJson(current.baseline))
293
- return { status: 'safe-halt', code: 'mutable-operation-abandonment-ambiguous' };
294
- if (!await input.invocationState.compareAndSwap(current, undefined))
295
- return blocked('safety', 'mutable-operation-state-conflict');
296
- }
297
- return result;
74
+ return this.finishWithSnapshot(input.worktreePath, before, launchResult, input, attempt);
298
75
  }
299
- async recover(input, invocation) {
300
- if (invocation.operation !== input.operation
301
- || invocation.generationHash !== input.workflowGeneration.generationHash || invocation.promptFactsSha256 !== promptFactsSha256(input.promptFacts)
302
- || canonicalJson(invocation.context) !== canonicalJson(input.context)
303
- || invocation.worktreePath !== input.worktreePath || invocation.host !== this.dependencies.host || invocation.bootId !== this.dependencies.bootId)
304
- return { status: 'safe-halt', code: 'mutable-operation-correlation-drift' };
305
- let attempt;
306
- try {
307
- attempt = await this.prepare(input, invocation.attemptId);
308
- }
309
- catch {
310
- return { status: 'safe-halt', code: 'mutable-operation-prepare-recovery-failed' };
311
- }
312
- if (attempt.reportPath !== invocation.reportPath)
313
- return { status: 'safe-halt', code: 'mutable-operation-report-path-drift' };
314
- const report = await this.observeReport(invocation.reportPath);
315
- if (report.status === 'unknown')
316
- return { status: 'safe-halt', code: 'mutable-operation-report-observation-unknown' };
317
- if (invocation.phase === 'adopted') {
318
- if (report.status !== 'available' || sha256(report.bytes) !== invocation.reportSha256)
319
- return { status: 'safe-halt', code: 'mutable-operation-adopted-report-drift' };
320
- const snapshot = await this.stableSnapshot(input.worktreePath);
321
- if (!snapshot || canonicalJson(snapshot) !== canonicalJson(invocation.resultSnapshot))
322
- return { status: 'safe-halt', code: 'mutable-operation-adopted-worktree-drift' };
323
- return this.completeAdopted(input, invocation, report.bytes);
324
- }
325
- if (invocation.phase === 'prepared') {
326
- if (report.status === 'available')
327
- return { status: 'safe-halt', code: 'mutable-operation-prepared-report-ambiguous' };
328
- const snapshot = await this.stableSnapshot(input.worktreePath);
329
- if (!snapshot || canonicalJson(snapshot) !== canonicalJson(invocation.baseline))
330
- return { status: 'safe-halt', code: 'mutable-operation-prepared-worktree-ambiguous' };
331
- if (!await input.invocationState.compareAndSwap(invocation, undefined))
332
- return blocked('safety', 'mutable-operation-state-conflict');
333
- return { status: 'retryable', code: 'mutable-operation-prepared-attempt-abandoned' };
334
- }
335
- const unresolved = await this.unresolvedProcess(invocation);
336
- if (unresolved)
337
- return unresolved;
338
- if (report.status === 'available')
339
- return this.adopt(input, invocation, report.bytes);
340
- const snapshot = await this.stableSnapshot(input.worktreePath);
341
- if (!snapshot || canonicalJson(snapshot) !== canonicalJson(invocation.baseline))
342
- return { status: 'safe-halt', code: 'mutable-operation-worktree-without-report' };
343
- if (!await input.invocationState.compareAndSwap(invocation, undefined))
344
- return blocked('safety', 'mutable-operation-state-conflict');
345
- return { status: 'retryable', code: 'mutable-operation-output-unavailable' };
346
- }
347
- async adopt(input, invocation, reportBytes) {
348
- const resultSnapshot = await this.stableSnapshot(input.worktreePath);
349
- if (!resultSnapshot || resultSnapshot.worktreeIdentity !== invocation.baseline.worktreeIdentity || resultSnapshot.headSha !== invocation.baseline.headSha)
350
- return { status: 'safe-halt', code: 'mutable-operation-result-worktree-mismatch' };
351
- const adopted = { ...invocation, phase: 'adopted', reportSha256: sha256(reportBytes), resultSnapshot };
352
- if (!await input.invocationState.compareAndSwap(invocation, adopted))
353
- return blocked('safety', 'mutable-operation-state-conflict');
354
- return this.completeAdopted(input, adopted, reportBytes);
355
- }
356
- async completeAdopted(input, invocation, reportBytes) {
357
- if (invocation.context.repairOnly && canonicalJson(invocation.baseline) !== canonicalJson(invocation.resultSnapshot)) {
358
- if (!await input.invocationState.compareAndSwap(invocation, undefined))
359
- return blocked('safety', 'mutable-operation-state-conflict');
360
- return blocked('safety', 'report-repair-modified-worktree');
361
- }
362
- return completedMutable(invocation, reportBytes);
363
- }
364
- async prepare(input, attemptId) {
365
- const operation = workerOperation(input.operation);
366
- const attempt = await this.dependencies.prepare({ operation, attemptId, runId: input.runId, workflowGeneration: input.workflowGeneration });
367
- if (attempt.operation !== operation || attempt.generationHash !== input.workflowGeneration.generationHash || !hasExactMutableAuthority(attempt.policy)
368
- || !attempt.reportPath)
369
- throw new ReportAuthorityError();
370
- return attempt;
371
- }
372
- async observeReport(path) {
373
- try {
374
- return await this.dependencies.readReport(path);
375
- }
376
- catch {
377
- return { status: 'unknown' };
378
- }
379
- }
380
- async stableSnapshot(worktreePath) {
76
+ async finishWithSnapshot(worktreePath, before, launchResult, input, attempt) {
77
+ let after;
381
78
  try {
382
- const first = requireReportSnapshot(await this.dependencies.snapshot(worktreePath));
383
- const second = requireReportSnapshot(await this.dependencies.snapshot(worktreePath));
384
- return canonicalJson(first) === canonicalJson(second) ? first : undefined;
79
+ after = requireReportSnapshot(await this.dependencies.snapshot(worktreePath));
385
80
  }
386
81
  catch {
387
- return undefined;
388
- }
389
- }
390
- async unresolvedProcess(invocation) {
391
- const observed = await this.dependencies.inspectProcess(invocation).catch(() => ({ status: 'unknown' }));
392
- if (observed.status === 'unknown')
393
- return { status: 'safe-halt', code: 'mutable-operation-process-observation-unknown' };
394
- if (observed.status === 'present' && (observed.processStartIdentity === invocation.processStartIdentity || observed.processGroupAlive !== false))
395
- return { status: 'safe-halt', code: 'mutable-operation-process-active-or-uncertain' };
396
- return observed.status === 'absent' && observed.processGroupAlive !== false
397
- ? { status: 'safe-halt', code: 'mutable-operation-process-group-unresolved' } : undefined;
398
- }
399
- }
400
- export function validateDurableMutableInvocation(value) {
401
- const keys = [
402
- 'version', 'operation', 'attemptId', 'generationHash', 'promptFactsSha256', 'worktreePath', 'reportPath',
403
- 'context', 'phase', 'host', 'bootId', 'preparedAt', 'launchedAt', 'pid', 'processStartIdentity', 'processGroupId', 'baseline',
404
- 'reportSha256', 'resultSnapshot',
405
- ];
406
- if (!hasExactKeys(value, keys))
407
- throw new Error('durable mutable invocation is invalid');
408
- const record = value;
409
- if (record.version !== 1 || !['qualification-repair', 'implementation', 'review-feedback-implementation'].includes(record.operation)
410
- || !nonEmpty(record.attemptId) || !/^[0-9a-f]{64}$/u.test(record.generationHash)
411
- || !/^[0-9a-f]{64}$/u.test(record.promptFactsSha256) || !nonEmpty(record.worktreePath) || !nonEmpty(record.reportPath)
412
- || !nonEmpty(record.host) || !nonEmpty(record.bootId) || !timestamp(record.preparedAt)
413
- || !['prepared', 'launched', 'adopted'].includes(record.phase))
414
- throw new Error('durable mutable invocation fields are invalid');
415
- const context = record.context;
416
- if (!hasExactKeys(context, ['repairOnly', 'reworkFindings']) || typeof context.repairOnly !== 'boolean'
417
- || !Array.isArray(context.reworkFindings) || !context.reworkFindings.every(nonEmpty)) {
418
- throw new Error('durable mutable invocation context is invalid');
419
- }
420
- const launched = record.phase === 'launched' || record.phase === 'adopted';
421
- if (launched !== (positive(record.pid) && positive(record.processGroupId) && nonEmpty(record.processStartIdentity) && timestamp(record.launchedAt)))
422
- throw new Error('durable mutable invocation process identity is invalid');
423
- if (!launched && (record.pid !== null || record.processGroupId !== null || record.processStartIdentity !== null || record.launchedAt !== null))
424
- throw new Error('prepared mutable invocation has process identity');
425
- const adopted = record.phase === 'adopted';
426
- if (adopted !== (typeof record.reportSha256 === 'string'
427
- && /^[0-9a-f]{64}$/u.test(record.reportSha256) && record.resultSnapshot !== null))
428
- throw new Error('durable mutable invocation adoption is invalid');
429
- if (!adopted && (record.reportSha256 !== null || record.resultSnapshot !== null))
430
- throw new Error('unfinished mutable invocation has adoption evidence');
431
- requireReportSnapshot(record.baseline);
432
- if (adopted)
433
- requireReportSnapshot(record.resultSnapshot);
434
- return structuredClone(value);
435
- }
436
- export function validateDurableReportInvocation(value) {
437
- const keys = [
438
- 'version', 'operation', 'attemptId', 'generationHash', 'promptFactsSha256', 'reportPath', 'phase', 'host', 'bootId', 'preparedAt',
439
- 'launchedAt', 'pid', 'processStartIdentity', 'processGroupId', 'baseline',
440
- ];
441
- if (!hasExactKeys(value, keys))
442
- throw new Error('durable report invocation is invalid');
443
- const record = value;
444
- if (record.version !== 1 || !['triage', 'ambiguity-review', 'code-review', 'spec-author', 'spec-review', 'acceptance-proof'].includes(record.operation)
445
- || !nonEmpty(record.attemptId) || !/^[0-9a-f]{64}$/u.test(record.generationHash)
446
- || !nonEmpty(record.reportPath) || !nonEmpty(record.host) || !nonEmpty(record.bootId)
447
- || !/^[0-9a-f]{64}$/u.test(record.promptFactsSha256)
448
- || !timestamp(record.preparedAt) || !['prepared', 'launched'].includes(record.phase))
449
- throw new Error('durable report invocation fields are invalid');
450
- const launched = record.phase === 'launched';
451
- if (launched !== (positive(record.pid) && positive(record.processGroupId) && nonEmpty(record.processStartIdentity)
452
- && timestamp(record.launchedAt)))
453
- throw new Error('durable report invocation process identity is invalid');
454
- if (!launched && (record.pid !== null || record.processGroupId !== null || record.processStartIdentity !== null || record.launchedAt !== null)) {
455
- throw new Error('prepared report invocation has process identity');
82
+ return { status: 'blocked', kind: 'safety', code: 'report-operation-snapshot-failed' };
83
+ }
84
+ if (!sameSnapshot(before, after)) {
85
+ return { status: 'blocked', kind: 'safety', code: 'report-operation-worktree-mutated' };
86
+ }
87
+ if (launchResult.status !== 'completed')
88
+ return launchResult;
89
+ if (!input)
90
+ return { status: 'blocked', kind: 'external', code: 'report-operation-prepare-failed' };
91
+ return validateCompletedReport(input.operation, input.attemptId, launchResult.reportBytes, {
92
+ ...input.reviewContext,
93
+ availableReviewers: [...(attempt?.reviewers ?? [])],
94
+ });
456
95
  }
457
- requireReportSnapshot(record.baseline);
458
- return structuredClone(value);
459
96
  }
460
- function hasExactReportAuthority(attempt, input) {
97
+ function hasExactReadOnlyAuthority(attempt, input) {
461
98
  const policy = attempt.policy;
462
- if (attempt.operation !== input.operation || attempt.generationHash !== input.workflowGeneration.generationHash
463
- || !hasExactKeys(policy, POLICY_KEYS) || policy.network !== 'deny' || policy.networkHosts.length !== 0
464
- || policy.mcpTools.length !== 0 || policy.approvalCeiling !== 'never' || policy.externalWrite !== false)
465
- return false;
466
- if (input.operation === 'spec-author') {
467
- return policy.sandboxMode === 'workspace-write' && policy.cwdClass === 'target-state'
468
- && policy.worktreeAccess === 'write' && canonicalJson(policy.writableRootClasses) === canonicalJson(['target-state'])
469
- && policy.runnerPostcondition === 'spec-only';
99
+ return attempt.operation === input.operation
100
+ && attempt.generationHash === input.workflowGeneration.generationHash
101
+ && hasExactKeys(policy, POLICY_KEYS)
102
+ && policy.sandboxMode === 'read-only'
103
+ && policy.cwdClass === 'worktree'
104
+ && policy.worktreeAccess === 'read-only'
105
+ && Array.isArray(policy.writableRootClasses)
106
+ && policy.writableRootClasses.length === 0
107
+ && policy.runnerPostcondition === 'report-only'
108
+ && policy.network === 'deny'
109
+ && Array.isArray(policy.networkHosts)
110
+ && policy.networkHosts.length === 0
111
+ && Array.isArray(policy.mcpTools)
112
+ && policy.mcpTools.length === 0
113
+ && policy.approvalCeiling === 'never'
114
+ && policy.externalWrite === false;
115
+ }
116
+ export function validateCompletedReport(operation, attemptId, reportBytes, reviewContext) {
117
+ const rawText = reportBytes.toString('utf8');
118
+ const repairable = Buffer.from(rawText, 'utf8').equals(reportBytes)
119
+ && !containsCredentialEvidence(rawText);
120
+ try {
121
+ if (!Buffer.from(rawText, 'utf8').equals(reportBytes) || containsCredentialEvidence(rawText)) {
122
+ throw new Error('report payload contains forbidden credential material');
123
+ }
124
+ const decoded = decodeAgentReportForValidation(reportBytes);
125
+ const reviewReport = inputReview(operation, inputReviewContext(reviewContext), decoded);
126
+ const validatedPayload = reviewReport;
127
+ const artifactSha256 = hashCodeReviewReport(reviewReport);
128
+ return {
129
+ status: 'completed',
130
+ attemptId,
131
+ validatedPayload,
132
+ artifactSha256,
133
+ };
470
134
  }
471
- if (input.operation === 'acceptance-proof') {
472
- return policy.sandboxMode === 'workspace-write' && policy.cwdClass === 'worktree'
473
- && policy.worktreeAccess === 'write' && canonicalJson(policy.writableRootClasses) === canonicalJson(['worktree'])
474
- && policy.runnerPostcondition === 'proof-only';
135
+ catch (error) {
136
+ return {
137
+ status: 'invalid',
138
+ attemptId,
139
+ findings: [safeFinding(error)],
140
+ ...(repairable ? { repairInput: { originalReportSha256: sha256(reportBytes), originalReportBytes: Buffer.from(reportBytes) } } : {}),
141
+ };
475
142
  }
476
- return policy.sandboxMode === 'read-only' && policy.cwdClass === 'worktree'
477
- && policy.worktreeAccess === 'read-only' && Array.isArray(policy.writableRootClasses) && policy.writableRootClasses.length === 0
478
- && policy.runnerPostcondition === 'report-only';
479
143
  }
480
- function hasExactMutableAuthority(policy) {
481
- return hasExactKeys(policy, POLICY_KEYS) && policy.sandboxMode === 'workspace-write' && policy.cwdClass === 'worktree'
482
- && policy.worktreeAccess === 'write' && canonicalJson(policy.writableRootClasses) === canonicalJson(['worktree'])
483
- && policy.runnerPostcondition === 'change-set' && policy.network === 'deny' && policy.networkHosts.length === 0
484
- && policy.mcpTools.length === 0 && policy.approvalCeiling === 'never' && policy.externalWrite === false;
144
+ function isImplementationReview(operation) {
145
+ return operation === 'code-review';
485
146
  }
486
- function workerOperation(operation) {
487
- return operation === 'qualification-repair' ? operation : 'implementation';
147
+ function inputReviewContext(context) {
148
+ if (!context)
149
+ throw new Error('implementation review validation context is missing');
150
+ return context;
488
151
  }
489
- function completedMutable(invocation, reportBytes) {
490
- return { status: 'completed', attemptId: invocation.attemptId, reportBytes: Buffer.from(reportBytes) };
152
+ function inputReview(operation, context, decoded) {
153
+ if (context.operation !== operation)
154
+ throw new Error('implementation review operation context mismatch');
155
+ return validateCodeReviewReport(decoded, context);
491
156
  }
492
- function requireReportSnapshot(value) {
493
- const keys = ['headSha', 'indexTreeSha', 'trackedContentSha256', 'untrackedContentSha256', 'worktreeIdentity'];
494
- if (!hasExactKeys(value, keys) || !keys.every((key) => nonEmpty(value[key])))
495
- throw new Error('report snapshot is invalid');
496
- return structuredClone(value);
157
+ function safeFinding(error) {
158
+ return error instanceof Error && error.message.length > 0
159
+ ? error.message.slice(0, MAX_STRING_LENGTH)
160
+ : 'report payload validation failed';
497
161
  }
498
- function blocked(kind, code) {
499
- return { status: 'blocked', kind, code };
162
+ function sameSnapshot(left, right) {
163
+ try {
164
+ return canonicalJson(left) === canonicalJson(right);
165
+ }
166
+ catch {
167
+ return false;
168
+ }
500
169
  }
501
170
  function hasExactKeys(value, keys) {
502
171
  if (typeof value !== 'object' || value === null || Array.isArray(value))
@@ -505,10 +174,14 @@ function hasExactKeys(value, keys) {
505
174
  const expected = [...keys].sort();
506
175
  return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
507
176
  }
508
- function nonEmpty(value) { return typeof value === 'string' && value.length > 0; }
509
- function positive(value) { return Number.isSafeInteger(value) && value > 0; }
510
- function timestamp(value) { return typeof value === 'string' && !Number.isNaN(Date.parse(value)) && new Date(value).toISOString() === value; }
511
- function promptFactsSha256(promptFacts) { return sha256(canonicalJson(promptFacts)); }
512
- class ReportAuthorityError extends Error {
177
+ function requireReportSnapshot(value) {
178
+ const keys = ['headSha', 'indexTreeSha', 'trackedContentSha256', 'untrackedContentSha256', 'worktreeIdentity'];
179
+ if (!hasExactKeys(value, keys))
180
+ throw new Error('report-operation snapshot is invalid');
181
+ for (const key of keys) {
182
+ if (typeof value[key] !== 'string')
183
+ throw new Error('report-operation snapshot is invalid');
184
+ }
185
+ return value;
513
186
  }
514
187
  //# sourceMappingURL=contained-report-operation.js.map