rea-agents 2.0.0 → 2.1.0

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 (246) hide show
  1. package/README.md +78 -32
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/ArtifactExtraction.js +1 -1
  4. package/dist/application/ArtifactInventory/classify.js +42 -0
  5. package/dist/application/ArtifactInventory/hash.js +43 -0
  6. package/dist/application/ArtifactInventory/reader.js +41 -0
  7. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  8. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  9. package/dist/application/ArtifactInventory/types.js +10 -0
  10. package/dist/application/ArtifactInventory.js +9 -351
  11. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  12. package/dist/application/BinarySession.js +47 -270
  13. package/dist/application/BinarySessionExecution.js +28 -0
  14. package/dist/application/BinarySessionOpen.js +39 -0
  15. package/dist/application/BinarySessionRecords.js +149 -0
  16. package/dist/application/BinarySessionStatus.js +75 -0
  17. package/dist/application/CapabilityInventory.js +24 -2
  18. package/dist/application/ClientRegistrationStatus.js +1 -1
  19. package/dist/application/CrossVersionInventory.js +8 -2
  20. package/dist/application/CrossVersionInvestigation.js +1 -23
  21. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  22. package/dist/application/DirectAnalysis.js +47 -44
  23. package/dist/application/Doctor.js +156 -238
  24. package/dist/application/DoctorDiagnostics.js +180 -0
  25. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  26. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  27. package/dist/application/EnhancedToolPagination.js +22 -0
  28. package/dist/application/EnhancedToolTypes.js +4 -0
  29. package/dist/application/EnhancedTools.js +20 -37
  30. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  31. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  32. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  33. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  34. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  35. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  36. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  37. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  38. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  39. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  40. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  41. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  42. package/dist/application/JavaScriptJsonModules.js +39 -0
  43. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  44. package/dist/application/JavaScriptReplayPermission.js +16 -0
  45. package/dist/application/JavaScriptReplayService.js +182 -156
  46. package/dist/application/LinuxHopper.js +10 -0
  47. package/dist/application/MacHopper.js +22 -11
  48. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  49. package/dist/application/PermissionAuthority.js +106 -63
  50. package/dist/application/ProcessCaptureCapability.js +1 -1
  51. package/dist/application/ProcessCapturePermission.js +13 -0
  52. package/dist/application/ProcessCli.js +2 -12
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProjectPermissionStore.js +48 -9
  55. package/dist/application/RealHopperAssertions.js +76 -0
  56. package/dist/application/ReconstructionCoverageService.js +69 -0
  57. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  58. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  59. package/dist/application/Setup.js +147 -259
  60. package/dist/application/SetupClientConfiguration.js +220 -0
  61. package/dist/application/SetupClients.js +24 -2
  62. package/dist/application/SetupPlan.js +123 -12
  63. package/dist/application/SetupSkill.js +26 -12
  64. package/dist/application/SupportedClients.js +1 -0
  65. package/dist/application/Uninstall.js +6 -3
  66. package/dist/application/WindowsCapabilities.js +86 -0
  67. package/dist/artifacts/AsarArtifactReader.js +11 -0
  68. package/dist/browser/BrowserProcessStartup.js +88 -0
  69. package/dist/browser/CdpCaptureDocuments.js +12 -8
  70. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  71. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  72. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  73. package/dist/browser/CdpCaptureEventState.js +1 -0
  74. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  75. package/dist/browser/CdpCaptureEvents.js +29 -496
  76. package/dist/browser/CdpObservationSession.js +51 -8
  77. package/dist/browser/CdpPageCapture.js +12 -133
  78. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  79. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  80. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  81. package/dist/browser/PngVisualDiff.js +9 -2
  82. package/dist/catalogIdentity.js +4 -0
  83. package/dist/cli/artifactCommands.js +82 -0
  84. package/dist/cli/coreAnalysisCommands.js +232 -0
  85. package/dist/cli/managedCommands.js +397 -0
  86. package/dist/cli/managedProjectGraphCommand.js +24 -0
  87. package/dist/cli/options.js +9 -0
  88. package/dist/cli/setupCommands.js +108 -0
  89. package/dist/cli/types.js +1 -0
  90. package/dist/cli/utilityCommands.js +122 -0
  91. package/dist/cli.js +10 -768
  92. package/dist/cliApplicationCommands.js +118 -4
  93. package/dist/cliBrowserAdvancedCommands.js +73 -16
  94. package/dist/cliBrowserCommands.js +39 -115
  95. package/dist/cliCommandNames.js +4 -0
  96. package/dist/cliElectronCommands.js +33 -62
  97. package/dist/cliEvidenceCommands.js +13 -2
  98. package/dist/cliInvestigationCommands.js +53 -10
  99. package/dist/cliObservationOptions.js +137 -0
  100. package/dist/cliPolicyCommands.js +80 -46
  101. package/dist/cliSetup.js +289 -0
  102. package/dist/config/environment.js +107 -0
  103. package/dist/config/parseConfig.js +228 -0
  104. package/dist/config/parsers.js +68 -0
  105. package/dist/config/permissions.js +30 -0
  106. package/dist/config/types.js +1 -0
  107. package/dist/config.js +1 -375
  108. package/dist/contracts/applicationToolContracts.js +134 -0
  109. package/dist/contracts/managedToolContracts.js +1 -1
  110. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  111. package/dist/contracts/providerSelection.js +2 -0
  112. package/dist/contracts/toolContracts.js +2 -2
  113. package/dist/contracts/toolEffects.js +14 -0
  114. package/dist/contracts/toolOutputSchemaGroups.js +232 -0
  115. package/dist/contracts/toolOutputSchemaPrimitives.js +327 -0
  116. package/dist/contracts/toolOutputSchemas.js +2 -550
  117. package/dist/domain/analysisErrorPresentation.js +234 -0
  118. package/dist/domain/analysisErrorProjection.js +284 -0
  119. package/dist/domain/artifactComparison.js +34 -15
  120. package/dist/domain/binaryTarget.js +87 -37
  121. package/dist/domain/browserObservation.js +1 -285
  122. package/dist/domain/browserObservationSchemas.js +286 -0
  123. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  124. package/dist/domain/errors.js +4 -486
  125. package/dist/domain/functionComparison.js +4 -165
  126. package/dist/domain/functionComparisonDimensions.js +164 -0
  127. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  128. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  129. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  130. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  131. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  132. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  133. package/dist/domain/javascriptSemanticIr.js +30 -0
  134. package/dist/domain/javascriptSemanticProjection.js +366 -0
  135. package/dist/domain/javascriptSemanticState.js +40 -0
  136. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  137. package/dist/domain/javascriptSemanticValues.js +352 -0
  138. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  139. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  140. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  141. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  142. package/dist/domain/knownPageCoverage.js +21 -0
  143. package/dist/domain/managedApplicationGraph.js +14 -364
  144. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  145. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  146. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  147. package/dist/domain/managedArtifact.js +16 -8
  148. package/dist/domain/managedMemberComparison.js +37 -391
  149. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  150. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  151. package/dist/domain/managedNativeVerification.js +27 -389
  152. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  153. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  154. package/dist/domain/managedReconstruction.js +82 -69
  155. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  156. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  157. package/dist/domain/permissionPolicy.js +2 -0
  158. package/dist/domain/reconstructionCoverage.js +387 -0
  159. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  160. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  161. package/dist/domain/runtimeCharacterization.js +85 -0
  162. package/dist/domain/webBundleAnalyzer.js +3 -409
  163. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  164. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  165. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  166. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  167. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  168. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  169. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  170. package/dist/dotnet/ManagedMemberRows.js +120 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  172. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  173. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  174. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  175. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  176. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  177. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  178. package/dist/dotnet/ManagedPeReader.js +59 -28
  179. package/dist/generatedPackageMetadata.js +2 -2
  180. package/dist/ghidra/GhidraClient.js +76 -91
  181. package/dist/ghidra/GhidraClientStartup.js +29 -0
  182. package/dist/ghidra/GhidraClientWire.js +34 -0
  183. package/dist/ghidra/GhidraDefaults.js +1 -1
  184. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  185. package/dist/ghidra/GhidraInstallation.js +20 -15
  186. package/dist/ghidra/GhidraLauncher.js +93 -29
  187. package/dist/ghidra/GhidraProvider.js +61 -161
  188. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  189. package/dist/ghidra/GhidraSessionValues.js +2 -0
  190. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  191. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  192. package/dist/ghidra/GhidraTransport.js +71 -0
  193. package/dist/identity.js +3 -2
  194. package/dist/main/messages.js +5 -0
  195. package/dist/main/reload.js +49 -0
  196. package/dist/main/shutdown.js +25 -0
  197. package/dist/main/startup.js +14 -0
  198. package/dist/main/state.js +26 -0
  199. package/dist/main/transport.js +61 -0
  200. package/dist/main/types.js +1 -0
  201. package/dist/main.js +35 -146
  202. package/dist/process/PrivateRuntimeRoot.js +3 -2
  203. package/dist/process/ProcessOwnership.js +35 -0
  204. package/dist/process/ProviderProcess.js +2 -0
  205. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  206. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  207. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  208. package/dist/replay/ReplayOutcome.js +95 -0
  209. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  210. package/dist/server/ProcessCaptureElicitation.js +157 -0
  211. package/dist/server/createServer.js +119 -54
  212. package/dist/server/registerApplicationTools/characterization.js +57 -0
  213. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  214. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  215. package/dist/server/registerApplicationTools/coverage.js +84 -0
  216. package/dist/server/registerApplicationTools/helpers.js +42 -0
  217. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  218. package/dist/server/registerApplicationTools/types.js +1 -0
  219. package/dist/server/registerApplicationTools.js +10 -112
  220. package/dist/server/registerBrowserTools.js +59 -83
  221. package/dist/server/registerElectronTools.js +31 -46
  222. package/dist/server/registerEnhancedTools.js +34 -42
  223. package/dist/server/registerEvidenceResources.js +54 -0
  224. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  225. package/dist/server/registerInvestigationTools/types.js +1 -0
  226. package/dist/server/registerInvestigationTools.js +83 -116
  227. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  228. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  229. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  230. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  231. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  232. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  233. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  234. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  235. package/dist/server/registerManagedWorkflowTools.js +10 -274
  236. package/dist/server/registerOfficialTools.js +73 -59
  237. package/dist/server/registerSessionRecordTools.js +137 -0
  238. package/dist/server/registerSessionStatusTool.js +2 -1
  239. package/dist/server/registerSessionTools.js +60 -164
  240. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  241. package/dist/server/toolRegistrationOptions.js +101 -3
  242. package/dist/server/toolResult.js +5 -0
  243. package/package.json +10 -11
  244. package/skills/{rea-analysis → reverse-engineer-anything}/SKILL.md +20 -7
  245. package/scripts/prepare-node-pty.mjs +0 -35
  246. package/scripts/rebuild-native.mjs +0 -22
