mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
refactor(evidence): centralize comparison identities and source recording
This commit is contained in:
@@ -3,7 +3,6 @@ import {
|
||||
createAnalysisProfile,
|
||||
type AnalysisProfileCommitment,
|
||||
} from "../domain/analysisProfile.js";
|
||||
|
||||
/** Provider identity for deterministic composed analysis workflows. */
|
||||
export const REA_WORKFLOW_PROVIDER = {
|
||||
id: "rea-workflow",
|
||||
@@ -112,20 +111,6 @@ export const JAVASCRIPT_APPLICATION_WORKFLOW_PROVIDER = {
|
||||
version: "1",
|
||||
} as const;
|
||||
|
||||
/** Provider identity for deterministic cross-artifact comparisons. */
|
||||
export const ARTIFACT_COMPARISON_PROVIDER = {
|
||||
id: "rea-artifact-comparison",
|
||||
name: "REA artifact comparison",
|
||||
version: "1",
|
||||
} as const;
|
||||
|
||||
/** Provider identity for deterministic cross-function comparisons. */
|
||||
export const FUNCTION_COMPARISON_PROVIDER = {
|
||||
id: "rea-function-comparison",
|
||||
name: "REA function comparison",
|
||||
version: "1",
|
||||
} as const;
|
||||
|
||||
/** Provider identity for deterministic Evidence bundle comparisons. */
|
||||
export const BUNDLE_COMPARISON_PROVIDER = {
|
||||
id: "rea-bundle-comparison",
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
type EvidenceLookup,
|
||||
} from "../EvidenceInputResolver.js";
|
||||
import type { ExecutionOptions } from "../AnalysisProvider.js";
|
||||
import { FUNCTION_COMPARISON_PROVIDER } from "../InvestigationProviders.js";
|
||||
import { FUNCTION_COMPARISON_PROVIDER } from "../../domain/comparisonEvidenceIdentity.js";
|
||||
|
||||
const OPERATION = "match_native_functions";
|
||||
export interface NativeFunctionMatchingResult {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
wasmInterfaceEntryRepresentationBytes,
|
||||
} from "../../domain/wasm/wasmInterface.js";
|
||||
import { setImmediate } from "node:timers/promises";
|
||||
import { ARTIFACT_COMPARISON_PROVIDER } from "../../domain/comparisonEvidenceIdentity.js";
|
||||
import {
|
||||
compareWasmInterfacesInputSchema,
|
||||
wasmInterfaceComparisonSchema,
|
||||
@@ -26,7 +27,6 @@ import { fixedAllowanceOptions } from "../../domain/fixedAllowanceRemediation.js
|
||||
import { AnalysisError } from "../../domain/analysisErrorBase.js";
|
||||
import { analysisInputErrorFromIssues } from "../../domain/inputIssueProjection.js";
|
||||
import { err, ok, type Result } from "../../domain/result.js";
|
||||
import { ARTIFACT_COMPARISON_PROVIDER } from "../InvestigationProviders.js";
|
||||
import {
|
||||
resolvePairedEvidenceRequest,
|
||||
type EvidenceLookup,
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { Logger } from "pino";
|
||||
import { matchNativeFunctionsFromBundles } from "../application/native/NativeFunctionMatchingService.js";
|
||||
import { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js";
|
||||
import { matchNativeFunctionsInputSchema } from "../contracts/native/functionMatchingContracts.js";
|
||||
import { FUNCTION_COMPARISON_PROVIDER } from "../domain/comparisonEvidenceIdentity.js";
|
||||
import { CLI_COMMANDS } from "../cliCommandNames.js";
|
||||
import { parseCliJsonInput } from "../cliJsonInput.js";
|
||||
import { logCliCommand } from "../cliLogging.js";
|
||||
@@ -66,7 +67,7 @@ export const registerNativeFunctionMatchingCommands = (
|
||||
),
|
||||
);
|
||||
const delivery = {
|
||||
providerId: "rea-function-comparison",
|
||||
providerId: FUNCTION_COMPARISON_PROVIDER.id,
|
||||
operation: "match_native_functions",
|
||||
signal,
|
||||
detail: parsed.data.detail,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createEvidence } from "../domain/evidence.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
import { FUNCTION_COMPARISON_PROVIDER } from "../domain/comparisonEvidenceIdentity.js";
|
||||
import { compareProcessCaptures } from "../domain/process/processComparison.js";
|
||||
import { parseProcessCapture } from "../domain/process/processCaptureParsing.js";
|
||||
import { FUNCTION_COMPARISON_EXAMPLE } from "./functionComparisonExample.js";
|
||||
@@ -13,11 +14,7 @@ const comparison = compareFunctions(
|
||||
|
||||
export const FUNCTION_COMPARISON_EVIDENCE = createEvidence(
|
||||
undefined,
|
||||
{
|
||||
id: "rea-function-comparison",
|
||||
name: "REA function comparison",
|
||||
version: "1",
|
||||
},
|
||||
FUNCTION_COMPARISON_PROVIDER,
|
||||
{
|
||||
predicateType: "rea.function-comparison",
|
||||
operation: "compare_functions",
|
||||
|
||||
@@ -11,11 +11,11 @@ import {
|
||||
comparisonSourceEvidenceIds,
|
||||
} from "./comparisonSourceEvidence.js";
|
||||
import { evidenceSchema, parseEvidence, type Evidence } from "./evidence.js";
|
||||
import { functionComparisonResultSchema } from "./functionComparisonSchemas.js";
|
||||
import {
|
||||
PROCESS_PROVIDER,
|
||||
isProcessEvidenceProvider,
|
||||
} from "./process/processEvidenceProvider.js";
|
||||
hasComparisonEvidenceIdentity,
|
||||
isComparisonEvidenceOperation,
|
||||
} from "./comparisonEvidenceIdentity.js";
|
||||
import { functionComparisonResultSchema } from "./functionComparisonSchemas.js";
|
||||
import {
|
||||
comparisonStatusSchema,
|
||||
deriveProcessComparisonStatus,
|
||||
@@ -111,30 +111,6 @@ type ParsedComparison =
|
||||
readonly result: z.infer<typeof artifactComparisonResultSchema>;
|
||||
};
|
||||
|
||||
const EXPECTED_PROVIDERS = {
|
||||
compare_process_captures: {
|
||||
predicates: ["rea.process-comparison"],
|
||||
...PROCESS_PROVIDER,
|
||||
},
|
||||
compare_artifacts: {
|
||||
predicates: ["rea.artifact-comparison"],
|
||||
id: "rea-artifact-comparison",
|
||||
name: "REA artifact comparison",
|
||||
version: "1",
|
||||
},
|
||||
compare_functions: {
|
||||
predicates: ["rea.function-comparison"],
|
||||
id: "rea-function-comparison",
|
||||
name: "REA function comparison",
|
||||
version: "1",
|
||||
},
|
||||
} as const;
|
||||
|
||||
const acceptsPredicate = (
|
||||
expected: readonly string[],
|
||||
actual: string,
|
||||
): boolean => expected.some((predicate) => predicate === actual);
|
||||
|
||||
/** Find observed runtime changes and separately report static change candidates. */
|
||||
interface FoundChangedBehavior {
|
||||
readonly result: ChangedBehaviorResult;
|
||||
@@ -202,29 +178,12 @@ export const findChangedBehavior = (input: unknown): FoundChangedBehavior => {
|
||||
|
||||
const parseComparisonEvidence = (input: unknown): ParsedComparison => {
|
||||
const evidence = parseEvidence(input);
|
||||
if (!(evidence.operation in EXPECTED_PROVIDERS))
|
||||
if (!isComparisonEvidenceOperation(evidence.operation))
|
||||
throw new TypeError(
|
||||
"Changed-behavior analysis requires process, artifact, or function comparison Evidence",
|
||||
);
|
||||
const operation = z
|
||||
.enum([
|
||||
"compare_process_captures",
|
||||
"compare_artifacts",
|
||||
"compare_functions",
|
||||
])
|
||||
.parse(evidence.operation);
|
||||
const expected = EXPECTED_PROVIDERS[operation];
|
||||
if (
|
||||
!acceptsPredicate(expected.predicates, evidence.predicate_type) ||
|
||||
(operation === "compare_process_captures"
|
||||
? !isProcessEvidenceProvider(evidence.provider)
|
||||
: evidence.provider.id !== expected.id ||
|
||||
evidence.provider.name !== expected.name ||
|
||||
evidence.provider.version !== expected.version) ||
|
||||
evidence.confidence !== "derived" ||
|
||||
evidence.authority !== "analyst-inference" ||
|
||||
evidence.subject !== null
|
||||
)
|
||||
const operation = evidence.operation;
|
||||
if (!hasComparisonEvidenceIdentity(evidence))
|
||||
throw new TypeError(
|
||||
"Comparison Evidence operation, predicate, and provider disagree",
|
||||
);
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { Evidence } from "./evidence.js";
|
||||
import {
|
||||
PROCESS_PROVIDER,
|
||||
isProcessEvidenceProvider,
|
||||
} from "./process/processEvidenceProvider.js";
|
||||
|
||||
/** Canonical provider identity for deterministic artifact comparisons. */
|
||||
export const ARTIFACT_COMPARISON_PROVIDER = {
|
||||
id: "rea-artifact-comparison",
|
||||
name: "REA artifact comparison",
|
||||
version: "1",
|
||||
} as const;
|
||||
|
||||
/** Canonical provider identity for deterministic function comparisons. */
|
||||
export const FUNCTION_COMPARISON_PROVIDER = {
|
||||
id: "rea-function-comparison",
|
||||
name: "REA function comparison",
|
||||
version: "1",
|
||||
} as const;
|
||||
|
||||
const COMPARISON_EVIDENCE_IDENTITIES = {
|
||||
compare_artifacts: {
|
||||
predicate: "rea.artifact-comparison",
|
||||
provider: ARTIFACT_COMPARISON_PROVIDER,
|
||||
},
|
||||
compare_functions: {
|
||||
predicate: "rea.function-comparison",
|
||||
provider: FUNCTION_COMPARISON_PROVIDER,
|
||||
},
|
||||
compare_process_captures: {
|
||||
predicate: "rea.process-comparison",
|
||||
provider: PROCESS_PROVIDER,
|
||||
},
|
||||
} as const;
|
||||
|
||||
type ComparisonEvidenceOperation =
|
||||
keyof typeof COMPARISON_EVIDENCE_IDENTITIES;
|
||||
|
||||
/** Whether an operation is one of the comparison workflows this domain owns. */
|
||||
export const isComparisonEvidenceOperation = (
|
||||
operation: string,
|
||||
): operation is ComparisonEvidenceOperation =>
|
||||
Object.hasOwn(COMPARISON_EVIDENCE_IDENTITIES, operation);
|
||||
|
||||
/** Check shared Evidence identity without imposing a consumer's link policy. */
|
||||
export const hasComparisonEvidenceIdentity = (
|
||||
evidence: Evidence,
|
||||
): boolean => {
|
||||
if (!isComparisonEvidenceOperation(evidence.operation)) return false;
|
||||
const expected = COMPARISON_EVIDENCE_IDENTITIES[evidence.operation];
|
||||
const providerMatches =
|
||||
evidence.operation === "compare_process_captures"
|
||||
? isProcessEvidenceProvider(evidence.provider)
|
||||
: evidence.provider.id === expected.provider.id &&
|
||||
evidence.provider.name === expected.provider.name &&
|
||||
evidence.provider.version === expected.provider.version;
|
||||
return (
|
||||
evidence.predicate_type === expected.predicate &&
|
||||
providerMatches &&
|
||||
evidence.confidence === "derived" &&
|
||||
evidence.authority === "analyst-inference" &&
|
||||
evidence.subject === null
|
||||
);
|
||||
};
|
||||
@@ -12,12 +12,9 @@ import {
|
||||
assertComparisonFindingLinks,
|
||||
} from "./comparisonSourceEvidence.js";
|
||||
import { parseEvidence, type Evidence } from "./evidence.js";
|
||||
import { hasComparisonEvidenceIdentity } from "./comparisonEvidenceIdentity.js";
|
||||
import { parseEvidenceBundle } from "./evidenceBundle.js";
|
||||
import { functionComparisonResultSchema } from "./functionComparisonSchemas.js";
|
||||
import {
|
||||
PROCESS_PROVIDER,
|
||||
isProcessEvidenceProvider,
|
||||
} from "./process/processEvidenceProvider.js";
|
||||
import { functionDossierSchema } from "./hopperValues.js";
|
||||
import { assessFunctionCallCoverage } from "./functionCallCoverage.js";
|
||||
import { processCaptureComparisonSchema } from "./process/processComparison.js";
|
||||
@@ -38,24 +35,10 @@ import {
|
||||
} from "./reconstructionUnknowns.js";
|
||||
import type { ResidualUnknown } from "./residualUnknown.js";
|
||||
|
||||
const providers = {
|
||||
behavioral: {
|
||||
operation: "compare_process_captures",
|
||||
predicates: ["rea.process-comparison"],
|
||||
...PROCESS_PROVIDER,
|
||||
},
|
||||
"structural-function": {
|
||||
operation: "compare_functions",
|
||||
predicates: ["rea.function-comparison"],
|
||||
id: "rea-function-comparison",
|
||||
name: "REA function comparison",
|
||||
},
|
||||
"structural-artifact": {
|
||||
operation: "compare_artifacts",
|
||||
predicates: ["rea.artifact-comparison"],
|
||||
id: "rea-artifact-comparison",
|
||||
name: "REA artifact comparison",
|
||||
},
|
||||
const comparisonOperationsByClaimKind = {
|
||||
behavioral: "compare_process_captures",
|
||||
"structural-function": "compare_functions",
|
||||
"structural-artifact": "compare_artifacts",
|
||||
} as const;
|
||||
|
||||
interface FunctionSourceFacts {
|
||||
@@ -64,11 +47,6 @@ interface FunctionSourceFacts {
|
||||
readonly callCoverageDiagnostics: readonly string[];
|
||||
}
|
||||
|
||||
const acceptsPredicate = (
|
||||
expected: readonly string[],
|
||||
actual: string,
|
||||
): boolean => expected.some((predicate) => predicate === actual);
|
||||
|
||||
/** Verify only explicitly declared claims; no global equivalence is inferred. */
|
||||
export const verifyReconstruction = (
|
||||
specificationInput: unknown,
|
||||
@@ -234,18 +212,10 @@ const readValidatedSourceFacts = (
|
||||
|
||||
const validateComparisonIdentity = (claim: Claim, evidence: Evidence): void => {
|
||||
parseEvidence(evidence);
|
||||
const expected = providers[claim.kind];
|
||||
const expectedOperation = comparisonOperationsByClaimKind[claim.kind];
|
||||
if (
|
||||
evidence.operation !== expected.operation ||
|
||||
!acceptsPredicate(expected.predicates, evidence.predicate_type) ||
|
||||
(claim.kind === "behavioral"
|
||||
? !isProcessEvidenceProvider(evidence.provider)
|
||||
: evidence.provider.id !== expected.id ||
|
||||
evidence.provider.name !== expected.name ||
|
||||
evidence.provider.version !== "1") ||
|
||||
evidence.confidence !== "derived" ||
|
||||
evidence.authority !== "analyst-inference" ||
|
||||
evidence.subject !== null
|
||||
evidence.operation !== expectedOperation ||
|
||||
!hasComparisonEvidenceIdentity(evidence)
|
||||
)
|
||||
throw new TypeError("Comparison Evidence identity or authority disagrees");
|
||||
};
|
||||
|
||||
@@ -11,11 +11,8 @@ import {
|
||||
assertComparisonFindingLinks,
|
||||
} from "./comparisonSourceEvidence.js";
|
||||
import { evidenceSchema, parseEvidence, type Evidence } from "./evidence.js";
|
||||
import { hasComparisonEvidenceIdentity } from "./comparisonEvidenceIdentity.js";
|
||||
import { functionComparisonResultSchema } from "./functionComparisonSchemas.js";
|
||||
import {
|
||||
PROCESS_PROVIDER,
|
||||
isProcessEvidenceProvider,
|
||||
} from "./process/processEvidenceProvider.js";
|
||||
import type { JsonValue } from "./jsonValue.js";
|
||||
import {
|
||||
comparisonStatusSchema,
|
||||
@@ -145,26 +142,6 @@ type Mapping = z.infer<typeof mappingSchema>;
|
||||
type CorrelationItem = z.infer<typeof correlationItemSchema>;
|
||||
type ComparisonStatus = z.infer<typeof comparisonStatusSchema>;
|
||||
|
||||
type ComparisonIdentity = readonly [string | readonly string[], string, string];
|
||||
|
||||
const EXPECTED: Readonly<Record<string, ComparisonIdentity>> = {
|
||||
compare_artifacts: [
|
||||
"rea.artifact-comparison",
|
||||
"rea-artifact-comparison",
|
||||
"REA artifact comparison",
|
||||
],
|
||||
compare_functions: [
|
||||
"rea.function-comparison",
|
||||
"rea-function-comparison",
|
||||
"REA function comparison",
|
||||
],
|
||||
compare_process_captures: [
|
||||
"rea.process-comparison",
|
||||
PROCESS_PROVIDER.id,
|
||||
PROCESS_PROVIDER.name,
|
||||
],
|
||||
};
|
||||
|
||||
/** Correlate only caller-declared static/runtime hypotheses. */
|
||||
export const correlateStaticAndRuntime = (
|
||||
input: unknown,
|
||||
@@ -381,21 +358,7 @@ const parseRuntime = (input: unknown): Evidence => {
|
||||
};
|
||||
|
||||
const assertComparisonIdentity = (evidence: Evidence): void => {
|
||||
const expected = EXPECTED[evidence.operation];
|
||||
if (
|
||||
expected === undefined ||
|
||||
!(Array.isArray(expected[0])
|
||||
? expected[0].includes(evidence.predicate_type)
|
||||
: evidence.predicate_type === expected[0]) ||
|
||||
(evidence.operation === "compare_process_captures"
|
||||
? !isProcessEvidenceProvider(evidence.provider)
|
||||
: evidence.provider.id !== expected[1] ||
|
||||
evidence.provider.name !== expected[2] ||
|
||||
evidence.provider.version !== "1") ||
|
||||
evidence.confidence !== "derived" ||
|
||||
evidence.authority !== "analyst-inference" ||
|
||||
evidence.subject !== null
|
||||
)
|
||||
if (!hasComparisonEvidenceIdentity(evidence))
|
||||
throw new TypeError(
|
||||
"Comparison Evidence operation, predicate, provider, or citations disagree",
|
||||
);
|
||||
|
||||
@@ -3,16 +3,17 @@ import type { EvidenceMcpServer } from "./EvidenceMcpServer.js";
|
||||
import type { BinarySessionPort } from "../application/binary/BinarySessionPort.js";
|
||||
import { toolContract } from "../contracts/toolContracts.js";
|
||||
import { compareArtifacts } from "../domain/artifactComparison.js";
|
||||
import { ARTIFACT_COMPARISON_PROVIDER } from "../domain/comparisonEvidenceIdentity.js";
|
||||
import { createEvidence, parseEvidence } from "../domain/evidence.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
import { resolveArtifactComparisonEvidence } from "../application/artifacts/ArtifactComparisonEvidence.js";
|
||||
import type { RecordUnknownInput } from "../domain/residualUnknown.js";
|
||||
import { recordDerivedEvidence } from "./recordDerivedEvidence.js";
|
||||
import { runDerivedOperation } from "./runDerivedOperation.js";
|
||||
import { ARTIFACT_COMPARISON_PROVIDER } from "../application/InvestigationProviders.js";
|
||||
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
|
||||
import { withAdmittedAnalysis } from "./analysisAdmission.js";
|
||||
import { runAdmittedToolOperation } from "./admittedToolOperation.js";
|
||||
import { recordSessionEvidenceSources } from "./sessionEvidence.js";
|
||||
|
||||
/** Register Evidence-backed deterministic artifact comparison. */
|
||||
export const registerArtifactComparisonTool = (
|
||||
@@ -58,12 +59,12 @@ export const registerArtifactComparisonTool = (
|
||||
if (!computed.ok)
|
||||
return server.delivery.toCallToolResult(computed, contract);
|
||||
const { left, right, comparison } = computed.value;
|
||||
const sources = [left, right];
|
||||
for (const source of sources) {
|
||||
const recordedSource = session.recordEvidence(source);
|
||||
if (!recordedSource.ok)
|
||||
return server.delivery.toCallToolResult(recordedSource, contract);
|
||||
}
|
||||
const recordedSources = recordSessionEvidenceSources(
|
||||
(source) => session.recordEvidence(source),
|
||||
[left, right],
|
||||
);
|
||||
if (!recordedSources.ok)
|
||||
return server.delivery.toCallToolResult(recordedSources, contract);
|
||||
const leftEvidenceIds = [left.evidence_id];
|
||||
const rightEvidenceIds = [right.evidence_id];
|
||||
const evidence = createEvidence(
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
parseEvidence,
|
||||
type Evidence,
|
||||
} from "../domain/evidence.js";
|
||||
import { FUNCTION_COMPARISON_PROVIDER } from "../domain/comparisonEvidenceIdentity.js";
|
||||
import { EvidenceIntegrityError } from "../domain/evidenceErrors.js";
|
||||
import { err } from "../domain/result.js";
|
||||
import { jsonValueSchema } from "../domain/jsonValue.js";
|
||||
@@ -15,7 +16,6 @@ import type { RecordUnknownInput } from "../domain/residualUnknown.js";
|
||||
import { recordDerivedEvidence } from "./recordDerivedEvidence.js";
|
||||
import { recordSessionEvidenceSources } from "./sessionEvidence.js";
|
||||
import { runDerivedOperation } from "./runDerivedOperation.js";
|
||||
import { FUNCTION_COMPARISON_PROVIDER } from "../application/InvestigationProviders.js";
|
||||
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
|
||||
import { withAdmittedAnalysis } from "./analysisAdmission.js";
|
||||
import { runAdmittedToolOperation } from "./admittedToolOperation.js";
|
||||
|
||||
Reference in New Issue
Block a user