mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
765e809e36
Remove forwarding wrappers and shadow aliases, migrate callers and verification scripts to their owners, and share one exhaustive semantic resource-bound projection.
216 lines
7.1 KiB
TypeScript
216 lines
7.1 KiB
TypeScript
import { z } from "zod";
|
|
|
|
import { emptyArraySchema } from "../emptyArraySchema.js";
|
|
import { evidenceSchema } from "../evidence.js";
|
|
import { javascriptApplicationGraphSchema } from "./javascriptApplicationGraph.js";
|
|
import { JAVASCRIPT_APPLICATION_NODE_KINDS } from "./javascriptApplicationGraphSchemas.js";
|
|
import { prefixedDigestSchema } from "../digests.js";
|
|
|
|
const evidenceIdSchema = prefixedDigestSchema("ev");
|
|
const nodeIdSchema = prefixedDigestSchema("jag_node");
|
|
const textSchema = z.string().min(1);
|
|
|
|
/** Two authenticated application versions and their native observations. */
|
|
export const compareApplicationVersionsInputSchema = z
|
|
.strictObject({
|
|
left: evidenceSchema,
|
|
right: evidenceSchema,
|
|
left_native_observations: z.array(evidenceSchema).default([]),
|
|
right_native_observations: z.array(evidenceSchema).default([]),
|
|
})
|
|
.superRefine((input, context) => {
|
|
if (input.left.evidence_id === input.right.evidence_id)
|
|
context.addIssue({
|
|
code: "custom",
|
|
path: ["right"],
|
|
message: "Application version Evidence must be distinct",
|
|
});
|
|
for (const side of [
|
|
"left_native_observations",
|
|
"right_native_observations",
|
|
] as const) {
|
|
const ids = input[side].map(({ evidence_id: id }) => id);
|
|
if (new Set(ids).size !== ids.length)
|
|
context.addIssue({
|
|
code: "custom",
|
|
path: [side],
|
|
message: "Native observations must be unique on each side",
|
|
});
|
|
}
|
|
});
|
|
|
|
const candidateNodesSchema = z.tuple([nodeIdSchema]).rest(nodeIdSchema);
|
|
const exactMatchSchema = z.strictObject({
|
|
status: z.literal("matched"),
|
|
basis: z.enum([
|
|
"exact-node-identity",
|
|
"exact-content-digest",
|
|
"exact-module-source-digest",
|
|
]),
|
|
confidence: z.literal("exact"),
|
|
candidate_left_node_ids: emptyArraySchema,
|
|
candidate_right_node_ids: emptyArraySchema,
|
|
});
|
|
const sourceMapMatchSchema = z.strictObject({
|
|
status: z.literal("matched"),
|
|
basis: z.literal("source-map-identity"),
|
|
confidence: z.literal("high"),
|
|
candidate_left_node_ids: emptyArraySchema,
|
|
candidate_right_node_ids: emptyArraySchema,
|
|
});
|
|
const inferredMatchSchema = z.strictObject({
|
|
status: z.literal("matched"),
|
|
basis: z.enum(["structural-fingerprint", "semantic-key"]),
|
|
confidence: z.literal("medium"),
|
|
candidate_left_node_ids: emptyArraySchema,
|
|
candidate_right_node_ids: emptyArraySchema,
|
|
});
|
|
const matchedItemMatchSchema = z.union([
|
|
exactMatchSchema,
|
|
sourceMapMatchSchema,
|
|
inferredMatchSchema,
|
|
]);
|
|
const unmatchedItemMatchSchema = z.strictObject({
|
|
status: z.literal("unmatched"),
|
|
basis: z.literal("none"),
|
|
confidence: z.literal("unknown"),
|
|
candidate_left_node_ids: emptyArraySchema,
|
|
candidate_right_node_ids: emptyArraySchema,
|
|
});
|
|
const leftAmbiguousMatchSchema = z.strictObject({
|
|
status: z.literal("ambiguous"),
|
|
basis: z.literal("none"),
|
|
confidence: z.literal("unknown"),
|
|
candidate_left_node_ids: emptyArraySchema,
|
|
candidate_right_node_ids: candidateNodesSchema,
|
|
});
|
|
const rightAmbiguousMatchSchema = z.strictObject({
|
|
status: z.literal("ambiguous"),
|
|
basis: z.literal("none"),
|
|
confidence: z.literal("unknown"),
|
|
candidate_left_node_ids: candidateNodesSchema,
|
|
candidate_right_node_ids: emptyArraySchema,
|
|
});
|
|
|
|
const comparisonItemContextShape = {
|
|
item_id: prefixedDigestSchema("javc_item"),
|
|
node_kind: z.enum(JAVASCRIPT_APPLICATION_NODE_KINDS),
|
|
dimensions: z.array(
|
|
z.enum([
|
|
"content",
|
|
"location",
|
|
"properties",
|
|
"relationships",
|
|
"availability",
|
|
"coverage",
|
|
]),
|
|
),
|
|
evidence_links: z.array(evidenceIdSchema).min(2),
|
|
limitations: z.array(textSchema),
|
|
};
|
|
|
|
const comparisonItemSchema = z.union([
|
|
z.strictObject({
|
|
...comparisonItemContextShape,
|
|
status: z.enum(["unchanged", "changed", "unknown"]),
|
|
left_node_id: nodeIdSchema,
|
|
right_node_id: nodeIdSchema,
|
|
match: matchedItemMatchSchema,
|
|
}),
|
|
z.strictObject({
|
|
...comparisonItemContextShape,
|
|
status: z.enum(["removed", "unknown"]),
|
|
left_node_id: nodeIdSchema,
|
|
right_node_id: z.null(),
|
|
match: unmatchedItemMatchSchema,
|
|
}),
|
|
z.strictObject({
|
|
...comparisonItemContextShape,
|
|
status: z.enum(["added", "unknown"]),
|
|
left_node_id: z.null(),
|
|
right_node_id: nodeIdSchema,
|
|
match: unmatchedItemMatchSchema,
|
|
}),
|
|
z.strictObject({
|
|
...comparisonItemContextShape,
|
|
status: z.literal("unknown"),
|
|
left_node_id: nodeIdSchema,
|
|
right_node_id: z.null(),
|
|
match: leftAmbiguousMatchSchema,
|
|
}),
|
|
z.strictObject({
|
|
...comparisonItemContextShape,
|
|
status: z.literal("unknown"),
|
|
left_node_id: z.null(),
|
|
right_node_id: nodeIdSchema,
|
|
match: rightAmbiguousMatchSchema,
|
|
}),
|
|
]);
|
|
|
|
const comparisonCoverageSchema = z.strictObject({
|
|
left_graph_status: z.enum(["complete", "partial", "unknown", "unavailable"]),
|
|
right_graph_status: z.enum(["complete", "partial", "unknown", "unavailable"]),
|
|
left_graph_omitted_count: z.number().int().min(0).nullable(),
|
|
right_graph_omitted_count: z.number().int().min(0).nullable(),
|
|
status: z.enum(["complete-within-inputs", "partial", "truncated"]),
|
|
});
|
|
|
|
/** Tiered module/entity matching plus its complete cross-version change graph. */
|
|
export const applicationVersionComparisonResultSchema = z
|
|
.strictObject({
|
|
comparison_id: prefixedDigestSchema("javc"),
|
|
left: z.strictObject({
|
|
evidence_id: evidenceIdSchema,
|
|
graph_id: prefixedDigestSchema("jag"),
|
|
root_artifact_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
}),
|
|
right: z.strictObject({
|
|
evidence_id: evidenceIdSchema,
|
|
graph_id: prefixedDigestSchema("jag"),
|
|
root_artifact_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
}),
|
|
summary: z.strictObject({
|
|
unchanged: z.number().int().min(0),
|
|
added: z.number().int().min(0),
|
|
removed: z.number().int().min(0),
|
|
changed: z.number().int().min(0),
|
|
unknown: z.number().int().min(0),
|
|
}),
|
|
matching: z.strictObject({
|
|
exact_node_identity: z.number().int().min(0),
|
|
exact_content_digest: z.number().int().min(0),
|
|
exact_module_source_digest: z.number().int().min(0),
|
|
source_map_identity: z.number().int().min(0),
|
|
structural_fingerprint: z.number().int().min(0),
|
|
semantic_key: z.number().int().min(0),
|
|
ambiguous: z.number().int().min(0),
|
|
unmatched: z.number().int().min(0),
|
|
}),
|
|
items: z.array(comparisonItemSchema),
|
|
graph: javascriptApplicationGraphSchema,
|
|
coverage: comparisonCoverageSchema,
|
|
evidence_links: z.array(evidenceIdSchema).min(2),
|
|
limitations: z.array(textSchema),
|
|
})
|
|
.superRefine((result, context) => {
|
|
const sourceGraphsComplete =
|
|
result.coverage.left_graph_status === "complete" &&
|
|
result.coverage.right_graph_status === "complete";
|
|
if (
|
|
(result.coverage.status === "complete-within-inputs") !==
|
|
sourceGraphsComplete
|
|
)
|
|
context.addIssue({
|
|
code: "custom",
|
|
path: ["coverage", "status"],
|
|
message: "Comparison coverage must match source graph completeness",
|
|
});
|
|
});
|
|
|
|
export type ApplicationVersionComparisonItem = z.infer<
|
|
typeof comparisonItemSchema
|
|
>;
|
|
export type ApplicationVersionComparisonResult = z.infer<
|
|
typeof applicationVersionComparisonResultSchema
|
|
>;
|