mirror of
https://github.com/morluto/rea.git
synced 2026-10-28 18:35:53 +00:00
Require process retention accounting, browser network transaction identity, and all web comparison dimensions. Remove obsolete omission repairs and derive completeness from the owning producer facts. BREAKING CHANGE: Historical captures without current producer accounting or transaction identity, and web diffs missing accessibility or storage dimensions, are rejected.
103 lines
3.3 KiB
TypeScript
103 lines
3.3 KiB
TypeScript
import { z } from "zod";
|
|
|
|
import { emptyArraySchema } from "./emptyArraySchema.js";
|
|
import { webPageInspectionSchema } from "./browserObservationSchemas.js";
|
|
import { webMcpDiscoverySchema } from "./webMcpDiscovery.js";
|
|
|
|
/** One normalized passive page snapshot accepted by capture comparison. */
|
|
export const captureSnapshotSchema = z.object({
|
|
inspection: webPageInspectionSchema.describe(
|
|
"Complete normalized_result from inspect_web_page, including capture identity, observations, completeness, and limitations.",
|
|
),
|
|
webmcp: webMcpDiscoverySchema
|
|
.nullable()
|
|
.default(null)
|
|
.describe(
|
|
"Complete normalized_result from discover_webmcp_tools for this capture, or null when it was not recorded.",
|
|
),
|
|
});
|
|
|
|
/** Input for deterministic comparison of two normalized web captures. */
|
|
export const compareWebCapturesInputSchema = z.strictObject({
|
|
before: captureSnapshotSchema,
|
|
after: captureSnapshotSchema,
|
|
});
|
|
|
|
const changeSchema = z.object({
|
|
identity: z.string(),
|
|
change: z.enum(["added", "removed", "modified"]),
|
|
});
|
|
const emptyDimensionShape = {
|
|
total_changes: z.literal(0),
|
|
changes: emptyArraySchema,
|
|
};
|
|
const dimensionSchema = z.union([
|
|
z
|
|
.object({
|
|
status: z.literal("changed"),
|
|
total_changes: z.number().int().min(1),
|
|
changes: z.array(changeSchema),
|
|
reason: z.null(),
|
|
})
|
|
.superRefine((dimension, context) => {
|
|
if (dimension.changes.length !== dimension.total_changes)
|
|
context.addIssue({
|
|
code: "custom",
|
|
message: "Inline changes must equal total changes",
|
|
path: ["total_changes"],
|
|
});
|
|
}),
|
|
z.object({
|
|
...emptyDimensionShape,
|
|
status: z.literal("unchanged"),
|
|
reason: z.null(),
|
|
}),
|
|
z.object({
|
|
...emptyDimensionShape,
|
|
status: z.literal("unknown"),
|
|
reason: z.string(),
|
|
}),
|
|
]);
|
|
/** Completeness-aware changes across stable browser evidence dimensions. */
|
|
export const webCaptureDiffSchema = z
|
|
.object({
|
|
overall_status: z.enum(["changed", "unchanged", "unknown"]),
|
|
before_target: z.object({ target_id: z.string(), url: z.string() }),
|
|
after_target: z.object({ target_id: z.string(), url: z.string() }),
|
|
dimensions: z.object({
|
|
dom_structure: dimensionSchema,
|
|
scripts: dimensionSchema,
|
|
resources: dimensionSchema,
|
|
network: dimensionSchema,
|
|
metadata: dimensionSchema,
|
|
webmcp: dimensionSchema,
|
|
accessibility: dimensionSchema,
|
|
storage: dimensionSchema,
|
|
}),
|
|
limitations: z.array(z.string()),
|
|
})
|
|
.superRefine((comparison, context) => {
|
|
const statuses = Object.values(comparison.dimensions).map(
|
|
({ status }) => status,
|
|
);
|
|
const expected = statuses.includes("changed")
|
|
? "changed"
|
|
: statuses.includes("unknown")
|
|
? "unknown"
|
|
: "unchanged";
|
|
if (comparison.overall_status !== expected)
|
|
context.addIssue({
|
|
code: "custom",
|
|
message: "Overall status must summarize dimension statuses",
|
|
path: ["overall_status"],
|
|
});
|
|
});
|
|
|
|
export type CompareWebCapturesInput = z.infer<
|
|
typeof compareWebCapturesInputSchema
|
|
>;
|
|
export type WebCaptureDiff = z.infer<typeof webCaptureDiffSchema>;
|
|
export type WebCaptureDimension =
|
|
WebCaptureDiff["dimensions"][keyof WebCaptureDiff["dimensions"]];
|
|
export type WebCaptureChange = WebCaptureDimension["changes"][number];
|