@aiwg/cli 2026.9.19 → 2026.9.21

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 (272) hide show
  1. package/agentic/code/providers/capability-matrix.yaml +58 -6
  2. package/agentic/code/providers/model-capabilities.v1.json +34 -0
  3. package/agentic/code/providers/model-catalog.v1.json +55 -13
  4. package/bin/aiwg.mjs +2 -0
  5. package/dist/src/api/index.d.ts +1 -0
  6. package/dist/src/api/index.js +1 -0
  7. package/dist/src/artifacts/enrichment/cli.js +1 -1
  8. package/dist/src/artifacts/views/cli.js +1 -1
  9. package/dist/src/audit/operator-decision.js +10 -0
  10. package/dist/src/auth/credential-store.js +127 -27
  11. package/dist/src/catalog/builtin-models.json +272 -40
  12. package/dist/src/catalog/cli.js +1 -1
  13. package/dist/src/catalog/cli.mjs +1 -1
  14. package/dist/src/cli/handlers/effect.js +872 -0
  15. package/dist/src/cli/handlers/help.js +2 -0
  16. package/dist/src/cli/handlers/index.js +4 -2
  17. package/dist/src/cli/handlers/models.js +41 -10
  18. package/dist/src/cli/handlers/ralph.js +1 -1
  19. package/dist/src/cli/handlers/sessions.js +113 -37
  20. package/dist/src/cli/handlers/subcommands.js +31 -0
  21. package/dist/src/cli/handlers/use.js +46 -9
  22. package/dist/src/cli/handlers/utilities.js +10 -0
  23. package/dist/src/cli/handlers/workspace.js +3 -1
  24. package/dist/src/cli/scope-resolver.js +112 -9
  25. package/dist/src/cli/services/deployment-verification.js +5 -0
  26. package/dist/src/config/aiwg-config.js +3 -0
  27. package/dist/src/decision/acceptance-rollout.js +93 -0
  28. package/dist/src/decision/acceptance.js +175 -0
  29. package/dist/src/decision/adapters/jev.js +234 -13
  30. package/dist/src/decision/adapters/llm-subagent.js +60 -12
  31. package/dist/src/decision/admission.js +550 -0
  32. package/dist/src/decision/batch-benchmark.js +47 -0
  33. package/dist/src/decision/batch-receipts/accounting.js +75 -0
  34. package/dist/src/decision/batch-receipts/export.js +40 -0
  35. package/dist/src/decision/batch-receipts/index.js +9 -0
  36. package/dist/src/decision/batch-receipts/protection.js +40 -0
  37. package/dist/src/decision/batch-receipts/receipt.js +44 -0
  38. package/dist/src/decision/batch-receipts/result-store.js +341 -0
  39. package/dist/src/decision/batch-receipts/store.js +362 -0
  40. package/dist/src/decision/batch-receipts/types.js +2 -0
  41. package/dist/src/decision/batch-receipts/validate.js +127 -0
  42. package/dist/src/decision/batch.js +68 -0
  43. package/dist/src/decision/calibration/index.js +4 -0
  44. package/dist/src/decision/calibration/receipts.js +130 -0
  45. package/dist/src/decision/calibration/registry.js +258 -0
  46. package/dist/src/decision/calibration/types.js +2 -0
  47. package/dist/src/decision/compile-cache/benchmark.js +62 -0
  48. package/dist/src/decision/compile-cache/compatibility.js +65 -0
  49. package/dist/src/decision/compile-cache/file-store.js +280 -0
  50. package/dist/src/decision/compile-cache/identity.js +26 -0
  51. package/dist/src/decision/compile-cache/index.js +9 -0
  52. package/dist/src/decision/compile-cache/prefix.js +21 -0
  53. package/dist/src/decision/compile-cache/records.js +86 -0
  54. package/dist/src/decision/compile-cache/runtime.js +68 -0
  55. package/dist/src/decision/compile-cache/store.js +108 -0
  56. package/dist/src/decision/compile-cache/types.js +2 -0
  57. package/dist/src/decision/context-plan.js +234 -0
  58. package/dist/src/decision/context-qualification.js +60 -0
  59. package/dist/src/decision/ensemble/aggregate.js +215 -0
  60. package/dist/src/decision/ensemble/contract.js +316 -0
  61. package/dist/src/decision/ensemble/drift.js +51 -0
  62. package/dist/src/decision/ensemble/index.js +6 -0
  63. package/dist/src/decision/ensemble/report.js +71 -0
  64. package/dist/src/decision/ensemble/types.js +2 -0
  65. package/dist/src/decision/evaluate.js +1024 -78
  66. package/dist/src/decision/file-lifecycle-store.js +168 -0
  67. package/dist/src/decision/graph-beam.js +40 -0
  68. package/dist/src/decision/graph-budget.js +110 -0
  69. package/dist/src/decision/graph-decision-bridge.js +93 -0
  70. package/dist/src/decision/graph-evidence.js +123 -0
  71. package/dist/src/decision/graph-flow-adapter.js +56 -0
  72. package/dist/src/decision/graph-flow.js +105 -0
  73. package/dist/src/decision/graph-parallel.js +79 -0
  74. package/dist/src/decision/graph-patterns.js +20 -0
  75. package/dist/src/decision/graph-run-store.js +66 -0
  76. package/dist/src/decision/graph-run.js +166 -0
  77. package/dist/src/decision/graph-skill-bridge.js +91 -0
  78. package/dist/src/decision/graph-templates.js +56 -0
  79. package/dist/src/decision/graph.js +115 -0
  80. package/dist/src/decision/index.js +35 -0
  81. package/dist/src/decision/job-accounting.js +93 -0
  82. package/dist/src/decision/job-contract.js +186 -0
  83. package/dist/src/decision/job-effects.js +95 -0
  84. package/dist/src/decision/job-evaluate.js +80 -0
  85. package/dist/src/decision/job-gateway.js +121 -0
  86. package/dist/src/decision/job-http.js +194 -0
  87. package/dist/src/decision/job-payload-store.js +258 -0
  88. package/dist/src/decision/job-poll-limiter.js +92 -0
  89. package/dist/src/decision/job-quota.js +158 -0
  90. package/dist/src/decision/job-runtime.js +240 -0
  91. package/dist/src/decision/job-scheduler.js +26 -0
  92. package/dist/src/decision/job-service.js +117 -0
  93. package/dist/src/decision/job-store.js +264 -0
  94. package/dist/src/decision/job-worker.js +141 -0
  95. package/dist/src/decision/lifecycle.js +148 -0
  96. package/dist/src/decision/patterns/artifacts.js +173 -0
  97. package/dist/src/decision/patterns/catalog.js +108 -0
  98. package/dist/src/decision/patterns/durable-review.js +237 -0
  99. package/dist/src/decision/patterns/index.js +8 -0
  100. package/dist/src/decision/patterns/playground.js +401 -0
  101. package/dist/src/decision/patterns/recorded.js +43 -0
  102. package/dist/src/decision/patterns/specs.js +174 -0
  103. package/dist/src/decision/patterns/types.js +2 -0
  104. package/dist/src/decision/portable-secrets.js +53 -0
  105. package/dist/src/decision/projection.js +210 -0
  106. package/dist/src/decision/qualification/capture.js +67 -0
  107. package/dist/src/decision/qualification/drift.js +68 -0
  108. package/dist/src/decision/qualification/gate-evidence.js +172 -0
  109. package/dist/src/decision/qualification/gates.js +83 -0
  110. package/dist/src/decision/qualification/index.js +12 -0
  111. package/dist/src/decision/qualification/load.js +275 -0
  112. package/dist/src/decision/qualification/manifest.js +37 -0
  113. package/dist/src/decision/qualification/privacy.js +41 -0
  114. package/dist/src/decision/qualification/quality.js +213 -0
  115. package/dist/src/decision/qualification/release.js +116 -0
  116. package/dist/src/decision/qualification/runner.js +408 -0
  117. package/dist/src/decision/qualification/types.js +2 -0
  118. package/dist/src/decision/receipts.js +30 -7
  119. package/dist/src/decision/result-cache/index.js +6 -0
  120. package/dist/src/decision/result-cache/integrity.js +28 -0
  121. package/dist/src/decision/result-cache/key.js +28 -0
  122. package/dist/src/decision/result-cache/service.js +92 -0
  123. package/dist/src/decision/result-cache/store.js +327 -0
  124. package/dist/src/decision/result-cache/types.js +3 -0
  125. package/dist/src/decision/review/authorization.js +110 -0
  126. package/dist/src/decision/review/effect-ledger-adapter.js +321 -0
  127. package/dist/src/decision/review/index.js +12 -0
  128. package/dist/src/decision/review/migrate.js +34 -0
  129. package/dist/src/decision/review/operator-audit.js +59 -0
  130. package/dist/src/decision/review/recovery-adapters.js +252 -0
  131. package/dist/src/decision/review/recovery.js +33 -0
  132. package/dist/src/decision/review/service.js +498 -0
  133. package/dist/src/decision/review/store.js +219 -0
  134. package/dist/src/decision/review/types.js +2 -0
  135. package/dist/src/decision/review/validate.js +205 -0
  136. package/dist/src/decision/scheduler.js +115 -0
  137. package/dist/src/decision/telemetry/context.js +42 -0
  138. package/dist/src/decision/telemetry/debug-sidecar.js +152 -0
  139. package/dist/src/decision/telemetry/exporter.js +77 -0
  140. package/dist/src/decision/telemetry/file-debug-backend.js +112 -0
  141. package/dist/src/decision/telemetry/index.js +13 -0
  142. package/dist/src/decision/telemetry/mapping.js +146 -0
  143. package/dist/src/decision/telemetry/metrics.js +164 -0
  144. package/dist/src/decision/telemetry/otlp-http.js +183 -0
  145. package/dist/src/decision/telemetry/redaction.js +93 -0
  146. package/dist/src/decision/telemetry/retention.js +109 -0
  147. package/dist/src/decision/telemetry/runtime.js +310 -0
  148. package/dist/src/decision/telemetry/trace.js +158 -0
  149. package/dist/src/decision/telemetry/types.js +3 -0
  150. package/dist/src/decision/validate.js +135 -5
  151. package/dist/src/effects/checkpoint-sinks.js +94 -0
  152. package/dist/src/effects/checkpoint.js +76 -0
  153. package/dist/src/effects/decision-links.js +54 -0
  154. package/dist/src/effects/errors.js +48 -0
  155. package/dist/src/effects/identity.js +178 -0
  156. package/dist/src/effects/index.js +25 -0
  157. package/dist/src/effects/keyring.js +160 -0
  158. package/dist/src/effects/keys.js +130 -0
  159. package/dist/src/effects/ledger.js +435 -0
  160. package/dist/src/effects/lifecycle.js +110 -0
  161. package/dist/src/effects/lock-recovery.js +269 -0
  162. package/dist/src/effects/reader.js +103 -0
  163. package/dist/src/effects/records.js +114 -0
  164. package/dist/src/effects/redaction.js +51 -0
  165. package/dist/src/effects/schema.js +73 -0
  166. package/dist/src/effects/store.js +239 -0
  167. package/dist/src/effects/types.js +49 -0
  168. package/dist/src/effects/verifiers/decision.js +168 -0
  169. package/dist/src/effects/verifiers/file.js +106 -0
  170. package/dist/src/effects/verifiers/git.js +206 -0
  171. package/dist/src/effects/verifiers/index.js +90 -0
  172. package/dist/src/effects/verifiers/registry.js +148 -0
  173. package/dist/src/effects/verifiers/review.js +123 -0
  174. package/dist/src/effects/verifiers/tracker.js +299 -0
  175. package/dist/src/effects/verifiers/types.js +21 -0
  176. package/dist/src/effects/verify.js +295 -0
  177. package/dist/src/evidence/bundle.js +2 -0
  178. package/dist/src/extensions/commands/definitions.js +28 -0
  179. package/dist/src/extensions/manifest.js +1 -0
  180. package/dist/src/marketplace/provenance.js +25 -27
  181. package/dist/src/mcp/cli.mjs +11 -1
  182. package/dist/src/mcp/grok-build-config.mjs +189 -0
  183. package/dist/src/mcp/registry.js +21 -1
  184. package/dist/src/mcp/registry.mjs +20 -1
  185. package/dist/src/models/grok-build-models.js +356 -0
  186. package/dist/src/models/index.js +1 -0
  187. package/dist/src/models/model-capabilities.v1.json +34 -0
  188. package/dist/src/models/model-catalog.v1.json +55 -13
  189. package/dist/src/models/model-discovery.js +41 -5
  190. package/dist/src/models/provider-policy.js +3 -2
  191. package/dist/src/plugin/skill-command-translator.js +1 -0
  192. package/dist/src/providers/capability-matrix.yaml +58 -6
  193. package/dist/src/providers/grok-build-acp.js +192 -0
  194. package/dist/src/providers/grok-build-dispatch.js +251 -0
  195. package/dist/src/providers/grok-build-headless.js +170 -0
  196. package/dist/src/providers/muse-paths.js +105 -0
  197. package/dist/src/providers/provider-definitions.js +93 -7
  198. package/dist/src/providers/provider-definitions.mjs +21 -1
  199. package/dist/src/providers/provider-inventory.js +3 -0
  200. package/dist/src/security/artifact-attestation.js +4 -8
  201. package/dist/src/security/signing.js +119 -0
  202. package/dist/src/sessions/adapters/grok-build.js +187 -0
  203. package/dist/src/sessions/adapters/muse.js +412 -0
  204. package/dist/src/sessions/contracts.js +1 -1
  205. package/dist/src/sessions/grok-build-cli.js +196 -0
  206. package/dist/src/sessions/index.js +3 -0
  207. package/dist/src/sessions/workspace-discovery.js +9 -5
  208. package/dist/src/skills/deployer.js +33 -5
  209. package/dist/src/smiths/context-pipeline/claude-hook.js +2 -0
  210. package/dist/src/smiths/context-pipeline/context-file-safety.js +18 -0
  211. package/dist/src/smiths/context-pipeline/finalization.js +1 -32
  212. package/dist/src/smiths/context-pipeline/generator.js +12 -0
  213. package/dist/src/smiths/context-pipeline/index.js +1 -1
  214. package/dist/src/smiths/context-pipeline/managed-hook.js +2 -0
  215. package/dist/src/smiths/context-pipeline/muse-bridge.js +50 -0
  216. package/dist/src/smiths/context-pipeline/provider-policy.js +5 -0
  217. package/dist/src/smiths/context-pipeline/workspace-context.js +22 -16
  218. package/dist/src/smiths/hook-bridge/grok-build-translator.js +64 -0
  219. package/dist/src/smiths/hook-bridge/index.js +3 -0
  220. package/dist/src/smiths/hook-bridge/shim.js +2 -0
  221. package/dist/src/smiths/hook-bridge/types.js +5 -0
  222. package/dist/src/smiths/skillsmith/collision-detector.js +1 -0
  223. package/dist/src/storage/protected-files.js +102 -0
  224. package/dist/src/tracker/forge-http.js +371 -0
  225. package/dist/src/tracker/project-inputs.js +41 -0
  226. package/package.json +2 -1
  227. package/schemas/decision/BatchReceipt.v1.schema.json +50 -0
  228. package/schemas/decision/CalibrationArtifact.v1.schema.json +76 -0
  229. package/schemas/decision/CalibrationCompatibility.v1.schema.json +18 -0
  230. package/schemas/decision/CalibrationGovernanceReceipt.v1.schema.json +44 -0
  231. package/schemas/decision/DecisionAdmissionEvidence.v1.schema.json +43 -0
  232. package/schemas/decision/DecisionBinding.schema.json +309 -0
  233. package/schemas/decision/DecisionBinding.v1alpha2.schema.json +369 -0
  234. package/schemas/decision/DecisionCacheTelemetry.v1.schema.json +20 -0
  235. package/schemas/decision/DecisionChampionChallenger.v1.schema.json +100 -0
  236. package/schemas/decision/DecisionCompileCacheEntry.v1.schema.json +35 -0
  237. package/schemas/decision/DecisionCompileCacheIdentity.v1.schema.json +26 -0
  238. package/schemas/decision/DecisionCompileCacheTombstone.v1.schema.json +31 -0
  239. package/schemas/decision/DecisionContextEvidence.v1.schema.json +136 -0
  240. package/schemas/decision/DecisionDefinition.schema.json +159 -0
  241. package/schemas/decision/DecisionDefinition.v1alpha2.schema.json +203 -0
  242. package/schemas/decision/DecisionDriftResponse.v1.schema.json +55 -0
  243. package/schemas/decision/DecisionEnsembleAggregate.v1.schema.json +86 -0
  244. package/schemas/decision/DecisionEnsembleIntegrityReport.v1.schema.json +64 -0
  245. package/schemas/decision/DecisionEnsemblePolicy.v1.schema.json +130 -0
  246. package/schemas/decision/DecisionGraph.v1.schema.json +77 -0
  247. package/schemas/decision/DecisionJob.v1.schema.json +71 -0
  248. package/schemas/decision/DecisionLifecyclePolicy.v1.schema.json +52 -0
  249. package/schemas/decision/DecisionProjectionPolicy.v1.schema.json +65 -0
  250. package/schemas/decision/DecisionProviderPrefixCompatibility.v1.schema.json +45 -0
  251. package/schemas/decision/DecisionProviderPrefixEvidence.v1.schema.json +17 -0
  252. package/schemas/decision/DecisionProviderPrefixIdentity.v1.schema.json +36 -0
  253. package/schemas/decision/DecisionResult.schema.json +524 -0
  254. package/schemas/decision/DecisionResult.v1alpha2.schema.json +619 -0
  255. package/schemas/decision/DecisionResultCacheEntry.v1.schema.json +53 -0
  256. package/schemas/decision/DecisionReview.schema.json +29 -0
  257. package/schemas/decision/DecisionRuleset.schema.json +313 -0
  258. package/schemas/decision/DecisionRuleset.v1alpha2.schema.json +313 -0
  259. package/schemas/decision/DecisionTelemetryTrace.v1.schema.json +95 -0
  260. package/schemas/decision/RulesetResult.schema.json +721 -0
  261. package/schemas/decision/RulesetResult.v1alpha2.schema.json +891 -0
  262. package/schemas/security/pr-evidence-receipt.v1.schema.json +77 -0
  263. package/schemas/security/threat-assessment-report.v1.schema.json +71 -1
  264. package/tools/agents/deploy-agents.mjs +21 -6
  265. package/tools/agents/providers/base.mjs +7 -4
  266. package/tools/agents/providers/codex.mjs +1 -1
  267. package/tools/agents/providers/grok-build.d.mts +7 -0
  268. package/tools/agents/providers/grok-build.mjs +249 -9
  269. package/tools/agents/providers/model-role.mjs +6 -3
  270. package/tools/agents/providers/muse-hooks.mjs +785 -0
  271. package/tools/agents/providers/muse-paths.mjs +112 -0
  272. package/tools/agents/providers/muse.mjs +336 -0
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Built-in `git.commit` and `git.tag` verifiers over a local repository.
3
+ *
4
+ * - Object lookups go through `git cat-file --batch-check` on stdin, so a
5
+ * target is never parsed as a command-line option and "missing" is a
6
+ * complete, successful answer rather than an error exit.
7
+ * - `absent` needs a complete repository: a shallow or partial clone gives
8
+ * `unknown` / `paging-incomplete` for a missing commit.
9
+ * - Signature checks reuse `git verify-commit` / `git verify-tag` (as
10
+ * `tools/ci/verify-signed-tag.sh` does). A required signature that is not
11
+ * good is never `present`: it is `unknown` / `evidence-conflict`, or
12
+ * `unknown` / `container-unreadable` when it cannot be checked (no key).
13
+ *
14
+ * @see docs/contracts/effect-ledger.v1.md "Built-in verifiers"
15
+ */
16
+ import { spawn } from 'node:child_process';
17
+ import { EffectVerifierError } from './types.js';
18
+ export const GIT_VERIFIER_VERSION = '1.0.0';
19
+ const OBJECT_ID = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/;
20
+ const TAG_NAME = /^[A-Za-z0-9][A-Za-z0-9._+/-]{0,254}$/;
21
+ const MAX_OUTPUT = 4 * 1024 * 1024;
22
+ function spawnRunner(repoDir, binary) {
23
+ return (args, options = {}) => new Promise((resolve, reject) => {
24
+ const child = spawn(binary, ['-C', repoDir, ...args], {
25
+ stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true, signal: options.signal,
26
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0', GIT_OPTIONAL_LOCKS: '0', LC_ALL: 'C' },
27
+ });
28
+ const out = [];
29
+ const err = [];
30
+ let size = 0;
31
+ const collect = (sink) => (chunk) => {
32
+ size += chunk.length;
33
+ if (size > MAX_OUTPUT)
34
+ child.kill();
35
+ else
36
+ sink.push(chunk);
37
+ };
38
+ child.stdout.on('data', collect(out));
39
+ child.stderr.on('data', collect(err));
40
+ // A git binary that cannot start is an unreadable container; an abort is the framework timeout.
41
+ child.once('error', error => reject(options.signal?.aborted ? error : new EffectVerifierError('container-unreadable')));
42
+ child.stdin.once('error', () => { });
43
+ child.once('close', code => {
44
+ if (size > MAX_OUTPUT)
45
+ reject(new EffectVerifierError('malformed-response'));
46
+ else
47
+ resolve({ stdout: Buffer.concat(out).toString('utf8'), stderr: Buffer.concat(err).toString('utf8'), exitCode: code ?? 1 });
48
+ });
49
+ child.stdin.end(options.stdin ?? '');
50
+ });
51
+ }
52
+ const unknown = (reason, evidence) => ({ result: 'unknown', reason, complete: false, ...(evidence ? { evidence } : {}) });
53
+ /** Read an expectation member from `expected`, falling back to the identity context. */
54
+ function expectation(request, name) {
55
+ return request.expected[name] ?? request.context[name];
56
+ }
57
+ function gitFor(options) {
58
+ if (!options || typeof options.repoDir !== 'string' || !options.repoDir)
59
+ throw new TypeError('git verifiers need repoDir');
60
+ const run = options.runner ?? spawnRunner(options.repoDir, options.gitBinary ?? 'git');
61
+ return {
62
+ run,
63
+ async assertReadable(signal) {
64
+ const result = await run(['rev-parse', '--git-dir'], { signal });
65
+ if (result.exitCode !== 0)
66
+ throw new EffectVerifierError('container-unreadable');
67
+ },
68
+ async lookup(name, signal) {
69
+ const result = await run(['cat-file', '--batch-check=%(objectname) %(objecttype)'], { stdin: `${name}\n`, signal });
70
+ if (result.exitCode !== 0)
71
+ throw new EffectVerifierError('container-unreadable');
72
+ const line = result.stdout.trim();
73
+ if (line === `${name} missing`)
74
+ return null;
75
+ if (line === `${name} ambiguous`)
76
+ throw new EffectVerifierError('evidence-conflict');
77
+ const match = /^([a-f0-9]{40}|[a-f0-9]{64}) ([a-z]+)$/.exec(line);
78
+ if (!match)
79
+ throw new EffectVerifierError('malformed-response');
80
+ return { oid: match[1], type: match[2] };
81
+ },
82
+ async complete(signal) {
83
+ const shallow = await run(['rev-parse', '--is-shallow-repository'], { signal });
84
+ if (shallow.exitCode !== 0 || !/^(?:true|false)$/.test(shallow.stdout.trim()))
85
+ throw new EffectVerifierError('container-unreadable');
86
+ if (shallow.stdout.trim() === 'true')
87
+ return false;
88
+ const partial = await run(['config', '--get', 'extensions.partialclone'], { signal });
89
+ if (partial.exitCode === 0 && partial.stdout.trim())
90
+ return false;
91
+ if (partial.exitCode !== 0 && partial.exitCode !== 1)
92
+ throw new EffectVerifierError('container-unreadable');
93
+ return true;
94
+ },
95
+ };
96
+ }
97
+ /** Classify a failed `verify-commit` / `verify-tag --raw` from its status output. */
98
+ function failedSignature(stderr) {
99
+ if (/\b(?:REVKEYSIG)\b|revoked/i.test(stderr))
100
+ return 'revoked';
101
+ if (/\b(?:EXPKEYSIG|EXPSIG)\b|expired/i.test(stderr))
102
+ return 'expired';
103
+ if (/\bBADSIG\b|bad signature/i.test(stderr))
104
+ return 'bad';
105
+ if (/\b(?:NO_PUBKEY|ERRSIG)\b|allowedSignersFile|No principal matched|no public key|can't check signature|gpg failed|cannot run/i.test(stderr))
106
+ return 'unverifiable';
107
+ return 'bad';
108
+ }
109
+ async function signatureStatus(git, command, oid, hasSignature, signal) {
110
+ if (!hasSignature)
111
+ return 'unsigned';
112
+ const result = await git.run([command, '--raw', oid], { signal });
113
+ return result.exitCode === 0 ? 'good' : failedSignature(result.stderr);
114
+ }
115
+ /** A required signature that is not good is never `present`. */
116
+ function signatureBlocks(status) {
117
+ if (status === 'good' || status === 'unchecked')
118
+ return null;
119
+ return status === 'unverifiable' ? 'container-unreadable' : 'evidence-conflict';
120
+ }
121
+ /** `git.commit`: target `git:<sha>` (full 40 or 64 hex). */
122
+ export function gitCommitVerifier(options) {
123
+ const git = gitFor(options);
124
+ return {
125
+ kind: 'git.commit',
126
+ version: GIT_VERIFIER_VERSION,
127
+ canReportAbsent: true,
128
+ async verify(request) {
129
+ const sha = request.target.startsWith('git:') ? request.target.slice(4) : '';
130
+ if (!OBJECT_ID.test(sha))
131
+ return unknown('malformed-response');
132
+ const signed = expectation(request, 'signed') === true;
133
+ await git.assertReadable(request.signal);
134
+ const object = await git.lookup(sha, request.signal);
135
+ if (!object) {
136
+ if (!await git.complete(request.signal))
137
+ return unknown('paging-incomplete');
138
+ return { result: 'absent', reason: 'complete-query-no-match', complete: true, evidence: { object: sha, found: false } };
139
+ }
140
+ if (object.type !== 'commit' || object.oid !== sha)
141
+ return unknown('evidence-conflict', { object: sha, type: object.type });
142
+ const body = await git.run(['cat-file', 'commit', sha], { signal: request.signal });
143
+ if (body.exitCode !== 0)
144
+ return unknown('container-unreadable');
145
+ const header = body.stdout.split('\n\n', 1)[0] ?? '';
146
+ const hasSignature = /^gpgsig(?:-sha256)? /m.test(header);
147
+ const signature = signed ? await signatureStatus(git, 'verify-commit', sha, hasSignature, request.signal) : 'unchecked';
148
+ const trailers = await git.run(['log', '-1', '--format=%(trailers:key=Effect-Id,valueonly)', sha], { signal: request.signal });
149
+ if (trailers.exitCode !== 0)
150
+ return unknown('container-unreadable');
151
+ const marker = trailers.stdout.split('\n').map(line => line.trim()).includes(request.effectId);
152
+ const evidence = { object: sha, type: 'commit', signature, signatureRequired: signed, marker };
153
+ const blocked = signed ? signatureBlocks(signature) : null;
154
+ if (blocked)
155
+ return unknown(blocked, evidence);
156
+ return { result: 'present', reason: marker ? 'marker-match' : 'state-match', complete: true, evidence };
157
+ },
158
+ };
159
+ }
160
+ /** `git.tag`: target `git-tag:<name>`; the expected peeled object comes from `expected.object` or `context.object`. */
161
+ export function gitTagVerifier(options) {
162
+ const git = gitFor(options);
163
+ return {
164
+ kind: 'git.tag',
165
+ version: GIT_VERIFIER_VERSION,
166
+ canReportAbsent: true,
167
+ async verify(request) {
168
+ const name = request.target.startsWith('git-tag:') ? request.target.slice(8) : '';
169
+ if (!TAG_NAME.test(name) || name.includes('..') || name.includes('//') || name.endsWith('.lock') || name.endsWith('/') || name.endsWith('.')) {
170
+ return unknown('malformed-response');
171
+ }
172
+ const expectedObject = expectation(request, 'object');
173
+ if (expectedObject !== undefined && (typeof expectedObject !== 'string' || !OBJECT_ID.test(expectedObject)))
174
+ return unknown('malformed-response');
175
+ const signed = expectation(request, 'signed') === true;
176
+ await git.assertReadable(request.signal);
177
+ const ref = `refs/tags/${name}`;
178
+ const tag = await git.lookup(ref, request.signal);
179
+ if (!tag)
180
+ return { result: 'absent', reason: 'complete-query-no-match', complete: true, evidence: { tag: name, found: false } };
181
+ const peeled = await git.lookup(`${ref}^{}`, request.signal);
182
+ if (!peeled)
183
+ return unknown('evidence-conflict');
184
+ let hasSignature = false;
185
+ if (tag.type === 'tag') {
186
+ const body = await git.run(['cat-file', 'tag', tag.oid], { signal: request.signal });
187
+ if (body.exitCode !== 0)
188
+ return unknown('container-unreadable');
189
+ hasSignature = /-----BEGIN (?:PGP|SSH) SIGNATURE-----/.test(body.stdout);
190
+ }
191
+ const signature = signed ? await signatureStatus(git, 'verify-tag', tag.oid, hasSignature, request.signal) : 'unchecked';
192
+ const evidence = {
193
+ tag: name, tagObject: tag.oid, annotated: tag.type === 'tag', object: peeled.oid,
194
+ expectedObject: typeof expectedObject === 'string' ? expectedObject : null, signature, signatureRequired: signed,
195
+ };
196
+ // The tag name exists but points elsewhere: a replay would collide, so never `absent`.
197
+ if (typeof expectedObject === 'string' && peeled.oid !== expectedObject && tag.oid !== expectedObject)
198
+ return unknown('evidence-conflict', evidence);
199
+ const blocked = signed ? signatureBlocks(signature) : null;
200
+ if (blocked)
201
+ return unknown(blocked, evidence);
202
+ return { result: 'present', reason: 'state-match', complete: true, evidence };
203
+ },
204
+ };
205
+ }
206
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Effect verifiers (#2718, epic #2714): the tri-state verifier interface, the
3
+ * registry, and the built-in verifiers.
4
+ *
5
+ * ## Interface
6
+ *
7
+ * A verifier is a plain object:
8
+ *
9
+ * ```ts
10
+ * interface EffectVerifier {
11
+ * readonly kind: string; // core kind or x.<vendor>.<name>
12
+ * readonly version: string; // semver, recorded in every verification
13
+ * readonly canReportAbsent: boolean; // false: any `absent` becomes `unknown`
14
+ * verify(request: EffectVerifierRequest): Promise<EffectVerifierObservation>;
15
+ * }
16
+ * ```
17
+ *
18
+ * `verify` receives the intent's identity (`effectId`, `scope`, `kind`,
19
+ * `target`, `context`, `payloadDigest`, `intentRecordedAt`), the caller's
20
+ * `expected` digests and references, and an `AbortSignal` that fires at the
21
+ * framework timeout. It answers `{result, reason, complete, evidence?}` with a
22
+ * reason code from the contract table. Classified failures either return
23
+ * `unknown` or throw `EffectVerifierError(reason)`.
24
+ *
25
+ * The framework (`runVerifier`, used by `reconcileEffect`) never lets a
26
+ * verifier bypass the rules: a missing verifier, a kind or pinned-version
27
+ * mismatch, any throw, a timeout, a malformed answer, a reason that does not
28
+ * belong to the result, `absent` from a verifier that cannot report it, and
29
+ * `absent` without `complete: true` all become `unknown`. Evidence is digested
30
+ * into `evidenceDigest`; only the digest reaches the ledger.
31
+ *
32
+ * ## Registering
33
+ *
34
+ * ```ts
35
+ * const registry = createBuiltinVerifierRegistry(
36
+ * { git: { repoDir }, file: { root }, decision: { receipts, jobs } },
37
+ * [...createTrackerVerifiers({ config, remoteUrls }), myVendorVerifier], // extensions
38
+ * );
39
+ * await reconcileEffect(ledger, id, { verifiers: registry, expected: { signed: true } });
40
+ * ```
41
+ *
42
+ * ## Extension point: `x.<vendor>.<name>`
43
+ *
44
+ * A vendor verifier uses a kind matching
45
+ * `^x\.[a-z0-9][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)+$` and a target in the
46
+ * `x-<vendor>:<ref>` scheme, and is passed in the extension list (or to
47
+ * `createVerifierRegistry`). It follows exactly the same interface and rules;
48
+ * it cannot shadow a registered kind, and adding a new core kind is a contract
49
+ * revision.
50
+ *
51
+ * @see docs/contracts/effect-ledger.v1.md "Verifier interface"
52
+ */
53
+ import { createVerifierRegistry } from './registry.js';
54
+ import { decisionReceiptVerifier } from './decision.js';
55
+ import { FILE_DIGEST_VERIFIER_VERSION, fileDigestVerifier } from './file.js';
56
+ import { GIT_VERIFIER_VERSION, gitCommitVerifier, gitTagVerifier } from './git.js';
57
+ import { REVIEW_CONTINUATION_VERIFIER_VERSION, reviewContinuationVerifier } from './review.js';
58
+ import { EffectVerifierError } from './types.js';
59
+ export * from './types.js';
60
+ export { DEFAULT_VERIFIER_TIMEOUT_MS, createVerifierRegistry, evidenceDigest, runVerifier, } from './registry.js';
61
+ export { GIT_VERIFIER_VERSION, gitCommitVerifier, gitTagVerifier, } from './git.js';
62
+ export { FILE_DIGEST_VERIFIER_VERSION, fileDigestVerifier } from './file.js';
63
+ export { DECISION_RECEIPT_VERIFIER_VERSION, REVIEW_CONTINUATION_PLACEHOLDER_VERSION, decisionReceiptVerifier, reviewContinuationPlaceholderVerifier, } from './decision.js';
64
+ export { REVIEW_CONTINUATION_VERIFIER_VERSION, reviewContinuationTarget, reviewContinuationVerifier, } from './review.js';
65
+ export { DEFAULT_MAX_COMMENT_PAGES, DEFAULT_MIN_ABSENT_AGE_MS, TRACKER_VERIFIER_VERSION, createTrackerVerifiers, parseEffectIdTrailers, parseEffectMarkers, renderEffectMarker, trackerCommentVerifier, trackerIssueClosedVerifier, trackerPrMergedVerifier, } from './tracker.js';
66
+ /** Stand-in for a built-in whose container is not configured on this host. */
67
+ function unconfigured(kind, version) {
68
+ return {
69
+ kind, version, canReportAbsent: true,
70
+ async verify() { throw new EffectVerifierError('container-unreadable', 'Effect verifier is not configured on this host'); },
71
+ };
72
+ }
73
+ /**
74
+ * The five built-in verifiers: `git.commit`, `git.tag`, `file.digest`,
75
+ * `decision.receipt` and `decision.review.continuation`.
76
+ */
77
+ export function createBuiltinVerifiers(options = {}) {
78
+ return [
79
+ options.git ? gitCommitVerifier(options.git) : unconfigured('git.commit', GIT_VERIFIER_VERSION),
80
+ options.git ? gitTagVerifier(options.git) : unconfigured('git.tag', GIT_VERIFIER_VERSION),
81
+ options.file ? fileDigestVerifier(options.file) : unconfigured('file.digest', FILE_DIGEST_VERIFIER_VERSION),
82
+ decisionReceiptVerifier(options.decision ?? {}),
83
+ options.review ? reviewContinuationVerifier(options.review) : unconfigured('decision.review.continuation', REVIEW_CONTINUATION_VERIFIER_VERSION),
84
+ ];
85
+ }
86
+ /** A registry of the built-ins plus `extensions` (tracker verifiers, `x.<vendor>.<name>` kinds). Duplicate kinds are refused. */
87
+ export function createBuiltinVerifierRegistry(options = {}, extensions = []) {
88
+ return createVerifierRegistry([...createBuiltinVerifiers(options), ...extensions]);
89
+ }
90
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Verifier registry keyed by kind, and the framework runner that enforces the
3
+ * tri-state rules on every verifier answer.
4
+ *
5
+ * @see docs/contracts/effect-ledger.v1.md "Verifier interface"
6
+ */
7
+ import { canonicalJson } from '../../security/artifact-trust.js';
8
+ import { usageError } from '../errors.js';
9
+ import { isEffectKind, sha256Digest } from '../identity.js';
10
+ import { assertDigestOnly } from '../redaction.js';
11
+ import { ABSENT_REASONS, PRESENT_REASONS, UNKNOWN_REASONS, } from '../types.js';
12
+ import { EffectVerifierError, } from './types.js';
13
+ const SEMVER = /^[0-9]+\.[0-9]+\.[0-9]+$/;
14
+ const DIGEST = /^sha256:[a-f0-9]{64}$/;
15
+ /**
16
+ * Build a registry. Kinds must be core kinds or `x.<vendor>.<name>` extension
17
+ * kinds, versions must be semver, and each kind has exactly one verifier.
18
+ * The default registry is empty: every reconcile is `unknown` / `verifier-missing`.
19
+ */
20
+ export function createVerifierRegistry(verifiers = []) {
21
+ const byKind = new Map();
22
+ for (const verifier of verifiers) {
23
+ if (!verifier || typeof verifier.verify !== 'function' || typeof verifier.canReportAbsent !== 'boolean') {
24
+ throw usageError('A verifier needs kind, version, canReportAbsent and verify', 'invalid-verifier');
25
+ }
26
+ if (!isEffectKind(verifier.kind))
27
+ throw usageError('Verifier kind must be a core kind or x.<vendor>.<name>', 'invalid-verifier');
28
+ if (byKind.has(verifier.kind))
29
+ throw usageError('Only one verifier may be registered per kind', 'duplicate-verifier');
30
+ if (typeof verifier.version !== 'string' || !SEMVER.test(verifier.version))
31
+ throw usageError('Verifier version must be semver', 'invalid-verifier');
32
+ byKind.set(verifier.kind, verifier);
33
+ }
34
+ const kinds = () => [...byKind.keys()].sort();
35
+ return {
36
+ get: kind => byKind.get(kind),
37
+ kinds,
38
+ listKinds: () => kinds().map(kind => refFor(byKind.get(kind))),
39
+ };
40
+ }
41
+ function refFor(verifier) {
42
+ return { kind: verifier.kind, version: verifier.version, canReportAbsent: verifier.canReportAbsent };
43
+ }
44
+ /** Digest of structured evidence: `sha256:` over `canonicalJson(evidence)`. */
45
+ export function evidenceDigest(evidence) {
46
+ return sha256Digest(canonicalJson(evidence));
47
+ }
48
+ export const DEFAULT_VERIFIER_TIMEOUT_MS = 30_000;
49
+ const unknown = (reason) => ({ result: 'unknown', reason, complete: false });
50
+ /** Enforce the tri-state rules: `absent` needs a capable verifier and a complete query; reasons must match the result. */
51
+ function normalize(verifier, observation) {
52
+ const value = observation;
53
+ if (!value || typeof value !== 'object' || typeof value.complete !== 'boolean'
54
+ || (value.evidenceDigest !== undefined && !DIGEST.test(value.evidenceDigest)))
55
+ return { observation: unknown('malformed-response') };
56
+ let evidence;
57
+ let digest = value.evidenceDigest;
58
+ if (value.evidence !== undefined) {
59
+ const candidate = value.evidence;
60
+ const flat = candidate && typeof candidate === 'object' && !Array.isArray(candidate)
61
+ && Object.values(candidate).every(item => item === null || ['string', 'number', 'boolean'].includes(typeof item));
62
+ if (!flat)
63
+ return { observation: unknown('malformed-response') };
64
+ try {
65
+ assertDigestOnly(candidate);
66
+ }
67
+ catch {
68
+ return { observation: unknown('malformed-response') };
69
+ }
70
+ evidence = structuredClone(candidate);
71
+ const computed = evidenceDigest(evidence);
72
+ if (digest !== undefined && digest !== computed)
73
+ return { observation: unknown('malformed-response') };
74
+ digest = computed;
75
+ }
76
+ const withEvidence = (base) => ({
77
+ observation: { ...base, ...(digest ? { evidenceDigest: digest } : {}) },
78
+ ...(evidence ? { evidence } : {}),
79
+ });
80
+ if (value.result === 'present') {
81
+ return PRESENT_REASONS.includes(value.reason)
82
+ ? withEvidence({ result: 'present', reason: value.reason, complete: value.complete })
83
+ : { observation: unknown('malformed-response') };
84
+ }
85
+ if (value.result === 'absent') {
86
+ if (!verifier.canReportAbsent)
87
+ return { observation: unknown('verifier-cannot-report-absent') };
88
+ if (!value.complete)
89
+ return { observation: unknown('paging-incomplete') };
90
+ if (!ABSENT_REASONS.includes(value.reason))
91
+ return { observation: unknown('malformed-response') };
92
+ return withEvidence({ result: 'absent', reason: value.reason, complete: true });
93
+ }
94
+ if (value.result === 'unknown') {
95
+ return UNKNOWN_REASONS.includes(value.reason)
96
+ ? withEvidence({ result: 'unknown', reason: value.reason, complete: value.complete })
97
+ : { observation: unknown('malformed-response') };
98
+ }
99
+ return { observation: unknown('malformed-response') };
100
+ }
101
+ function thrownReason(error, timedOut) {
102
+ if (timedOut)
103
+ return 'timeout';
104
+ if (error instanceof EffectVerifierError && UNKNOWN_REASONS.includes(error.reason))
105
+ return error.reason;
106
+ return 'server-error';
107
+ }
108
+ /**
109
+ * Run one verifier query under the framework rules. Never throws: a missing
110
+ * verifier, a kind or version mismatch, a throw, a timeout or a malformed
111
+ * answer all give `unknown`. `absent` survives only from a verifier with
112
+ * `canReportAbsent: true` reporting `complete: true`.
113
+ */
114
+ export async function runVerifier(verifier, request, options = {}) {
115
+ if (!verifier)
116
+ return { verifier: { kind: request.kind, version: '0.0.0', canReportAbsent: false }, observation: unknown('verifier-missing') };
117
+ const ref = { kind: request.kind, version: SEMVER.test(String(verifier.version)) ? verifier.version : '0.0.0', canReportAbsent: verifier.canReportAbsent === true };
118
+ if (verifier.kind !== request.kind || (options.verifierVersion !== undefined && options.verifierVersion !== verifier.version)) {
119
+ return { verifier: ref, observation: unknown('verifier-version-mismatch') };
120
+ }
121
+ const timeoutMs = options.timeoutMs ?? DEFAULT_VERIFIER_TIMEOUT_MS;
122
+ const controller = new AbortController();
123
+ let timedOut = false;
124
+ let timer;
125
+ const expiry = new Promise((_, reject) => {
126
+ timer = setTimeout(() => {
127
+ timedOut = true;
128
+ controller.abort(new EffectVerifierError('timeout', 'Effect verifier timed out'));
129
+ reject(new EffectVerifierError('timeout'));
130
+ }, timeoutMs);
131
+ });
132
+ try {
133
+ const answer = await Promise.race([
134
+ Promise.resolve().then(() => verifier.verify({ ...structuredClone(request), signal: controller.signal })),
135
+ expiry,
136
+ ]);
137
+ if (timedOut)
138
+ return { verifier: ref, observation: unknown('timeout') };
139
+ return { verifier: ref, ...normalize(verifier, answer) };
140
+ }
141
+ catch (error) {
142
+ return { verifier: ref, observation: unknown(thrownReason(error, timedOut)) };
143
+ }
144
+ finally {
145
+ clearTimeout(timer);
146
+ }
147
+ }
148
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Built-in `decision.review.continuation` verifier over the D13 review store
3
+ * (#2721). It replaces the #2718 placeholder.
4
+ *
5
+ * Target: `review:<tenant>/<project>/<reviewId>`. The context is exactly the
6
+ * `d13.review/v1` identity `{reviewId, continuationId, proposalVersion}`, and
7
+ * the effect ID must equal `reviewDigest(context)`.
8
+ *
9
+ * The review store is the authority for whether the continuation was dispatched
10
+ * and whether its receipt was persisted. While the review holds the
11
+ * continuation without a receipt (the crash window between dispatch and
12
+ * `execution-completed`), the store cannot settle the effect; the verifier then
13
+ * asks the host's `execution` probe, which queries the effect's own target. With
14
+ * no probe the answer is `unknown` / `consistency-lag`.
15
+ *
16
+ * `absent` never authorizes a D13 replay by itself (see the contract).
17
+ *
18
+ * @see docs/contracts/effect-ledger.v1.md "Built-in verifiers"
19
+ */
20
+ import { canonicalJson } from '../../security/artifact-trust.js';
21
+ import { ReviewAccessError, reviewDigest } from '../../decision/review/validate.js';
22
+ import { sha256Digest } from '../identity.js';
23
+ export const REVIEW_CONTINUATION_VERIFIER_VERSION = '1.0.0';
24
+ const DIGEST = /^sha256:[a-f0-9]{64}$/;
25
+ const unknown = (reason, evidence) => ({ result: 'unknown', reason, complete: false, ...(evidence ? { evidence } : {}) });
26
+ /**
27
+ * Parse `review:<tenant>/<project>/<reviewId>` against the effect scope. Tenant
28
+ * and project may themselves contain `/`, so the prefix comes from the scope.
29
+ */
30
+ function targetReviewId(target, scope) {
31
+ const prefix = `review:${scope.tenant}/${scope.project}/`;
32
+ return target.startsWith(prefix) && target.length > prefix.length ? target.slice(prefix.length) : null;
33
+ }
34
+ /** The `decision.review.continuation` target for a review. */
35
+ export function reviewContinuationTarget(tenantId, projectId, reviewId) {
36
+ return `review:${tenantId}/${projectId}/${reviewId}`;
37
+ }
38
+ function receiptDigest(review) {
39
+ try {
40
+ return reviewDigest(review.effectReceipt);
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ async function delegate(options, request, base) {
47
+ if (!options.execution)
48
+ return unknown('consistency-lag', { ...base, execution: 'not-configured' });
49
+ const answer = await options.execution(request);
50
+ if (!answer || typeof answer !== 'object')
51
+ return unknown('malformed-response', base);
52
+ let executionEvidenceDigest = answer.evidenceDigest ?? null;
53
+ if (answer.evidence !== undefined) {
54
+ try {
55
+ executionEvidenceDigest = sha256Digest(canonicalJson(answer.evidence));
56
+ }
57
+ catch {
58
+ return unknown('malformed-response', base);
59
+ }
60
+ }
61
+ // The framework normalizes the delegated result and reason like any other answer.
62
+ return {
63
+ result: answer.result, reason: answer.reason, complete: answer.complete,
64
+ evidence: { ...base, execution: 'probed', executionReason: String(answer.reason), executionEvidenceDigest },
65
+ };
66
+ }
67
+ /** `decision.review.continuation`: the D13 review store, then the host execution probe. */
68
+ export function reviewContinuationVerifier(options) {
69
+ return {
70
+ kind: 'decision.review.continuation',
71
+ version: REVIEW_CONTINUATION_VERIFIER_VERSION,
72
+ canReportAbsent: true,
73
+ async verify(request) {
74
+ const targetId = targetReviewId(request.target, request.scope);
75
+ const { reviewId, continuationId, proposalVersion } = request.context;
76
+ if (request.scope.subsystem !== 'review' || targetId === null || targetId !== reviewId
77
+ || typeof continuationId !== 'string' || typeof proposalVersion !== 'number'
78
+ || request.effectId !== reviewDigest({ reviewId, continuationId, proposalVersion }))
79
+ return unknown('malformed-response');
80
+ const expected = request.expected.digest;
81
+ if (expected !== undefined && !DIGEST.test(expected))
82
+ return unknown('malformed-response');
83
+ let review;
84
+ try {
85
+ review = await options.store.read(targetId, request.scope.tenant, request.scope.project);
86
+ }
87
+ catch (error) {
88
+ return unknown(error instanceof ReviewAccessError ? 'auth-denied' : 'container-unreadable');
89
+ }
90
+ const base = { source: 'd13-review', found: review !== null, expectedDigest: expected ?? null };
91
+ if (!review)
92
+ return { result: 'absent', reason: 'complete-query-no-match', complete: true, evidence: base };
93
+ const state = { ...base, status: review.status, revision: review.revision };
94
+ if (review.status === 'tombstoned')
95
+ return unknown('container-unreadable', state);
96
+ if (review.continuation.id !== continuationId)
97
+ return unknown('evidence-conflict', state);
98
+ if (review.effectReceipt) {
99
+ const actual = receiptDigest(review);
100
+ const evidence = { ...state, receiptDigest: actual };
101
+ if (!actual)
102
+ return unknown('malformed-response', state);
103
+ // A receipt for another proposal version means this identity never completed here, but a
104
+ // replay under it would still race the recorded effect: never absent.
105
+ if (review.effectReceipt.effectId !== request.effectId)
106
+ return unknown('evidence-conflict', evidence);
107
+ if (expected === undefined)
108
+ return { result: 'present', reason: 'state-match', complete: true, evidence };
109
+ return actual === expected
110
+ ? { result: 'present', reason: 'digest-match', complete: true, evidence }
111
+ : unknown('evidence-conflict', evidence);
112
+ }
113
+ const dispatched = review.events.some(event => event.type === 'resumed' && event.data?.effectId === request.effectId);
114
+ if (!dispatched)
115
+ return { result: 'absent', reason: 'complete-query-no-match', complete: true, evidence: { ...state, dispatched: false } };
116
+ // A definitive executor failure is recorded only when the target rejected the effect.
117
+ if (review.status === 'execution-failed')
118
+ return { result: 'absent', reason: 'complete-query-no-match', complete: true, evidence: { ...state, dispatched: true } };
119
+ return delegate(options, request, { ...state, dispatched: true });
120
+ },
121
+ };
122
+ }
123
+ //# sourceMappingURL=review.js.map