rea-agents 2.3.0 → 2.5.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.
- package/README.md +80 -18
- package/bridge/ghidra/ReaGhidraBridge.java +48 -1
- package/bridge/hopper_bridge.py +259 -24
- package/dist/application/AnalysisClientCleanup.js +14 -0
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
- package/dist/application/ArtifactExtraction.js +5 -1
- package/dist/application/ArtifactGraphConstruction.js +3 -15
- package/dist/application/ArtifactInventory/classify.js +5 -1
- package/dist/application/ArtifactInventory/reader.js +2 -0
- package/dist/application/BinarySession.js +15 -4
- package/dist/application/BinarySessionStatus.js +32 -1
- package/dist/application/BrowserEvidence.js +14 -0
- package/dist/application/BrowserScenarioCapturePort.js +1 -0
- package/dist/application/BrowserScenarioCaptureService.js +53 -0
- package/dist/application/BrowserScenarioEvidence.js +40 -0
- package/dist/application/CallPathTracing.js +160 -0
- package/dist/application/CapabilityInventory.js +79 -15
- package/dist/application/CompositeProvider.js +10 -1
- package/dist/application/DirectAnalysis.js +7 -2
- package/dist/application/EnhancedLiteralTracing.js +118 -0
- package/dist/application/EnhancedTools.js +35 -121
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
- package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
- package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
- package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
- package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
- package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
- package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
- package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
- package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
- package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
- package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
- package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
- package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
- package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
- package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
- package/dist/application/ProcessCaptureEnvironment.js +26 -0
- package/dist/application/ProcessCaptureLifecycle.js +1 -0
- package/dist/application/ProcessCheckpoints.js +5 -0
- package/dist/application/ProcessHarness.js +45 -34
- package/dist/application/ProcessNormalization.js +6 -0
- package/dist/application/ProcessReactiveCoordinator.js +10 -3
- package/dist/application/ProcessReactiveEffects.js +15 -1
- package/dist/application/ProcessReactiveHarness.js +100 -0
- package/dist/application/ProcessReactiveObservations.js +101 -16
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/ReconstructionObligationAuthority.js +72 -0
- package/dist/application/ReconstructionObligationCandidates.js +265 -0
- package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
- package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
- package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
- package/dist/application/ReconstructionObligationLedgerService.js +72 -0
- package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
- package/dist/application/ReconstructionObligationPolicies.js +132 -0
- package/dist/application/ReconstructionReadinessService.js +48 -0
- package/dist/artifacts/ArtifactProvider.js +87 -22
- package/dist/browser/BrowserScenarioSecrets.js +53 -0
- package/dist/browser/BrowserScenarioSessionPort.js +1 -0
- package/dist/browser/CdpBrowserProvider.js +2 -2
- package/dist/browser/CdpCaptureDocuments.js +57 -0
- package/dist/browser/CdpCaptureStorage.js +71 -29
- package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
- package/dist/browser/CdpEndpoint.js +8 -6
- package/dist/browser/CdpPageCapture.js +2 -0
- package/dist/browser/JavaScriptRuntimeScope.js +38 -0
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +253 -0
- package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
- package/dist/browser/PlaywrightScenarioActions.js +62 -0
- package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
- package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
- package/dist/browser/PlaywrightScenarioEvents.js +301 -0
- package/dist/browser/PlaywrightScenarioSession.js +300 -0
- package/dist/browser/V8InspectorCaptureProjection.js +110 -0
- package/dist/browser/V8InspectorEndpoint.js +72 -0
- package/dist/browser/V8InspectorProvider.js +267 -0
- package/dist/catalogIdentity.js +8 -0
- package/dist/cli/artifactCommands.js +39 -0
- package/dist/cli/coreAnalysisCommands.js +37 -0
- package/dist/cli.js +4 -0
- package/dist/cliApplicationCommands.js +30 -2
- package/dist/cliBrowserAdvancedCommands.js +17 -5
- package/dist/cliBrowserCommands.js +1 -0
- package/dist/cliBrowserScenarioCommands.js +43 -0
- package/dist/cliCommandNames.js +8 -0
- package/dist/cliJavaScriptRuntimeCommands.js +164 -0
- package/dist/cliObservationOptions.js +4 -0
- package/dist/cliPolicyCommands.js +1 -0
- package/dist/config/environment.js +12 -0
- package/dist/config/parseConfig.js +88 -17
- package/dist/contracts/analysisSearchContract.js +14 -0
- package/dist/contracts/applicationToolContracts.js +86 -2
- package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
- package/dist/contracts/artifactToolContracts.js +22 -0
- package/dist/contracts/browserProviderToolContracts.js +7 -0
- package/dist/contracts/browserScenarioToolContracts.js +81 -0
- package/dist/contracts/browserToolContracts.js +88 -98
- package/dist/contracts/enhancedInputs.js +25 -6
- package/dist/contracts/functionInstructionContract.js +8 -0
- package/dist/contracts/hopperMemoryContracts.js +22 -0
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
- package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
- package/dist/contracts/reconstructionReadinessExample.js +436 -0
- package/dist/contracts/toolContractExamples.js +5 -0
- package/dist/contracts/toolContractTypes.js +1 -0
- package/dist/contracts/toolContracts.js +13 -21
- package/dist/contracts/toolEffects.js +21 -0
- package/dist/contracts/toolOutputSchemaGroups.js +70 -20
- package/dist/contracts/toolOutputSchemaPrimitives.js +40 -0
- package/dist/domain/analysisErrorProjection.js +8 -0
- package/dist/domain/analysisSnapshot.js +2 -0
- package/dist/domain/artifactGraph.js +2 -0
- package/dist/domain/artifactInspection.js +347 -0
- package/dist/domain/binaryTarget.js +3 -6
- package/dist/domain/browserCaptureComparison.js +82 -0
- package/dist/domain/browserObservation.js +8 -1
- package/dist/domain/browserObservationErrors.js +1 -0
- package/dist/domain/browserObservationSchemas.js +26 -0
- package/dist/domain/browserScenario.js +165 -0
- package/dist/domain/browserScenarioCapture.js +57 -0
- package/dist/domain/browserScenarioCaptureValues.js +204 -0
- package/dist/domain/browserScenarioDiff.js +324 -0
- package/dist/domain/browserScenarioDiffValues.js +157 -0
- package/dist/domain/browserScenarioNormalization.js +45 -0
- package/dist/domain/browserScenarioValues.js +372 -0
- package/dist/domain/evidence.js +2 -0
- package/dist/domain/hopperValues.js +10 -0
- package/dist/domain/javascriptRuntimeObservation.js +170 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
- package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
- package/dist/domain/javascriptSemanticAnalysis.js +5 -4
- package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
- package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
- package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
- package/dist/domain/javascriptSemanticDataEffects.js +284 -0
- package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
- package/dist/domain/javascriptSemanticFingerprints.js +174 -0
- package/dist/domain/javascriptSemanticIr.js +21 -1
- package/dist/domain/javascriptSemanticObjects.js +115 -0
- package/dist/domain/javascriptSemanticPromises.js +290 -0
- package/dist/domain/javascriptSemanticResources.js +136 -0
- package/dist/domain/javascriptStaticAnalysis.js +4 -3
- package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
- package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
- package/dist/domain/processCapture.js +71 -1
- package/dist/domain/processCaptureReactiveValidation.js +175 -0
- package/dist/domain/processCaptureValidation.js +2 -0
- package/dist/domain/processObservation.js +15 -6
- package/dist/domain/processReactiveRuntime.js +21 -10
- package/dist/domain/processReactiveScenario.js +1 -0
- package/dist/domain/processReactiveTransition.js +17 -10
- package/dist/domain/processScenario.js +21 -0
- package/dist/domain/providerCleanupError.js +11 -0
- package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
- package/dist/domain/reconstructionReadiness.js +121 -0
- package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
- package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
- package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
- package/dist/domain/reconstructionReadinessSchemas.js +240 -0
- package/dist/domain/sourceToBundleComparison.js +335 -0
- package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
- package/dist/domain/sourceToBundleSignals.js +202 -0
- package/dist/domain/webCaptureDiff.js +141 -27
- package/dist/domain/webScreenshot.js +1 -1
- package/dist/domain/zipPackageFormat.js +20 -0
- package/dist/generatedPackageMetadata.js +1 -1
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraFunctionValues.js +13 -1
- package/dist/ghidra/GhidraProviderCapabilities.js +7 -0
- package/dist/hopper/HopperCleanup.js +158 -0
- package/dist/hopper/HopperClient.js +69 -99
- package/dist/hopper/HopperProvider.js +20 -1
- package/dist/hopper/HopperRequestQueue.js +187 -0
- package/dist/hopper/HopperResponseStream.js +46 -0
- package/dist/hopper/protocol.js +1 -0
- package/dist/main/transport.js +24 -24
- package/dist/main.js +6 -0
- package/dist/mcpDoctor.js +25 -2
- package/dist/native/NativeMacOSProvider.js +1 -0
- package/dist/process/ProcessCommandIdentity.js +19 -0
- package/dist/process/ProcessOwnership.js +11 -8
- package/dist/server/DynamicToolAvailability.js +70 -0
- package/dist/server/createServer.js +46 -1
- package/dist/server/lifecycleProgress.js +10 -0
- package/dist/server/permissionFailure.js +12 -0
- package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
- package/dist/server/registerApplicationTools/helpers.js +11 -7
- package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
- package/dist/server/registerApplicationTools/readiness.js +55 -0
- package/dist/server/registerApplicationTools.js +6 -0
- package/dist/server/registerArtifactTools.js +9 -1
- package/dist/server/registerBrowserScenarioTool.js +25 -0
- package/dist/server/registerBrowserTools.js +2 -2
- package/dist/server/registerCloseLifecycleTool.js +57 -0
- package/dist/server/registerEnhancedTools.js +16 -5
- package/dist/server/registerEvidenceResources.js +4 -0
- package/dist/server/registerEvidenceTools.js +9 -2
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
- package/dist/server/registerReconstructionObligationLedgerResource.js +39 -0
- package/dist/server/registerReconstructionReadinessResource.js +39 -0
- package/dist/server/registerSessionStatusTool.js +11 -8
- package/dist/server/registerSessionTools.js +11 -57
- package/dist/server/sessionAvailabilityPolicy.js +10 -8
- package/package.json +12 -6
- package/skills/reverse-engineer-anything/SKILL.md +4 -3
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +2 -2
|
@@ -75,7 +75,15 @@ const applyProcessReactiveTransition = (input) => {
|
|
|
75
75
|
record,
|
|
76
76
|
};
|
|
77
77
|
};
|
|
78
|
-
const
|
|
78
|
+
const terminalControlObservationMatches = (action, payload) => {
|
|
79
|
+
if (action.type === "resize")
|
|
80
|
+
return (payload["type"] === "resize" &&
|
|
81
|
+
payload["data"] === `${String(action.columns)}x${String(action.rows)}`);
|
|
82
|
+
if (action.type === "close_stdin")
|
|
83
|
+
return payload["type"] === "stdin_close" && payload["data"] === "";
|
|
84
|
+
return payload["type"] === "signal" && payload["data"] === action.signal;
|
|
85
|
+
};
|
|
86
|
+
const effectObservationMatchesAction = (action, observation, committedSensitiveInputs) => {
|
|
79
87
|
const payload = typeof observation.payload === "object" &&
|
|
80
88
|
observation.payload !== null &&
|
|
81
89
|
!Array.isArray(observation.payload)
|
|
@@ -98,14 +106,13 @@ const effectObservationMatchesAction = (action, observation) => {
|
|
|
98
106
|
if (action.type === "send_input")
|
|
99
107
|
return (payload["type"] === "input" &&
|
|
100
108
|
payload["data"] ===
|
|
101
|
-
(action.sensitive
|
|
102
|
-
|
|
103
|
-
: action.data));
|
|
104
|
-
if (action.type === "resize")
|
|
105
|
-
return (payload["type"] === "resize" &&
|
|
106
|
-
payload["data"] === `${String(action.columns)}x${String(action.rows)}`);
|
|
107
|
-
return payload["type"] === "signal" && payload["data"] === action.signal;
|
|
109
|
+
expectedInputEvidence(action.data, action.sensitive, committedSensitiveInputs));
|
|
110
|
+
return terminalControlObservationMatches(action, payload);
|
|
108
111
|
};
|
|
112
|
+
const expectedInputEvidence = (data, sensitive, committedSensitiveInput) => !sensitive ||
|
|
113
|
+
(committedSensitiveInput && /^<redacted-input:\d+-bytes>$/.test(data))
|
|
114
|
+
? data
|
|
115
|
+
: `<redacted-input:${String(Buffer.byteLength(data))}-bytes>`;
|
|
109
116
|
const failedProposal = (proposed, outcome) => ({
|
|
110
117
|
kind: "finished",
|
|
111
118
|
outcome,
|
|
@@ -113,7 +120,7 @@ const failedProposal = (proposed, outcome) => ({
|
|
|
113
120
|
snapshot: { ...proposed.snapshot, status: "finished", outcome },
|
|
114
121
|
});
|
|
115
122
|
/** Commit a proposal only after the host reports every effect as successful. */
|
|
116
|
-
export const commitProcessReactiveProposal = (scenario, proposed, results) => {
|
|
123
|
+
export const commitProcessReactiveProposal = (scenario, proposed, results, options = {}) => {
|
|
117
124
|
if (results.some(({ status }) => status === "target_lost"))
|
|
118
125
|
return failedProposal(proposed, "target_lost");
|
|
119
126
|
const state = scenario.states.find(({ id }) => id === proposed.snapshot.active_state);
|
|
@@ -137,7 +144,7 @@ export const commitProcessReactiveProposal = (scenario, proposed, results) => {
|
|
|
137
144
|
const action = transition.actions[index];
|
|
138
145
|
const effectObservation = result.observation;
|
|
139
146
|
if (action === undefined ||
|
|
140
|
-
!effectObservationMatchesAction(action, effectObservation) ||
|
|
147
|
+
!effectObservationMatchesAction(action, effectObservation, options.committedSensitiveInputs ?? false) ||
|
|
141
148
|
!Number.isSafeInteger(effectObservation.capture_order) ||
|
|
142
149
|
effectObservation.capture_order <= previousOrder ||
|
|
143
150
|
knownEventIds.has(effectObservation.event_id))
|
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import canonicalize from "canonicalize";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { processReactiveScenarioSchema, } from "./processReactiveScenario.js";
|
|
5
6
|
import { replayMachineSchema } from "./replayMachine.js";
|
|
6
7
|
const positiveBudget = z.number().int().positive();
|
|
7
8
|
const timedEventBase = { at_ms: z.number().int().nonnegative() };
|
|
@@ -44,6 +45,23 @@ export const digestProcessCommitment = (value) => {
|
|
|
44
45
|
throw new TypeError("Process capture commitment is not canonical JSON");
|
|
45
46
|
return createHash("sha256").update(serialized).digest("hex");
|
|
46
47
|
};
|
|
48
|
+
const reactiveScenarioCommitment = (scenario) => scenario === null
|
|
49
|
+
? null
|
|
50
|
+
: {
|
|
51
|
+
...scenario,
|
|
52
|
+
states: scenario.states.map((state) => ({
|
|
53
|
+
...state,
|
|
54
|
+
on: state.on.map((transition) => ({
|
|
55
|
+
...transition,
|
|
56
|
+
actions: transition.actions.map((action) => action.type === "send_input" && action.sensitive
|
|
57
|
+
? {
|
|
58
|
+
...action,
|
|
59
|
+
data: `<redacted-input:${String(Buffer.byteLength(action.data))}-bytes>`,
|
|
60
|
+
}
|
|
61
|
+
: action),
|
|
62
|
+
})),
|
|
63
|
+
})),
|
|
64
|
+
};
|
|
47
65
|
/** Build the secret-safe scenario projection committed by Process Capture v4. */
|
|
48
66
|
export const processScenarioCommitment = (scenario, executableSha256) => ({
|
|
49
67
|
...scenario,
|
|
@@ -51,6 +69,7 @@ export const processScenarioCommitment = (scenario, executableSha256) => ({
|
|
|
51
69
|
name,
|
|
52
70
|
scenario.secret_aliases.includes(name) ? "<redacted-secret>" : value,
|
|
53
71
|
])),
|
|
72
|
+
reactive: reactiveScenarioCommitment(scenario.reactive),
|
|
54
73
|
unknown_registry_approved: scenario.unknown_registry_approved === true,
|
|
55
74
|
executable_sha256: executableSha256 ?? null,
|
|
56
75
|
});
|
|
@@ -71,6 +90,7 @@ export const processComparisonContract = (scenario) => ({
|
|
|
71
90
|
normalization: scenario.normalization,
|
|
72
91
|
command_shims: scenario.command_shims,
|
|
73
92
|
replay: scenario.replay,
|
|
93
|
+
reactive: reactiveScenarioCommitment(scenario.reactive),
|
|
74
94
|
});
|
|
75
95
|
/**
|
|
76
96
|
* Boundary schema for one explicitly approved, bounded process experiment.
|
|
@@ -258,6 +278,7 @@ export const processScenarioSchema = z
|
|
|
258
278
|
websocket_messages: [],
|
|
259
279
|
websocket_connections: [],
|
|
260
280
|
}),
|
|
281
|
+
reactive: processReactiveScenarioSchema.nullable().default(null),
|
|
261
282
|
})
|
|
262
283
|
.strict()
|
|
263
284
|
.superRefine((scenario, context) => {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ProviderAdapterError } from "./errors.js";
|
|
2
|
+
/** Provider resources could not be proven closed after bounded cleanup. */
|
|
3
|
+
export class ProviderCleanupError extends ProviderAdapterError {
|
|
4
|
+
cleanupIncomplete = true;
|
|
5
|
+
cleanupResources;
|
|
6
|
+
userMessage = "Provider cleanup could not be fully confirmed. Review the reported local resources before opening another target.";
|
|
7
|
+
constructor(providerId, resources, diagnostics, options) {
|
|
8
|
+
super(providerId, "close_binary", { ...options, diagnostics });
|
|
9
|
+
this.cleanupResources = [...resources];
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import canonicalize from "canonicalize";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { evidenceBundleSchema } from "./evidenceBundle.js";
|
|
4
|
+
const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
5
|
+
const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
|
|
6
|
+
const stableIdSchema = z.string().regex(/^[A-Za-z][A-Za-z0-9._:/-]{0,199}$/u);
|
|
7
|
+
const boundedTextSchema = z.string().trim().min(1).max(4_096);
|
|
8
|
+
export const obligationStatusSchema = z.enum([
|
|
9
|
+
"unowned",
|
|
10
|
+
"characterized",
|
|
11
|
+
"implemented",
|
|
12
|
+
"verified",
|
|
13
|
+
"contradicted",
|
|
14
|
+
"blocked",
|
|
15
|
+
"out-of-scope",
|
|
16
|
+
"unknown",
|
|
17
|
+
]);
|
|
18
|
+
export const obligationLayerSchema = z.enum([
|
|
19
|
+
"cli",
|
|
20
|
+
"protocol",
|
|
21
|
+
"electron",
|
|
22
|
+
"persistence",
|
|
23
|
+
"process",
|
|
24
|
+
"runtime",
|
|
25
|
+
"packaging",
|
|
26
|
+
"native-abi",
|
|
27
|
+
"application",
|
|
28
|
+
"other",
|
|
29
|
+
]);
|
|
30
|
+
export const obligationCaseKindSchema = z.enum([
|
|
31
|
+
"positive",
|
|
32
|
+
"negative",
|
|
33
|
+
"malformed",
|
|
34
|
+
"cancellation",
|
|
35
|
+
"teardown",
|
|
36
|
+
]);
|
|
37
|
+
export const obligationProofAuthoritySchema = z.enum([
|
|
38
|
+
"unit",
|
|
39
|
+
"integration",
|
|
40
|
+
"protocol",
|
|
41
|
+
"renderer",
|
|
42
|
+
"packaged-process",
|
|
43
|
+
"native-abi",
|
|
44
|
+
"live-observation",
|
|
45
|
+
"external",
|
|
46
|
+
]);
|
|
47
|
+
export const obligationOriginalAuthoritySchema = z.enum([
|
|
48
|
+
"static",
|
|
49
|
+
"runtime",
|
|
50
|
+
"process",
|
|
51
|
+
"external",
|
|
52
|
+
]);
|
|
53
|
+
const obligationTargetSchema = z.strictObject({
|
|
54
|
+
artifact_sha256: digestSchema.nullable(),
|
|
55
|
+
application_node_id: z
|
|
56
|
+
.string()
|
|
57
|
+
.regex(/^jag_node_[a-f0-9]{64}$/u)
|
|
58
|
+
.nullable(),
|
|
59
|
+
semantic_node_id: z
|
|
60
|
+
.string()
|
|
61
|
+
.regex(/^jsrg_node_[a-f0-9]{64}$/u)
|
|
62
|
+
.nullable(),
|
|
63
|
+
location: boundedTextSchema,
|
|
64
|
+
});
|
|
65
|
+
const authorityReferenceSchema = z.strictObject({
|
|
66
|
+
evidence_id: evidenceIdSchema,
|
|
67
|
+
authority: z.enum([
|
|
68
|
+
"shipped-artifact",
|
|
69
|
+
"controlled-replay",
|
|
70
|
+
"historical-reference",
|
|
71
|
+
"external-service",
|
|
72
|
+
"analyst-inference",
|
|
73
|
+
]),
|
|
74
|
+
state: z.enum(["candidate", "observed", "reviewed", "unknown"]),
|
|
75
|
+
location: boundedTextSchema,
|
|
76
|
+
});
|
|
77
|
+
const observedCaseSchema = z.strictObject({
|
|
78
|
+
kind: obligationCaseKindSchema,
|
|
79
|
+
evidence_id: evidenceIdSchema,
|
|
80
|
+
location: boundedTextSchema,
|
|
81
|
+
});
|
|
82
|
+
const ownerBindingSchema = z.strictObject({
|
|
83
|
+
module_path: boundedTextSchema,
|
|
84
|
+
symbol: z.string().trim().min(1).max(1_024),
|
|
85
|
+
owner_sha256: digestSchema,
|
|
86
|
+
});
|
|
87
|
+
const parserTypeBindingSchema = z.strictObject({
|
|
88
|
+
parser: boundedTextSchema,
|
|
89
|
+
schema: boundedTextSchema,
|
|
90
|
+
domain_type: boundedTextSchema,
|
|
91
|
+
});
|
|
92
|
+
const fixtureBindingSchema = z.strictObject({
|
|
93
|
+
fixture_id: stableIdSchema,
|
|
94
|
+
case_kind: obligationCaseKindSchema,
|
|
95
|
+
authority: obligationProofAuthoritySchema,
|
|
96
|
+
evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
|
|
97
|
+
});
|
|
98
|
+
const nondeterminismSchema = z.strictObject({
|
|
99
|
+
mode: z.enum(["total-order", "partial-order", "finite-traces"]),
|
|
100
|
+
specification: boundedTextSchema,
|
|
101
|
+
});
|
|
102
|
+
const verifierBindingSchema = z.strictObject({
|
|
103
|
+
verifier_id: stableIdSchema,
|
|
104
|
+
claim_id: stableIdSchema,
|
|
105
|
+
command: boundedTextSchema,
|
|
106
|
+
authority: obligationProofAuthoritySchema,
|
|
107
|
+
status: z.enum(["pass", "fail", "unknown", "unsupported", "skipped"]),
|
|
108
|
+
result_evidence_id: evidenceIdSchema,
|
|
109
|
+
enumerated_obligation_ids: z.array(stableIdSchema).min(1).max(10_000),
|
|
110
|
+
nondeterminism: nondeterminismSchema,
|
|
111
|
+
});
|
|
112
|
+
export const reconstructionObligationManifestBindingSchema = z.strictObject({
|
|
113
|
+
obligation_id: stableIdSchema,
|
|
114
|
+
owner: ownerBindingSchema,
|
|
115
|
+
parser_type: parserTypeBindingSchema.nullable(),
|
|
116
|
+
original_cases: z.array(observedCaseSchema).max(100),
|
|
117
|
+
fixtures: z.array(fixtureBindingSchema).max(100),
|
|
118
|
+
verifier: verifierBindingSchema.nullable(),
|
|
119
|
+
});
|
|
120
|
+
export const reconstructionObligationManifestSchema = z.strictObject({
|
|
121
|
+
schema_version: z.literal(1),
|
|
122
|
+
bindings: z.array(reconstructionObligationManifestBindingSchema).max(10_000),
|
|
123
|
+
contradictions: z
|
|
124
|
+
.array(z.strictObject({
|
|
125
|
+
obligation_id: stableIdSchema,
|
|
126
|
+
evidence_ids: z.array(evidenceIdSchema).min(2).max(100),
|
|
127
|
+
}))
|
|
128
|
+
.max(10_000),
|
|
129
|
+
});
|
|
130
|
+
export const reviewedReconstructionObligationSchema = z.strictObject({
|
|
131
|
+
obligation_id: stableIdSchema,
|
|
132
|
+
obligation_version: z.number().int().min(1),
|
|
133
|
+
title: z.string().trim().min(1).max(500),
|
|
134
|
+
application_layer: obligationLayerSchema,
|
|
135
|
+
family: z.string().trim().min(1).max(100),
|
|
136
|
+
target: obligationTargetSchema,
|
|
137
|
+
required: z.boolean(),
|
|
138
|
+
required_case_kinds: z.array(obligationCaseKindSchema).min(1).max(5),
|
|
139
|
+
required_original_authority: obligationOriginalAuthoritySchema,
|
|
140
|
+
required_fixture_authority: obligationProofAuthoritySchema,
|
|
141
|
+
required_verifier_authority: obligationProofAuthoritySchema,
|
|
142
|
+
requires_parser_type: z.boolean(),
|
|
143
|
+
dependency_obligation_ids: z.array(stableIdSchema).max(1_000),
|
|
144
|
+
residual_unknown_ids: z.array(stableIdSchema).max(1_000),
|
|
145
|
+
unavailable_authority: z.array(boundedTextSchema).max(100),
|
|
146
|
+
required_next_evidence: z.array(boundedTextSchema).max(100),
|
|
147
|
+
disposition: z.enum(["active", "blocked", "out-of-scope"]),
|
|
148
|
+
review_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
|
|
149
|
+
});
|
|
150
|
+
const diagnosticSchema = z.strictObject({
|
|
151
|
+
code: z.enum([
|
|
152
|
+
"duplicate-definition",
|
|
153
|
+
"missing-owner",
|
|
154
|
+
"ambiguous-owner",
|
|
155
|
+
"missing-parser-type",
|
|
156
|
+
"missing-original-case",
|
|
157
|
+
"weak-original-case-authority",
|
|
158
|
+
"missing-case",
|
|
159
|
+
"weak-fixture-authority",
|
|
160
|
+
"missing-verifier",
|
|
161
|
+
"verifier-does-not-enumerate",
|
|
162
|
+
"missing-verifier-result",
|
|
163
|
+
"weak-verifier-authority",
|
|
164
|
+
"verifier-failed",
|
|
165
|
+
"static-only",
|
|
166
|
+
"contradiction",
|
|
167
|
+
"contradiction-evidence-missing",
|
|
168
|
+
"residual-unknown",
|
|
169
|
+
"dependency-missing",
|
|
170
|
+
"dependency-open",
|
|
171
|
+
"unavailable-authority",
|
|
172
|
+
"blocked",
|
|
173
|
+
"out-of-scope",
|
|
174
|
+
]),
|
|
175
|
+
detail: boundedTextSchema,
|
|
176
|
+
});
|
|
177
|
+
export const reconstructionObligationSchema = z.strictObject({
|
|
178
|
+
obligation_id: stableIdSchema,
|
|
179
|
+
obligation_version: z.number().int().min(1),
|
|
180
|
+
title: z.string().trim().min(1).max(500),
|
|
181
|
+
origin: z.enum(["generated", "reviewed"]),
|
|
182
|
+
application_layer: obligationLayerSchema,
|
|
183
|
+
family: z.string().trim().min(1).max(100),
|
|
184
|
+
target: obligationTargetSchema,
|
|
185
|
+
authority_references: z.array(authorityReferenceSchema).min(1).max(1_000),
|
|
186
|
+
source_state: z.enum(["candidate", "observed", "reviewed", "unknown"]),
|
|
187
|
+
observed_cases: z.array(observedCaseSchema).max(100),
|
|
188
|
+
required: z.boolean(),
|
|
189
|
+
required_case_kinds: z.array(obligationCaseKindSchema).min(1).max(5),
|
|
190
|
+
required_original_authority: obligationOriginalAuthoritySchema,
|
|
191
|
+
required_fixture_authority: obligationProofAuthoritySchema,
|
|
192
|
+
required_verifier_authority: obligationProofAuthoritySchema,
|
|
193
|
+
requires_parser_type: z.boolean(),
|
|
194
|
+
dependency_obligation_ids: z.array(stableIdSchema).max(1_000),
|
|
195
|
+
residual_unknown_ids: z.array(stableIdSchema).max(1_000),
|
|
196
|
+
unavailable_authority: z.array(boundedTextSchema).max(100),
|
|
197
|
+
required_next_evidence: z.array(boundedTextSchema).max(100),
|
|
198
|
+
binding: reconstructionObligationManifestBindingSchema.nullable(),
|
|
199
|
+
status: obligationStatusSchema,
|
|
200
|
+
diagnostics: z.array(diagnosticSchema).max(1_000),
|
|
201
|
+
});
|
|
202
|
+
const countBySchema = (key) => z.array(z.strictObject({
|
|
203
|
+
key,
|
|
204
|
+
count: z.number().int().min(0),
|
|
205
|
+
}));
|
|
206
|
+
export const reconstructionObligationLedgerSchema = z.strictObject({
|
|
207
|
+
schema: z.literal("ReconstructionObligationLedger"),
|
|
208
|
+
schema_version: z.literal(1),
|
|
209
|
+
ledger_id: z.string().regex(/^rol_[a-f0-9]{64}$/u),
|
|
210
|
+
closure_digest: digestSchema,
|
|
211
|
+
status: z.enum(["ready", "open", "failed", "unknown"]),
|
|
212
|
+
coverage: z.strictObject({
|
|
213
|
+
status: z.enum(["complete", "partial"]),
|
|
214
|
+
truncated: z.boolean(),
|
|
215
|
+
omitted_count: z.number().int().min(0),
|
|
216
|
+
max_obligations: z.number().int().min(1),
|
|
217
|
+
}),
|
|
218
|
+
summary: z.strictObject({
|
|
219
|
+
total: z.number().int().min(0),
|
|
220
|
+
required: z.number().int().min(0),
|
|
221
|
+
verified: z.number().int().min(0),
|
|
222
|
+
required_open: z.number().int().min(0),
|
|
223
|
+
by_status: countBySchema(obligationStatusSchema),
|
|
224
|
+
by_application_layer: countBySchema(obligationLayerSchema),
|
|
225
|
+
by_evidence_authority: countBySchema(z.enum(["candidate", "observed", "reviewed", "unknown"])),
|
|
226
|
+
}),
|
|
227
|
+
reports: z.strictObject({
|
|
228
|
+
missing_owner_obligation_ids: z.array(stableIdSchema),
|
|
229
|
+
missing_verifier_obligation_ids: z.array(stableIdSchema),
|
|
230
|
+
contradicted_obligation_ids: z.array(stableIdSchema),
|
|
231
|
+
residual_unknown_ids: z.array(stableIdSchema),
|
|
232
|
+
}),
|
|
233
|
+
ownership_graph: z.array(z.strictObject({
|
|
234
|
+
obligation_id: stableIdSchema,
|
|
235
|
+
module_path: boundedTextSchema,
|
|
236
|
+
symbol: z.string().trim().min(1).max(1_024),
|
|
237
|
+
})),
|
|
238
|
+
dependency_graph: z.array(z.strictObject({
|
|
239
|
+
obligation_id: stableIdSchema,
|
|
240
|
+
depends_on_obligation_id: stableIdSchema,
|
|
241
|
+
})),
|
|
242
|
+
obligations: z.array(reconstructionObligationSchema).max(10_000),
|
|
243
|
+
evidence_links: z.array(evidenceIdSchema).max(100_000),
|
|
244
|
+
limitations: z.array(boundedTextSchema).max(1_000),
|
|
245
|
+
});
|
|
246
|
+
export const reconstructionObligationLedgerInputSchema = z.strictObject({
|
|
247
|
+
evidence_bundle: evidenceBundleSchema,
|
|
248
|
+
reviewed_obligations: z
|
|
249
|
+
.array(reviewedReconstructionObligationSchema)
|
|
250
|
+
.max(10_000),
|
|
251
|
+
manifest: reconstructionObligationManifestSchema,
|
|
252
|
+
limits: z.strictObject({
|
|
253
|
+
max_obligations: z.number().int().min(1).max(10_000),
|
|
254
|
+
}),
|
|
255
|
+
page: z.strictObject({
|
|
256
|
+
offset: z.number().int().min(0).max(10_000),
|
|
257
|
+
limit: z.number().int().min(1).max(100),
|
|
258
|
+
}),
|
|
259
|
+
});
|
|
260
|
+
export const reconstructionObligationLedgerPageSchema = z.strictObject({
|
|
261
|
+
...reconstructionObligationLedgerSchema.omit({ obligations: true }).shape,
|
|
262
|
+
page: z.strictObject({
|
|
263
|
+
offset: z.number().int().min(0),
|
|
264
|
+
limit: z.number().int().min(1).max(100),
|
|
265
|
+
total: z.number().int().min(0),
|
|
266
|
+
returned: z.number().int().min(0),
|
|
267
|
+
next_offset: z.number().int().min(0).nullable(),
|
|
268
|
+
}),
|
|
269
|
+
obligations: z.array(reconstructionObligationSchema).max(100),
|
|
270
|
+
});
|
|
271
|
+
/** Parse and canonically serialize one complete reconstruction obligation ledger. */
|
|
272
|
+
export const serializeReconstructionObligationLedger = (input) => {
|
|
273
|
+
const ledger = reconstructionObligationLedgerSchema.parse(input);
|
|
274
|
+
const encoded = canonicalize(ledger);
|
|
275
|
+
if (encoded === undefined)
|
|
276
|
+
throw new TypeError("Reconstruction obligation ledger is not canonical JSON");
|
|
277
|
+
return encoded;
|
|
278
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import canonicalize from "canonicalize";
|
|
3
|
+
import { parseEvidenceBundle } from "./evidenceBundle.js";
|
|
4
|
+
import { readinessFindingOrder } from "./reconstructionReadinessFindingHelpers.js";
|
|
5
|
+
import { READINESS_STAGE_IDS, readinessJourneyFindings, } from "./reconstructionReadinessJourneyFindings.js";
|
|
6
|
+
import { readinessIntegrityFindings } from "./reconstructionReadinessIntegrityFindings.js";
|
|
7
|
+
import { reconstructionReadinessInputSchema, reconstructionReadinessReportSchema, } from "./reconstructionReadinessSchemas.js";
|
|
8
|
+
/** Evaluate one public-contract journey into a deterministic fail-closed report. */
|
|
9
|
+
export const createReconstructionReadinessReport = (inputValue) => {
|
|
10
|
+
const input = reconstructionReadinessInputSchema.parse(inputValue);
|
|
11
|
+
const sourceDigest = digest(sourceValue(input));
|
|
12
|
+
const evidenceIds = new Set(parseEvidenceBundle(input.evidence_bundle).records.map(({ evidence_id: evidenceId }) => evidenceId));
|
|
13
|
+
const findings = [
|
|
14
|
+
...readinessJourneyFindings(input, evidenceIds),
|
|
15
|
+
...readinessIntegrityFindings(input, evidenceIds, sourceDigest),
|
|
16
|
+
].sort(readinessFindingOrder);
|
|
17
|
+
const stages = effectiveStages(input, findings);
|
|
18
|
+
const status = aggregateStatus(stages);
|
|
19
|
+
const semantic = {
|
|
20
|
+
source_digest: sourceDigest,
|
|
21
|
+
status,
|
|
22
|
+
summary: {
|
|
23
|
+
stages: stages.length,
|
|
24
|
+
required_stages: stages.filter(({ required }) => required).length,
|
|
25
|
+
passed_required_stages: stages.filter(({ required, status: stageStatus }) => required && stageStatus === "pass").length,
|
|
26
|
+
findings: findings.length,
|
|
27
|
+
failed_findings: findings.filter(({ status: findingStatus }) => findingStatus === "fail").length,
|
|
28
|
+
unknown_findings: findings.filter(({ status: findingStatus }) => findingStatus === "unknown").length,
|
|
29
|
+
},
|
|
30
|
+
metrics: readinessMetrics(input),
|
|
31
|
+
stages,
|
|
32
|
+
findings,
|
|
33
|
+
evidence_links: [...evidenceIds].sort(),
|
|
34
|
+
snapshot: input,
|
|
35
|
+
};
|
|
36
|
+
const reportDigest = digest({
|
|
37
|
+
schema: "rea.reconstruction-readiness-report/v1",
|
|
38
|
+
...semantic,
|
|
39
|
+
});
|
|
40
|
+
return reconstructionReadinessReportSchema.parse({
|
|
41
|
+
schema: "ReconstructionReadinessReport",
|
|
42
|
+
schema_version: 1,
|
|
43
|
+
report_id: `rr_${reportDigest}`,
|
|
44
|
+
report_digest: reportDigest,
|
|
45
|
+
...semantic,
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
const effectiveStages = (input, findings) => READINESS_STAGE_IDS.map((stageId) => {
|
|
49
|
+
const stage = input.stages.find(({ stage_id: id }) => id === stageId);
|
|
50
|
+
const stageFindings = findings.filter(({ stage_id: id }) => id === stageId);
|
|
51
|
+
const status = stageFindings.some(({ status: findingStatus }) => findingStatus === "fail")
|
|
52
|
+
? "fail"
|
|
53
|
+
: stage !== undefined && stage.status !== "pass"
|
|
54
|
+
? stage.status
|
|
55
|
+
: stageFindings.length > 0
|
|
56
|
+
? "unknown"
|
|
57
|
+
: (stage?.status ?? "fail");
|
|
58
|
+
return stage === undefined
|
|
59
|
+
? {
|
|
60
|
+
stage_id: stageId,
|
|
61
|
+
required: true,
|
|
62
|
+
status,
|
|
63
|
+
capability_issue: null,
|
|
64
|
+
next_action: "Supply the missing conformance stage.",
|
|
65
|
+
evidence_ids: [],
|
|
66
|
+
checks: [],
|
|
67
|
+
}
|
|
68
|
+
: { ...stage, required: true, status };
|
|
69
|
+
});
|
|
70
|
+
const aggregateStatus = (stages) => {
|
|
71
|
+
const required = stages.filter(({ required }) => required);
|
|
72
|
+
if (required.some(({ status }) => status === "fail"))
|
|
73
|
+
return "fail";
|
|
74
|
+
if (required.every(({ status }) => status === "pass"))
|
|
75
|
+
return "pass";
|
|
76
|
+
for (const status of [
|
|
77
|
+
"unknown",
|
|
78
|
+
"truncated",
|
|
79
|
+
"unsupported",
|
|
80
|
+
"skipped",
|
|
81
|
+
])
|
|
82
|
+
if (required.some(({ status: stageStatus }) => stageStatus === status))
|
|
83
|
+
return status;
|
|
84
|
+
return "unknown";
|
|
85
|
+
};
|
|
86
|
+
const readinessMetrics = (input) => {
|
|
87
|
+
const selectedFixtures = input.fixtures.filter((fixture) => input.workflow_candidates.some(({ fixture_id: fixtureId, selected, compatibility }) => fixtureId === fixture.fixture_id &&
|
|
88
|
+
selected &&
|
|
89
|
+
compatibility === "compatible")).length;
|
|
90
|
+
const prerequisiteFailures = input.operation_outcomes.filter(({ call_kind: kind, expected_success: success }) => !success && ["setup", "permission"].includes(kind));
|
|
91
|
+
const localized = input.comparisons.filter(({ deliberate_divergence_ref: expected, divergence_refs: observed }) => expected !== null && observed.includes(expected)).length;
|
|
92
|
+
const deliberate = input.comparisons.filter(({ deliberate_divergence_ref: expected }) => expected !== null).length;
|
|
93
|
+
const falseEquivalence = input.comparisons.filter((comparison) => comparison.verdict === "equivalent" &&
|
|
94
|
+
(comparison.truncated ||
|
|
95
|
+
comparison.unavailable_authority ||
|
|
96
|
+
comparison.unstable ||
|
|
97
|
+
comparison.contradiction_ids.length > 0)).length;
|
|
98
|
+
return {
|
|
99
|
+
first_valid_workflow_selection_rate: ratio(selectedFixtures, input.fixtures.length),
|
|
100
|
+
malformed_or_over_limit_call_rate: ratio(input.operation_outcomes.filter(({ call_kind: kind }) => ["malformed", "over-limit"].includes(kind)).length, input.operation_outcomes.length),
|
|
101
|
+
incompatible_provider_invocations: input.workflow_candidates.filter(({ invoked, compatibility }) => invoked && compatibility !== "compatible").length,
|
|
102
|
+
failed_calls_before_prerequisites: prerequisiteFailures.length,
|
|
103
|
+
recovery_rate: ratio(prerequisiteFailures.filter(({ recovered }) => recovered).length, prerequisiteFailures.length),
|
|
104
|
+
divergence_localization_precision: ratio(localized, deliberate),
|
|
105
|
+
false_equivalence_count: falseEquivalence,
|
|
106
|
+
unowned_obligation_count: input.obligation_ledger.reports.missing_owner_obligation_ids.length,
|
|
107
|
+
cleanup_leak_count: input.cleanup.filter(({ owned_resources_remaining: remaining }) => remaining > 0).length,
|
|
108
|
+
nondeterministic_replay_count: input.replay.deterministic ? 0 : 1,
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
const sourceValue = (input) => ({
|
|
112
|
+
...input,
|
|
113
|
+
replay: { ...input.replay, expected_source_digest: null },
|
|
114
|
+
});
|
|
115
|
+
const ratio = (numerator, denominator) => denominator === 0 ? 1 : numerator / denominator;
|
|
116
|
+
const digest = (value) => {
|
|
117
|
+
const encoded = canonicalize(value);
|
|
118
|
+
if (encoded === undefined)
|
|
119
|
+
throw new TypeError("Reconstruction readiness value is not canonical JSON");
|
|
120
|
+
return createHash("sha256").update(encoded).digest("hex");
|
|
121
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const addMissingEvidenceFindings = (findings, stageId, referencedIds, availableIds) => {
|
|
2
|
+
for (const evidenceId of new Set(referencedIds))
|
|
3
|
+
if (!availableIds.has(evidenceId))
|
|
4
|
+
findings.push(readinessFinding("referenced-evidence-missing", stageId, "unknown", `Referenced Evidence is absent from the bundle: ${evidenceId}.`));
|
|
5
|
+
};
|
|
6
|
+
export const readinessFinding = (...[code, stageId, status, detail, evidenceIds = []]) => ({
|
|
7
|
+
code,
|
|
8
|
+
stage_id: stageId,
|
|
9
|
+
status,
|
|
10
|
+
detail,
|
|
11
|
+
evidence_ids: [...new Set(evidenceIds)].sort(),
|
|
12
|
+
});
|
|
13
|
+
export const readinessFindingOrder = (left, right) => {
|
|
14
|
+
const leftKey = `${left.stage_id}:${left.code}:${left.detail}`;
|
|
15
|
+
const rightKey = `${right.stage_id}:${right.code}:${right.detail}`;
|
|
16
|
+
return leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0;
|
|
17
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { addMissingEvidenceFindings, readinessFinding as finding, } from "./reconstructionReadinessFindingHelpers.js";
|
|
2
|
+
export const readinessIntegrityFindings = (input, evidenceIds, sourceDigest) => [
|
|
3
|
+
...comparisonFindings(input, evidenceIds),
|
|
4
|
+
...contradictionFindings(input, evidenceIds),
|
|
5
|
+
...closureFindings(input, evidenceIds),
|
|
6
|
+
...cleanupFindings(input, evidenceIds),
|
|
7
|
+
...replayFindings(input, evidenceIds, sourceDigest),
|
|
8
|
+
];
|
|
9
|
+
const comparisonFindings = (input, evidenceIds) => {
|
|
10
|
+
const findings = [];
|
|
11
|
+
for (const comparison of input.comparisons) {
|
|
12
|
+
const unsafeEquivalent = comparison.verdict === "equivalent" &&
|
|
13
|
+
(comparison.truncated ||
|
|
14
|
+
comparison.unavailable_authority ||
|
|
15
|
+
comparison.unstable ||
|
|
16
|
+
comparison.contradiction_ids.length > 0);
|
|
17
|
+
if (unsafeEquivalent)
|
|
18
|
+
findings.push(finding("false-equivalence", "compare-authority-candidate", "fail", `${comparison.comparison_id} claims equivalence across incomplete or contradicted Evidence.`, comparison.evidence_ids));
|
|
19
|
+
if (comparison.concurrent &&
|
|
20
|
+
comparison.schedule_semantics === "total-order")
|
|
21
|
+
findings.push(finding("unsafe-concurrency-semantics", "compare-authority-candidate", "fail", `${comparison.comparison_id} uses total-order semantics for concurrent behavior.`, comparison.evidence_ids));
|
|
22
|
+
if (comparison.deliberate_divergence_ref !== null &&
|
|
23
|
+
!comparison.divergence_refs.includes(comparison.deliberate_divergence_ref))
|
|
24
|
+
findings.push(finding("divergence-not-localized", "compare-authority-candidate", "fail", `${comparison.comparison_id} did not localize its deliberate primary divergence.`, comparison.evidence_ids));
|
|
25
|
+
addMissingEvidenceFindings(findings, "compare-authority-candidate", comparison.evidence_ids, evidenceIds);
|
|
26
|
+
}
|
|
27
|
+
return findings;
|
|
28
|
+
};
|
|
29
|
+
const contradictionFindings = (input, evidenceIds) => {
|
|
30
|
+
const findings = [];
|
|
31
|
+
const comparisons = new Map(input.comparisons.map((comparison) => [
|
|
32
|
+
comparison.comparison_id,
|
|
33
|
+
comparison,
|
|
34
|
+
]));
|
|
35
|
+
for (const contradiction of input.contradictions) {
|
|
36
|
+
if (contradiction.declared_sha256 === contradiction.observed_sha256 ||
|
|
37
|
+
new Set(contradiction.evidence_ids).size < 2)
|
|
38
|
+
findings.push(finding("contradiction-proof-incomplete", "preserve-contradictions", "fail", `${contradiction.contradiction_id} must preserve distinct hashes and Evidence records.`, contradiction.evidence_ids));
|
|
39
|
+
for (const comparisonId of contradiction.affected_comparison_ids) {
|
|
40
|
+
const comparison = comparisons.get(comparisonId);
|
|
41
|
+
if (comparison === undefined)
|
|
42
|
+
findings.push(finding("contradiction-comparison-missing", "preserve-contradictions", "unknown", `Contradiction references missing comparison ${comparisonId}.`, contradiction.evidence_ids));
|
|
43
|
+
else if (comparison.verdict === "equivalent" ||
|
|
44
|
+
!comparison.contradiction_ids.includes(contradiction.contradiction_id))
|
|
45
|
+
findings.push(finding("contradiction-equivalence-not-blocked", "preserve-contradictions", "fail", `${comparisonId} does not preserve contradiction ${contradiction.contradiction_id}.`, contradiction.evidence_ids));
|
|
46
|
+
}
|
|
47
|
+
addMissingEvidenceFindings(findings, "preserve-contradictions", contradiction.evidence_ids, evidenceIds);
|
|
48
|
+
}
|
|
49
|
+
return findings;
|
|
50
|
+
};
|
|
51
|
+
const closureFindings = (input, evidenceIds) => {
|
|
52
|
+
const findings = [];
|
|
53
|
+
if (input.obligation_ledger.summary.required === 0)
|
|
54
|
+
findings.push(finding("reconstruction-obligations-empty", "verify-reconstruction-closure", "unknown", "A ready ledger must include at least one required reconstruction obligation."));
|
|
55
|
+
if (input.obligation_ledger.status !== "ready" ||
|
|
56
|
+
input.obligation_ledger.summary.required_open > 0)
|
|
57
|
+
findings.push(finding("reconstruction-closure-open", "verify-reconstruction-closure", "unknown", `Obligation ledger is ${input.obligation_ledger.status} with ${String(input.obligation_ledger.summary.required_open)} required obligations open.`, input.obligation_ledger.evidence_links));
|
|
58
|
+
addMissingEvidenceFindings(findings, "verify-reconstruction-closure", input.obligation_ledger.evidence_links, evidenceIds);
|
|
59
|
+
evaluateClosureHistory(input, findings, evidenceIds);
|
|
60
|
+
for (const check of input.delegation_checks) {
|
|
61
|
+
if (check.delegates_to_authority)
|
|
62
|
+
findings.push(finding("candidate-delegates-to-authority", "verify-reconstruction-closure", "fail", `${check.candidate_id} delegates behavior back to the authority.`, check.evidence_ids));
|
|
63
|
+
addMissingEvidenceFindings(findings, "verify-reconstruction-closure", check.evidence_ids, evidenceIds);
|
|
64
|
+
}
|
|
65
|
+
return findings;
|
|
66
|
+
};
|
|
67
|
+
const evaluateClosureHistory = (input, findings, evidenceIds) => {
|
|
68
|
+
const history = [...input.closure_history].sort((left, right) => left.sequence - right.sequence);
|
|
69
|
+
if (new Set(history.map(({ sequence }) => sequence)).size !== history.length ||
|
|
70
|
+
history.at(-1)?.ledger_digest !== input.obligation_ledger.closure_digest)
|
|
71
|
+
findings.push(finding("closure-history-identity-mismatch", "verify-reconstruction-closure", "fail", "Closure history sequences must be unique and end at the supplied ledger digest."));
|
|
72
|
+
if (history[0]?.required_open === 0 ||
|
|
73
|
+
history.at(-1)?.required_open !== 0 ||
|
|
74
|
+
history.at(-1)?.status !== "ready")
|
|
75
|
+
findings.push(finding("incremental-closure-history-missing", "verify-reconstruction-closure", "unknown", "Closure history must begin partial and end ready with zero required obligations open."));
|
|
76
|
+
for (const [index, observation] of history.entries()) {
|
|
77
|
+
const previous = history[index - 1];
|
|
78
|
+
if (previous !== undefined &&
|
|
79
|
+
(observation.required_open > previous.required_open ||
|
|
80
|
+
observation.newly_verified_obligation_ids.length !==
|
|
81
|
+
previous.required_open - observation.required_open))
|
|
82
|
+
findings.push(finding("invalid-obligation-closure-transition", "verify-reconstruction-closure", "fail", `Closure observation ${String(observation.sequence)} does not match its newly verified obligations.`, observation.evidence_ids));
|
|
83
|
+
if (observation.newly_verified_obligation_ids.some((obligationId) => !input.obligation_ledger.obligations.some(({ obligation_id: candidateId, status }) => candidateId === obligationId && status === "verified")))
|
|
84
|
+
findings.push(finding("closure-obligation-proof-missing", "verify-reconstruction-closure", "fail", `Closure observation ${String(observation.sequence)} names an obligation without verified ledger proof.`, observation.evidence_ids));
|
|
85
|
+
addMissingEvidenceFindings(findings, "verify-reconstruction-closure", observation.evidence_ids, evidenceIds);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const cleanupFindings = (input, evidenceIds) => {
|
|
89
|
+
const findings = [];
|
|
90
|
+
for (const cleanup of input.cleanup) {
|
|
91
|
+
if (cleanup.owned_resources_remaining > 0)
|
|
92
|
+
findings.push(finding("owned-resource-leak", "reactive-scenarios", "fail", `${cleanup.run_id} retained ${String(cleanup.owned_resources_remaining)} owned resources.`, cleanup.diagnostic_evidence_ids));
|
|
93
|
+
if (cleanup.cancelled && cleanup.diagnostic_evidence_ids.length === 0)
|
|
94
|
+
findings.push(finding("cancelled-diagnostic-evidence-missing", "reactive-scenarios", "unknown", `${cleanup.run_id} cancellation retained no diagnostic Evidence.`));
|
|
95
|
+
addMissingEvidenceFindings(findings, "reactive-scenarios", cleanup.diagnostic_evidence_ids, evidenceIds);
|
|
96
|
+
}
|
|
97
|
+
for (const grant of input.grants) {
|
|
98
|
+
if (["denied", "cancelled"].includes(grant.decision) &&
|
|
99
|
+
grant.launched_process)
|
|
100
|
+
findings.push(finding("denied-authority-launched-process", "acquire-authority", "fail", `${grant.grant_id} launched a process after ${grant.decision}.`, grant.evidence_ids));
|
|
101
|
+
addMissingEvidenceFindings(findings, "acquire-authority", grant.evidence_ids, evidenceIds);
|
|
102
|
+
}
|
|
103
|
+
return findings;
|
|
104
|
+
};
|
|
105
|
+
const replayFindings = (input, evidenceIds, sourceDigest) => {
|
|
106
|
+
const findings = [];
|
|
107
|
+
if (!input.replay.deterministic)
|
|
108
|
+
findings.push(finding("replay-nondeterministic", "export-replay", "fail", "Clean replay did not reproduce the same semantic report.", input.replay.evidence_ids));
|
|
109
|
+
if (!input.replay.tamper_detected)
|
|
110
|
+
findings.push(finding("tamper-not-detected", "export-replay", "fail", "The conformance replay did not reject a tampered input.", input.replay.evidence_ids));
|
|
111
|
+
if (!input.replay.stale_input_detected)
|
|
112
|
+
findings.push(finding("stale-input-not-detected", "export-replay", "fail", "The conformance replay did not reject a stale digest.", input.replay.evidence_ids));
|
|
113
|
+
if (input.replay.expected_source_digest !== null &&
|
|
114
|
+
input.replay.expected_source_digest !== sourceDigest)
|
|
115
|
+
findings.push(finding("replay-source-digest-mismatch", "export-replay", "fail", `Expected ${input.replay.expected_source_digest}; observed ${sourceDigest}.`, input.replay.evidence_ids));
|
|
116
|
+
addMissingEvidenceFindings(findings, "export-replay", input.replay.evidence_ids, evidenceIds);
|
|
117
|
+
return findings;
|
|
118
|
+
};
|