@@ -0,0 +1,66 @@
1
+ import { z } from "zod";
2
+ import { controlledReplayInputSchema, controlledReplayOutputSchema, } from "./javascriptReplay.js";
3
+ import { javascriptExportInstrumentationInputSchema, javascriptExportTransformationManifestSchema, } from "./javascriptExportInstrumentation.js";
4
+ import { runtimeCharacterizationPlanSchema } from "./runtimeCharacterization.js";
5
+ import { evidenceRecordSchema } from "./evidence.js";
6
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
7
+ export const nodeCharacterizationPreparationInputSchema = z
8
+ .strictObject({
9
+ preparation_approved: z.literal(true),
10
+ selected_alias: z.string().min(1).max(200),
11
+ expected_effect: z.enum(["pure", "observation-only"]),
12
+ instrumentation: javascriptExportInstrumentationInputSchema,
13
+ replay: controlledReplayInputSchema,
14
+ })
15
+ .superRefine((input, context) => {
16
+ if (input.replay.mode !== "plan")
17
+ context.addIssue({
18
+ code: "custom",
19
+ path: ["replay", "mode"],
20
+ message: "Characterization preparation requires replay plan mode",
21
+ });
22
+ const selected = input.replay.left.modules.filter(({ alias }) => alias === input.selected_alias);
23
+ if (selected.length !== 1 ||
24
+ selected[0]?.path !== input.instrumentation.artifact_path ||
25
+ selected[0]?.format !== "commonjs-factory")
26
+ context.addIssue({
27
+ code: "custom",
28
+ path: ["selected_alias"],
29
+ message: "Selected alias must identify exactly one commonjs-factory instrumented artifact",
30
+ });
31
+ if (input.replay.left.entry_alias !== input.selected_alias)
32
+ context.addIssue({
33
+ code: "custom",
34
+ path: ["replay", "left", "entry_alias"],
35
+ message: "Instrumented alias must be the characterization entry",
36
+ });
37
+ if (input.replay.left.entry_export !==
38
+ input.instrumentation.selection.export_name)
39
+ context.addIssue({
40
+ code: "custom",
41
+ path: ["replay", "left", "entry_export"],
42
+ message: "Replay entry export must match the instrumented export",
43
+ });
44
+ });
45
+ export const nodeCharacterizationExecutionInputSchema = z.strictObject({
46
+ execution_approved: z.literal(true),
47
+ approved_plan_sha256: digestSchema,
48
+ preparation: nodeCharacterizationPreparationInputSchema,
49
+ });
50
+ export const nodeCharacterizationPreparationOutputSchema = z.strictObject({
51
+ schema_version: z.literal(1),
52
+ phase: z.literal("preparation"),
53
+ plan: runtimeCharacterizationPlanSchema,
54
+ transformation: javascriptExportTransformationManifestSchema,
55
+ transformation_evidence: evidenceRecordSchema,
56
+ replay: controlledReplayOutputSchema,
57
+ });
58
+ export const nodeCharacterizationExecutionOutputSchema = z.strictObject({
59
+ schema_version: z.literal(1),
60
+ phase: z.literal("execution"),
61
+ plan: runtimeCharacterizationPlanSchema,
62
+ transformation: javascriptExportTransformationManifestSchema,
63
+ transformation_evidence: evidenceRecordSchema,
64
+ evidence: evidenceRecordSchema,
65
+ replay: controlledReplayOutputSchema,
66
+ });
@@ -65,6 +65,8 @@ export const evaluatePermission = (policy, request, now = new Date()) => {
65
65
  missing: missingFromScopes(normalized, candidates),
66
66
  };
