mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 09:52:27 +00:00
* feat(evidence): discover missing Evidence links without transferring records An agent could only learn that a retained record's source was absent by reading complete records or by having a downstream analysis refuse. The summary said nothing about links, and complete-record retention was easy to mistake for source closure. - Add detail: links to get_evidence_bundle and CLI inspect-evidence-bundle: paged link rows with retained or missing status and no record payloads, for selected records against the whole session or file. - Add retained-closure traversal, the walk composed workflows require, reporting complete or incomplete. Direct reads make no closure claim. - Count direct and missing direct links in the summary. Fixes #8454 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(evidence): count a repeated Evidence link once in every delivery The Evidence schema does not deduplicate evidence_links, so the summary counted a repeated link twice while link rows and counts reported it once. Both now read one helper for a record's distinct direct links. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(evidence): validate link delivery against the advertised schemas The test never called tools/list, so neither the shared validator nor the MCP client checked its replies against what each profile advertises. A schema that rejected the links result could have passed. Fetch the advertised definition and validate every request and reply against it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
100 lines
3.4 KiB
TypeScript
100 lines
3.4 KiB
TypeScript
import { z } from "zod";
|
|
|
|
import { absoluteLocalPathStringSchema } from "../domain/localPath.js";
|
|
import { bundleComparisonInputSchema } from "../domain/bundleComparison.js";
|
|
|
|
import { prefixedDigestSchema } from "./../domain/digests.js";
|
|
import {
|
|
EVIDENCE_LINK_INSPECTION_LIMIT,
|
|
EVIDENCE_RECORD_SELECTION_LIMIT,
|
|
evidenceLinkStatusFilterSchema,
|
|
evidenceLinkTraversalSchema,
|
|
evidenceRecordFiltersSchema,
|
|
} from "../domain/evidenceRecordSummary.js";
|
|
import { residualUnknownSchema } from "../domain/residualUnknown.js";
|
|
|
|
/** Complete retained Evidence, discovery metadata, a page of complete records, or link membership. */
|
|
export const getEvidenceBundleInputSchema = z.strictObject({
|
|
detail: z
|
|
.enum(["complete", "summary", "records", "links"])
|
|
.default("complete"),
|
|
filters: evidenceRecordFiltersSchema.optional(),
|
|
offset: z
|
|
.int()
|
|
.nonnegative()
|
|
.optional()
|
|
.describe("records or links: first matching row to return; default 0."),
|
|
limit: z
|
|
.int()
|
|
.positive()
|
|
.optional()
|
|
.describe(
|
|
`records: maximum complete records, default ${String(EVIDENCE_RECORD_SELECTION_LIMIT)}. links: maximum link rows, default ${String(EVIDENCE_LINK_INSPECTION_LIMIT)}.`,
|
|
),
|
|
traversal: evidenceLinkTraversalSchema
|
|
.optional()
|
|
.describe(
|
|
"links only: direct (default) reads the selected records' own links; retained-closure also follows retained links.",
|
|
),
|
|
link_status: evidenceLinkStatusFilterSchema
|
|
.optional()
|
|
.describe(
|
|
"links only: rows to return, default missing. Counts always cover every inspected link.",
|
|
),
|
|
});
|
|
|
|
/** Optional document selection for volatile navigation context. */
|
|
export const navigationContextInputSchema = z.strictObject({
|
|
document: z.string().min(1).optional(),
|
|
});
|
|
|
|
/** Explicit reproducible address context query. */
|
|
export const addressContextInputSchema = z.strictObject({
|
|
address: z.string().min(1),
|
|
document: z.string().min(1).optional(),
|
|
});
|
|
|
|
/** Session-owned Evidence bundle import options. */
|
|
export const importEvidenceBundleInputSchema = z.strictObject({
|
|
path: absoluteLocalPathStringSchema.describe(
|
|
"Absolute local filesystem path for the evidence bundle to import; relative paths are rejected.",
|
|
),
|
|
});
|
|
|
|
/** MCP compares files selected on the server host, so require explicit paths. */
|
|
export const bundleComparisonMcpInputSchema =
|
|
bundleComparisonInputSchema.extend({
|
|
left_bundle_path: absoluteLocalPathStringSchema.describe(
|
|
"Absolute local filesystem path to the left Evidence bundle.",
|
|
),
|
|
right_bundle_path: absoluteLocalPathStringSchema.describe(
|
|
"Absolute local filesystem path to the right Evidence bundle.",
|
|
),
|
|
});
|
|
|
|
/** Residual-unknown list filters. */
|
|
export const listUnknownsInputSchema = z.strictObject({
|
|
status: residualUnknownSchema.shape.status.optional(),
|
|
severity: residualUnknownSchema.shape.severity.optional(),
|
|
domain: residualUnknownSchema.shape.domain.optional(),
|
|
offset: z
|
|
.int()
|
|
.min(0)
|
|
.max(Number.MAX_SAFE_INTEGER)
|
|
.default(0)
|
|
.describe("Zero-based offset in the stable filtered unknown ID order."),
|
|
limit: z
|
|
.int()
|
|
.positive()
|
|
.max(Number.MAX_SAFE_INTEGER)
|
|
.optional()
|
|
.describe(
|
|
"Optional maximum number of heads to return. Omit to return every matching head from offset.",
|
|
),
|
|
});
|
|
|
|
/** Exact residual-unknown identity to revalidate. */
|
|
export const verifyUnknownResolutionInputSchema = z.strictObject({
|
|
unknown_id: prefixedDigestSchema("unk"),
|
|
});
|