67
67
  };
68
+ /** Test whether one exact request is wholly contained by one ceiling. */
69
+ export const isPermissionRequestWithinCeiling = (request, ceiling) => !hasMissing(missingFromScopes(normalizeRequest(request), [ceiling]));
68
70
  /** Consume an allowed one-shot decision; longer-lived grants are unchanged. */
69
71
  export const consumePermission = (policy, decision) => decision.lifetime === "once"
70
72
  ? {
@@ -0,0 +1,387 @@
1
+ import { createHash } from "node:crypto";
2
+ import canonicalize from "canonicalize";
3
+ import { z } from "zod";
4
+ import { evidenceBundleSchema, parseEvidenceBundle } from "./evidenceBundle.js";
5
+ import { evaluateReconstructionClaims, evaluateReconstructionPackageProofs, evaluateReconstructionSurfaces, evaluateReconstructionWorkspaceRisks, recommendedReconstructionProbes, } from "./reconstructionCoverageEvaluation.js";
6
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
7
+ const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
8
+ const stableIdSchema = z.string().regex(/^[A-Za-z][A-Za-z0-9._:/-]{0,199}$/u);
9
+ const boundedTextSchema = z.string().trim().min(1).max(4_096);
10
+ const authoritySchema = z.enum([
11
+ "observed",
12
+ "derived",
13
+ "inferred",
14
+ "historical",
15
+ "external",
16
+ ]);
17
+ const artifactSchema = z.strictObject({
18
+ artifact_id: stableIdSchema,
19
+ artifact_sha256: digestSchema,
20
+ version: z.string().trim().min(1).max(500),
21
+ environment_sha256: digestSchema,
22
+ evidence_ids: z.array(evidenceIdSchema).min(1).max(1_000),
23
+ });
24
+ const surfaceSchema = z.strictObject({
25
+ surface_id: stableIdSchema,
26
+ family: z.string().trim().min(1).max(100),
27
+ artifact_id: stableIdSchema,
28
+ occurrence_id: z.string().trim().min(1).max(500).nullable(),
29
+ location: boundedTextSchema,
30
+ authority: authoritySchema,
31
+ dependency_surface_ids: z.array(stableIdSchema).max(1_000),
32
+ evidence_ids: z.array(evidenceIdSchema).min(1).max(1_000),
33
+ });
34
+ const implementationOwnerSchema = z.strictObject({
35
+ disposition: z.literal("implemented"),
36
+ owner_path: boundedTextSchema,
37
+ owner_export: z.string().trim().min(1).max(500).nullable(),
38
+ owner_sha256: digestSchema,
39
+ path_state: z.enum(["present", "missing", "unknown"]),
40
+ package_state: z.enum(["distributed", "missing", "unknown"]),
41
+ authority_route: z.enum(["none", "detected", "unknown"]),
42
+ });
43
+ const externalOwnerSchema = z.strictObject({
44
+ disposition: z.enum(["external", "non-goal"]),
45
+ rationale: boundedTextSchema,
46
+ });
47
+ const ownerSchema = z.strictObject({
48
+ surface_id: stableIdSchema,
49
+ ownership: z.discriminatedUnion("disposition", [
50
+ implementationOwnerSchema,
51
+ externalOwnerSchema,
52
+ ]),
53
+ });
54
+ const claimSchema = z.strictObject({
55
+ claim_id: stableIdSchema,
56
+ title: z.string().trim().min(1).max(500),
57
+ kind: z.string().trim().min(1).max(100),
58
+ surface_ids: z.array(stableIdSchema).min(1).max(1_000),
59
+ required_dimensions: z.array(stableIdSchema).min(1).max(100),
60
+ required_authority: z.enum([
61
+ "shipped-artifact",
62
+ "controlled-replay",
63
+ "live-observation",
64
+ "external",
65
+ ]),
66
+ });
67
+ const verifierContractBaseSchema = z.strictObject({
68
+ verifier_id: stableIdSchema,
69
+ claim_ids: z.array(stableIdSchema).min(1).max(1_000),
70
+ dimensions: z.array(stableIdSchema).min(1).max(100),
71
+ authority: z.enum([
72
+ "shipped-artifact",
73
+ "controlled-replay",
74
+ "live-observation",
75
+ "external",
76
+ ]),
77
+ max_age_ms: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER),
78
+ minimum_repeats: z.number().int().min(1).max(1_000),
79
+ normalization_sha256: digestSchema,
80
+ normalization_removes_dimensions: z.literal(false),
81
+ });
82
+ const verifierContractSchema = verifierContractBaseSchema.extend({
83
+ contract_sha256: digestSchema,
84
+ });
85
+ const verifierResultSchema = z.strictObject({
86
+ verifier_id: stableIdSchema,
87
+ contract_sha256: digestSchema,
88
+ observed_at: z.string().datetime({ offset: true }),
89
+ status: z.enum([
90
+ "pass",
91
+ "fail",
92
+ "unknown",
93
+ "unsupported",
94
+ "truncated",
95
+ "skipped",
96
+ ]),
97
+ covered_claim_ids: z.array(stableIdSchema).min(1).max(1_000),
98
+ covered_dimensions: z.array(stableIdSchema).min(1).max(100),
99
+ artifact_sha256s: z.array(digestSchema).min(1).max(1_000),
100
+ owner_sha256s: z.array(digestSchema).max(1_000),
101
+ normalization_sha256: digestSchema,
102
+ repeats: z.number().int().min(1).max(1_000),
103
+ evidence_ids: z.array(evidenceIdSchema).min(1).max(1_000),
104
+ });
105
+ const contradictionSchema = z.strictObject({
106
+ contradiction_id: stableIdSchema,
107
+ status: z.enum(["active", "resolved"]),
108
+ surface_ids: z.array(stableIdSchema).max(1_000),
109
+ claim_ids: z.array(stableIdSchema).max(1_000),
110
+ evidence_ids: z.array(evidenceIdSchema).min(2).max(1_000),
111
+ });
112
+ const packageProofSchema = z.strictObject({
113
+ proof_id: stableIdSchema,
114
+ kind: z.enum([
115
+ "build",
116
+ "clean-install",
117
+ "package-contents",
118
+ "runtime-routing",
119
+ "authority-independence",
120
+ ]),
121
+ status: z.enum(["pass", "fail", "unknown", "unsupported", "skipped"]),
122
+ artifact_sha256s: z.array(digestSchema).min(1).max(1_000),
123
+ evidence_ids: z.array(evidenceIdSchema).min(1).max(1_000),
124
+ });
125
+ const boundarySchema = z.strictObject({
126
+ boundary_id: stableIdSchema,
127
+ title: z.string().trim().min(1).max(500),
128
+ required_surface_ids: z.array(stableIdSchema).min(1).max(10_000),
129
+ required_claim_ids: z.array(stableIdSchema).min(1).max(10_000),
130
+ required_package_proof_kinds: z
131
+ .array(packageProofSchema.shape.kind)
132
+ .min(1)
133
+ .max(5),
134
+ allowed_dispositions: z.array(z.enum(["external", "non-goal"])).max(2),
135
+ allowed_unknown_ids: z.array(stableIdSchema).max(1_000),
136
+ });
137
+ const workspaceSemanticSchema = z.strictObject({
138
+ schema_version: z.literal(1),
139
+ workspace_id: z.string().regex(/^rcw_[a-f0-9]{64}$/u),
140
+ name: z.string().trim().min(1).max(200),
141
+ revision: z.number().int().min(1),
142
+ previous_revision_sha256: digestSchema.nullable(),
143
+ evidence_bundle: evidenceBundleSchema,
144
+ artifacts: z.array(artifactSchema).max(1_000),
145
+ surfaces: z.array(surfaceSchema).max(10_000),
146
+ owners: z.array(ownerSchema).max(10_000),
147
+ claims: z.array(claimSchema).max(10_000),
148
+ verifier_contracts: z.array(verifierContractSchema).max(10_000),
149
+ verifier_results: z.array(verifierResultSchema).max(100_000),
150
+ residual_unknown_ids: z.array(stableIdSchema).max(10_000),
151
+ contradictions: z.array(contradictionSchema).max(10_000),
152
+ package_proofs: z.array(packageProofSchema).max(10_000),
153
+ boundaries: z.array(boundarySchema).max(1_000),
154
+ });
155
+ export const reconstructionCoverageWorkspaceSchema = workspaceSemanticSchema.extend({ revision_sha256: digestSchema });
156
+ const closureReasonSchema = z.strictObject({
157
+ code: z.enum([
158
+ "surface-missing",
159
+ "owner-missing",
160
+ "owner-duplicate",
161
+ "owner-path-missing",
162
+ "owner-path-unknown",
163
+ "owner-undistributed",
164
+ "owner-distribution-unknown",
165
+ "authority-routing-detected",
166
+ "authority-routing-unknown",
167
+ "disposition-not-allowed",
168
+ "dependency-surface-missing",
169
+ "claim-missing",
170
+ "verifier-missing",
171
+ "verifier-duplicate",
172
+ "verifier-result-missing",
173
+ "verifier-result-stale",
174
+ "verifier-result-incompatible",
175
+ "verifier-failed",
176
+ "verifier-unknown",
177
+ "active-unknown",
178
+ "active-contradiction",
179
+ "package-proof-missing",
180
+ "package-proof-failed",
181
+ "package-proof-unknown",
182
+ ]),
183
+ subject_id: stableIdSchema,
184
+ detail: boundedTextSchema,
185
+ });
186
+ export const reconstructionClosureResultSchema = z.strictObject({
187
+ boundary_id: stableIdSchema,
188
+ status: z.enum(["partial", "failed", "unknown", "ready"]),
189
+ workspace_revision_sha256: digestSchema,
190
+ summary: z.strictObject({
191
+ required_surfaces: z.number().int().min(0),
192
+ required_claims: z.number().int().min(0),
193
+ reasons: z.number().int().min(0),
194
+ }),
195
+ reasons: z.array(closureReasonSchema).max(100_000),
196
+ recommended_probes: z
197
+ .array(z.strictObject({
198
+ operation: stableIdSchema,
199
+ subject_id: stableIdSchema,
200
+ rationale: boundedTextSchema,
201
+ }))
202
+ .max(100_000),
203
+ evidence_ids: z.array(evidenceIdSchema).max(100_000),
204
+ });
205
+ /** Build a verifier contract whose digest prevents retrospective coverage edits. */
206
+ export const createReconstructionVerifierContract = (input) => {
207
+ const parsed = verifierContractBaseSchema.parse(input);
208
+ return verifierContractSchema.parse({
209
+ ...parsed,
210
+ contract_sha256: digest(parsed),
211
+ });
212
+ };
213
+ /** Build one canonical immutable coverage-workspace revision. */
214
+ export const createReconstructionCoverageWorkspace = (input) => {
215
+ const semanticInput = {
216
+ schema_version: 1,
217
+ workspace_id: `rcw_${digest({ schema: "rea.reconstruction-coverage-workspace/v1", name: input.name })}`,
218
+ ...input,
219
+ };
220
+ const semantic = canonicalWorkspace(workspaceSemanticSchema.parse(semanticInput));
221
+ validateWorkspaceReferences(semantic);
222
+ return reconstructionCoverageWorkspaceSchema.parse({
223
+ ...semantic,
224
+ revision_sha256: digest(semantic),
225
+ });
226
+ };
227
+ /** Parse persisted coverage state and verify canonical order, references, and digests. */
228
+ export const parseReconstructionCoverageWorkspace = (input) => {
229
+ const parsed = reconstructionCoverageWorkspaceSchema.parse(input);
230
+ const rebuilt = createReconstructionCoverageWorkspace({
231
+ name: parsed.name,
232
+ revision: parsed.revision,
233
+ previous_revision_sha256: parsed.previous_revision_sha256,
234
+ evidence_bundle: parsed.evidence_bundle,
235
+ artifacts: parsed.artifacts,
236
+ surfaces: parsed.surfaces,
237
+ owners: parsed.owners,
238
+ claims: parsed.claims,
239
+ verifier_contracts: parsed.verifier_contracts,
240
+ verifier_results: parsed.verifier_results,
241
+ residual_unknown_ids: parsed.residual_unknown_ids,
242
+ contradictions: parsed.contradictions,
243
+ package_proofs: parsed.package_proofs,
244
+ boundaries: parsed.boundaries,
245
+ });
246
+ if (parsed.workspace_id !== rebuilt.workspace_id ||
247
+ parsed.revision_sha256 !== rebuilt.revision_sha256 ||
248
+ JSON.stringify(parsed) !== JSON.stringify(rebuilt))
249
+ throw new TypeError("Reconstruction coverage workspace is not canonical");
250
+ return rebuilt;
251
+ };
252
+ /** Serialize one validated coverage revision as byte-stable canonical JSON. */
253
+ export const serializeReconstructionCoverageWorkspace = (workspace) => {
254
+ const encoded = canonicalize(parseReconstructionCoverageWorkspace(workspace));
255
+ if (encoded === undefined)
256
+ throw new TypeError("Reconstruction coverage workspace is not canonical JSON");
257
+ return encoded;
258
+ };
259
+ /** Evaluate one named completion boundary without inferring undiscovered coverage. */
260
+ export const evaluateReconstructionClosure = (workspaceInput, boundaryId, nowEpochMs) => {
261
+ const workspace = parseReconstructionCoverageWorkspace(workspaceInput);
262
+ const boundary = workspace.boundaries.find(({ boundary_id: id }) => id === boundaryId);
263
+ if (boundary === undefined)
264
+ throw new TypeError(`Unknown reconstruction boundary: ${boundaryId}`);
265
+ const context = {
266
+ workspace,
267
+ boundary,
268
+ nowEpochMs,
269
+ reasons: [],
270
+ evidenceIds: new Set(),
271
+ };
272
+ evaluateReconstructionSurfaces(context);
273
+ evaluateReconstructionClaims(context);
274
+ evaluateReconstructionWorkspaceRisks(context);
275
+ evaluateReconstructionPackageProofs(context);
276
+ const reasons = [...context.reasons].sort(reasonOrder);
277
+ return reconstructionClosureResultSchema.parse({
278
+ boundary_id: boundary.boundary_id,
279
+ status: closureStatus(reasons),
280
+ workspace_revision_sha256: workspace.revision_sha256,
281
+ summary: {
282
+ required_surfaces: boundary.required_surface_ids.length,
283
+ required_claims: boundary.required_claim_ids.length,
284
+ reasons: reasons.length,
285
+ },
286
+ reasons,
287
+ recommended_probes: recommendedReconstructionProbes(reasons),
288
+ evidence_ids: [...context.evidenceIds].sort(),
289
+ });
290
+ };
291
+ const validateWorkspaceReferences = (workspace) => {
292
+ const evidenceBundle = parseEvidenceBundle(workspace.evidence_bundle);
293
+ const evidenceIds = new Set(evidenceBundle.records.map(({ evidence_id: id }) => id));
294
+ const unknownIds = new Set(evidenceBundle.unknowns.map(({ unknown_id: id }) => id));
295
+ const artifacts = assertUnique(workspace.artifacts, "artifact_id");
296
+ const surfaces = assertUnique(workspace.surfaces, "surface_id");
297
+ const claims = assertUnique(workspace.claims, "claim_id");
298
+ const contracts = assertUnique(workspace.verifier_contracts, "verifier_id");
299
+ assertUnique(workspace.contradictions, "contradiction_id");
300
+ assertUnique(workspace.package_proofs, "proof_id");
301
+ assertUnique(workspace.boundaries, "boundary_id");
302
+ for (const contract of workspace.verifier_contracts) {
303
+ const { contract_sha256: contractSha256, ...base } = contract;
304
+ if (contractSha256 !== digest(verifierContractBaseSchema.parse(base)))
305
+ throw new TypeError(`Verifier contract digest is invalid: ${contract.verifier_id}`);
306
+ }
307
+ for (const surface of workspace.surfaces)
308
+ if (!artifacts.has(surface.artifact_id))
309
+ throw new TypeError(`Surface artifact is missing: ${surface.surface_id}`);
310
+ for (const owner of workspace.owners)
311
+ if (!surfaces.has(owner.surface_id))
312
+ throw new TypeError(`Owner surface is missing: ${owner.surface_id}`);
313
+ for (const claim of workspace.claims)
314
+ if (claim.surface_ids.some((id) => !surfaces.has(id)))
315
+ throw new TypeError(`Claim surface is missing: ${claim.claim_id}`);
316
+ for (const contract of workspace.verifier_contracts)
317
+ if (contract.claim_ids.some((id) => !claims.has(id)))
318
+ throw new TypeError(`Verifier claim is missing: ${contract.verifier_id}`);
319
+ for (const result of workspace.verifier_results)
320
+ if (!contracts.has(result.verifier_id))
321
+ throw new TypeError(`Verifier result contract is missing: ${result.verifier_id}`);
322
+ const referencedEvidenceIds = [
323
+ ...workspace.artifacts.flatMap(({ evidence_ids: ids }) => ids),
324
+ ...workspace.surfaces.flatMap(({ evidence_ids: ids }) => ids),
325
+ ...workspace.verifier_results.flatMap(({ evidence_ids: ids }) => ids),
326
+ ...workspace.contradictions.flatMap(({ evidence_ids: ids }) => ids),
327
+ ...workspace.package_proofs.flatMap(({ evidence_ids: ids }) => ids),
328
+ ];
329
+ const danglingEvidence = referencedEvidenceIds.find((id) => !evidenceIds.has(id));
330
+ if (danglingEvidence !== undefined)
331
+ throw new TypeError(`Coverage workspace Evidence is missing: ${danglingEvidence}`);
332
+ const danglingUnknown = workspace.residual_unknown_ids.find((id) => !unknownIds.has(id));
333
+ if (danglingUnknown !== undefined)
334
+ throw new TypeError(`Coverage workspace residual unknown is missing: ${danglingUnknown}`);
335
+ };
336
+ const canonicalWorkspace = (workspace) => ({
337
+ ...workspace,
338
+ artifacts: sorted(workspace.artifacts, "artifact_id"),
339
+ surfaces: sorted(workspace.surfaces, "surface_id"),
340
+ owners: [...workspace.owners].sort((left, right) => left.surface_id.localeCompare(right.surface_id)),
341
+ claims: sorted(workspace.claims, "claim_id"),
342
+ verifier_contracts: sorted(workspace.verifier_contracts, "verifier_id"),
343
+ verifier_results: [...workspace.verifier_results].sort((left, right) => `${left.verifier_id}:${left.observed_at}`.localeCompare(`${right.verifier_id}:${right.observed_at}`)),
344
+ residual_unknown_ids: [...new Set(workspace.residual_unknown_ids)].sort(),
345
+ contradictions: sorted(workspace.contradictions, "contradiction_id"),
346
+ package_proofs: sorted(workspace.package_proofs, "proof_id"),
347
+ boundaries: sorted(workspace.boundaries, "boundary_id"),
348
+ });
349
+ const sorted = (items, key) => [...items].sort((left, right) => left[key].localeCompare(right[key]));
350
+ const assertUnique = (items, key) => {
351
+ const values = items.map((item) => item[key]);
352
+ if (new Set(values).size !== values.length)
353
+ throw new TypeError(`Duplicate reconstruction coverage ${String(key)}`);
354
+ return new Set(values);
355
+ };
356
+ const closureStatus = (reasons) => {
357
+ if (reasons.length === 0)
358
+ return "ready";
359
+ if (reasons.some(({ code }) => [
360
+ "verifier-failed",
361
+ "active-contradiction",
362
+ "authority-routing-detected",
363
+ "owner-path-missing",
364
+ "owner-undistributed",
365
+ "package-proof-failed",
366
+ ].includes(code)))
367
+ return "failed";
368
+ if (reasons.some(({ code }) => [
369
+ "verifier-result-stale",
370
+ "verifier-result-incompatible",
371
+ "verifier-unknown",
372
+ "active-unknown",
373
+ "owner-path-unknown",
374
+ "owner-distribution-unknown",
375
+ "authority-routing-unknown",
376
+ "package-proof-unknown",
377
+ ].includes(code)))
378
+ return "unknown";
379
+ return "partial";
380
+ };
381
+ const reasonOrder = (left, right) => `${left.code}:${left.subject_id}`.localeCompare(`${right.code}:${right.subject_id}`);
382
+ const digest = (value) => {
383
+ const encoded = canonicalize(value);
384
+ if (encoded === undefined)
385
+ throw new TypeError("Reconstruction coverage value is not canonical JSON");
386
+ return createHash("sha256").update(encoded).digest("hex");
387
+ };
@@ -0,0 +1,212 @@
1
+ export const recommendedReconstructionProbes = (reasons) => reasons.slice(0, 100).map((reason) => ({
2
+ operation: probeOperation(reason.code),
3
+ subject_id: reason.subject_id,
4
+ rationale: reason.detail,
5
+ }));
6
+ const probeOperation = (code) => {
7
+ if (code.startsWith("owner-") || code.startsWith("authority-routing"))
8
+ return "reconcile_reconstruction_owner";
9
+ if (code.startsWith("verifier-"))
10
+ return "run_reconstruction_verifier";
11
+ if (code.startsWith("package-proof"))
12
+ return "verify_reconstruction_package";
13
+ if (code === "active-contradiction")
14
+ return "resolve_contradiction";
15
+ if (code === "active-unknown")
16
+ return "probe_residual_unknown";
17
+ return "update_authoritative_inventory";
18
+ };
19
+ export const evaluateReconstructionSurfaces = (context) => {
20
+ const surfaces = indexUnique(context.workspace.surfaces, "surface_id");
21
+ for (const surfaceId of context.boundary.required_surface_ids) {
22
+ const surface = surfaces.get(surfaceId);
23
+ if (surface === undefined) {
24
+ addReason(context, "surface-missing", surfaceId, "Required surface is absent from the authoritative inventory.");
25
+ continue;
26
+ }
27
+ addEvidence(context, surface.evidence_ids);
28
+ for (const dependencyId of surface.dependency_surface_ids)
29
+ if (!surfaces.has(dependencyId))
30
+ addReason(context, "dependency-surface-missing", surfaceId, `Declared dependency surface is absent: ${dependencyId}`);
31
+ evaluateOwner(context, surfaceId);
32
+ }
33
+ };
34
+ const evaluateOwner = (context, surfaceId) => {
35
+ const owners = context.workspace.owners.filter(({ surface_id: id }) => id === surfaceId);
36
+ if (owners.length === 0) {
37
+ addReason(context, "owner-missing", surfaceId, "Required surface has no reconstructed owner or explicit disposition.");
38
+ return;
39
+ }
40
+ if (owners.length > 1) {
41
+ addReason(context, "owner-duplicate", surfaceId, "Required surface has conflicting ownership declarations.");
42
+ return;
43
+ }
44
+ const owner = owners[0]?.ownership;
45
+ if (owner === undefined)
46
+ return;
47
+ if (owner.disposition !== "implemented") {
48
+ if (!context.boundary.allowed_dispositions.includes(owner.disposition))
49
+ addReason(context, "disposition-not-allowed", surfaceId, `${owner.disposition} is not allowed by this completion boundary.`);
50
+ return;
51
+ }
52
+ const ownerChecks = [
53
+ [
54
+ owner.path_state === "missing",
55
+ "owner-path-missing",
56
+ "Declared owner path is missing.",
57
+ ],
58
+ [
59
+ owner.path_state === "unknown",
60
+ "owner-path-unknown",
61
+ "Declared owner path has not been checked.",
62
+ ],
63
+ [
64
+ owner.package_state === "missing",
65
+ "owner-undistributed",
66
+ "Declared owner is absent from the distributable.",
67
+ ],
68
+ [
69
+ owner.package_state === "unknown",
70
+ "owner-distribution-unknown",
71
+ "Owner distribution state is unknown.",
72
+ ],
73
+ [
74
+ owner.authority_route === "detected",
75
+ "authority-routing-detected",
76
+ "Reconstructed owner routes into the authority.",
77
+ ],
78
+ [
79
+ owner.authority_route === "unknown",
80
+ "authority-routing-unknown",
81
+ "Authority independence has not been established.",
82
+ ],
83
+ ];
84
+ for (const [matches, code, detail] of ownerChecks)
85
+ if (matches)
86
+ addReason(context, code, surfaceId, detail);
87
+ };
88
+ export const evaluateReconstructionClaims = (context) => {
89
+ const claims = indexUnique(context.workspace.claims, "claim_id");
90
+ for (const claimId of context.boundary.required_claim_ids) {
91
+ const claim = claims.get(claimId);
92
+ if (claim === undefined) {
93
+ addReason(context, "claim-missing", claimId, "Required finite claim is absent.");
94
+ continue;
95
+ }
96
+ const contracts = context.workspace.verifier_contracts.filter(({ claim_ids: ids }) => ids.includes(claimId));
97
+ if (contracts.length === 0) {
98
+ addReason(context, "verifier-missing", claimId, "No verifier contract covers this claim.");
99
+ continue;
100
+ }
101
+ if (contracts.length > 1) {
102
+ addReason(context, "verifier-duplicate", claimId, "Multiple verifier contracts claim primary coverage.");
103
+ continue;
104
+ }
105
+ const contract = contracts[0];
106
+ if (contract !== undefined)
107
+ evaluateVerifierResult(context, claim, contract);
108
+ }
109
+ };
110
+ const evaluateVerifierResult = (context, claim, contract) => {
111
+ const results = context.workspace.verifier_results
112
+ .filter(({ verifier_id: id }) => id === contract.verifier_id)
113
+ .sort((left, right) => Date.parse(right.observed_at) - Date.parse(left.observed_at));
114
+ const result = results[0];
115
+ if (result === undefined) {
116
+ addReason(context, "verifier-result-missing", claim.claim_id, "Verifier has no recorded result.");
117
+ return;
118
+ }
119
+ addEvidence(context, result.evidence_ids);
120
+ if (!verifierResultIsCompatible(context, claim, contract, result)) {
121
+ addReason(context, "verifier-result-incompatible", claim.claim_id, "Latest verifier result does not match current artifacts, owners, dimensions, authority, normalization, repeats, or contract.");
122
+ return;
123
+ }
124
+ const observedAt = Date.parse(result.observed_at);
125
+ if (observedAt > context.nowEpochMs ||
126
+ context.nowEpochMs - observedAt > contract.max_age_ms) {
127
+ addReason(context, "verifier-result-stale", claim.claim_id, "Latest compatible verifier result is stale.");
128
+ return;
129
+ }
130
+ if (result.status === "fail")
131
+ addReason(context, "verifier-failed", claim.claim_id, "Latest compatible verifier result failed.");
132
+ else if (result.status !== "pass")
133
+ addReason(context, "verifier-unknown", claim.claim_id, `Latest compatible verifier result is ${result.status}.`);
134
+ };
135
+ const verifierResultIsCompatible = (context, claim, contract, result) => {
136
+ const artifactDigests = new Set(context.workspace.artifacts.map(({ artifact_sha256: value }) => value));
137
+ const ownerDigests = new Set(context.workspace.owners.flatMap(({ ownership }) => ownership.disposition === "implemented" ? [ownership.owner_sha256] : []));
138
+ const surfaces = new Map(context.workspace.surfaces.map((surface) => [surface.surface_id, surface]));
139
+ const artifacts = new Map(context.workspace.artifacts.map((artifact) => [
140
+ artifact.artifact_id,
141
+ artifact,
142
+ ]));
143
+ const expectedArtifactDigests = claim.surface_ids.flatMap((surfaceId) => {
144
+ const surface = surfaces.get(surfaceId);
145
+ const artifact = surface === undefined ? undefined : artifacts.get(surface.artifact_id);
146
+ return artifact === undefined ? [] : [artifact.artifact_sha256];
147
+ });
148
+ const expectedOwnerDigests = context.workspace.owners.flatMap(({ surface_id: surfaceId, ownership }) => claim.surface_ids.includes(surfaceId) &&
149
+ ownership.disposition === "implemented"
150
+ ? [ownership.owner_sha256]
151
+ : []);
152
+ return (result.contract_sha256 === contract.contract_sha256 &&
153
+ result.normalization_sha256 === contract.normalization_sha256 &&
154
+ result.covered_claim_ids.includes(claim.claim_id) &&
155
+ result.covered_claim_ids.every((item) => contract.claim_ids.includes(item)) &&
156
+ claim.required_dimensions.every((item) => contract.dimensions.includes(item)) &&
157
+ claim.required_dimensions.every((item) => result.covered_dimensions.includes(item)) &&
158
+ result.covered_dimensions.every((item) => contract.dimensions.includes(item)) &&
159
+ result.artifact_sha256s.every((item) => artifactDigests.has(item)) &&
160
+ expectedArtifactDigests.every((item) => result.artifact_sha256s.includes(item)) &&
161
+ result.owner_sha256s.every((item) => ownerDigests.has(item)) &&
162
+ expectedOwnerDigests.every((item) => result.owner_sha256s.includes(item)) &&
163
+ result.repeats >= contract.minimum_repeats &&
164
+ claim.required_authority === contract.authority);
165
+ };
166
+ export const evaluateReconstructionWorkspaceRisks = (context) => {
167
+ for (const unknownId of context.workspace.residual_unknown_ids)
168
+ if (!context.boundary.allowed_unknown_ids.includes(unknownId))
169
+ addReason(context, "active-unknown", unknownId, "Active residual unknown is not permitted by the completion boundary.");
170
+ for (const contradiction of context.workspace.contradictions)
171
+ if (contradiction.status === "active" &&
172
+ (contradiction.surface_ids.some((id) => context.boundary.required_surface_ids.includes(id)) ||
173
+ contradiction.claim_ids.some((id) => context.boundary.required_claim_ids.includes(id)))) {
174
+ addEvidence(context, contradiction.evidence_ids);
175
+ addReason(context, "active-contradiction", contradiction.contradiction_id, "Active contradiction affects the completion boundary.");
176
+ }
177
+ };
178
+ export const evaluateReconstructionPackageProofs = (context) => {
179
+ const surfaces = new Map(context.workspace.surfaces.map((surface) => [surface.surface_id, surface]));
180
+ const artifacts = new Map(context.workspace.artifacts.map((artifact) => [
181
+ artifact.artifact_id,
182
+ artifact,
183
+ ]));
184
+ const requiredArtifactDigests = context.boundary.required_surface_ids.flatMap((surfaceId) => {
185
+ const surface = surfaces.get(surfaceId);
186
+ const artifact = surface === undefined ? undefined : artifacts.get(surface.artifact_id);
187
+ return artifact === undefined ? [] : [artifact.artifact_sha256];
188
+ });
189
+ for (const kind of context.boundary.required_package_proof_kinds) {
190
+ const proofs = context.workspace.package_proofs.filter((proof) => proof.kind === kind);
191
+ const passing = proofs.find(({ artifact_sha256s: digests, status }) => status === "pass" &&
192
+ requiredArtifactDigests.every((digest) => digests.includes(digest)));
193
+ if (passing !== undefined) {
194
+ addEvidence(context, passing.evidence_ids);
195
+ continue;
196
+ }
197
+ if (proofs.some(({ status }) => status === "fail"))
198
+ addReason(context, "package-proof-failed", kind, "Required package or integration proof failed.");
199
+ else if (proofs.length > 0)
200
+ addReason(context, "package-proof-unknown", kind, "Required package or integration proof is not passing.");
201
+ else
202
+ addReason(context, "package-proof-missing", kind, "Required package or integration proof is missing.");
203
+ }
204
+ };
205
+ const indexUnique = (items, key) => new Map(items.map((item) => [item[key], item]));
206
+ const addReason = (context, code, subjectId, detail) => {
207
+ context.reasons.push({ code, subject_id: subjectId, detail });
208
+ };
209
+ const addEvidence = (context, evidenceIds) => {
210
+ for (const evidenceId of evidenceIds)
211
+ context.evidenceIds.add(evidenceId);
212
+ };