fix: address MCP resource review feedback

This commit is contained in:
morluto committed 2026-07-29 18:10:45 +00:00
1 parent ab4193c1f4
commit f3b3fb07de
13 files changed
+209 -45

No files matched your search

+3 -3
View File
@@ -671,10 +671,10 @@
"mcp_resource_templates": 11
},
"digests": {
"tools_sha256": "ea2b9be5eb6230a65e25e6b52a9df9b60812aeb43dd08612ac96458cfb5a7f9a",
"prompts_sha256": "2dd61fd4e29657e7faf739ef67be7b1f637ddf446e7d73acc0d2fd1d9f06b43e",
"tools_sha256": "daef46f5c6af3e04bf5adde36468da9a87c862057f8e64ff423b22028f5680c4",
"prompts_sha256": "3886a3bf55a448def79e3eb429e6a56eb2ba075423c5cb1e5747642b389ef8fe",
"resources_sha256": "e0e872276ebf6f88e916ef24930a99eed27ce4c93e494e60820e9bd8cc8497c3",
"combined_sha256": "2c55a0742cca841ba58200cf87d851fdaeae24cfaadcd356d3642c67fd70a2a2",
"combined_sha256": "5b09f9fa9e75e9cd520fa405cb869e1c6bdbbcc576f52d5a334482975ec7b1fe",
"providers_sha256": "918ccd6b8ae442cd5844dcfde55086f458176241c8d166c1e9dd0f242ccd2bb0"
}
}
@@ -1,6 +1,6 @@
{
"schema_version": 1,
"manifest_id": "ecm_8838e70fc89ebcc80d24c387a04491d4c24cbcca03bcecb553b9373627234b7b",
"manifest_id": "ecm_9571e8e6cbe76192d3d6b2b0f3f039aa72d13c45c93278510abf8db4951a8aa8",
"verifier": {
"id": "rea-managed-conformance",
"version": "1"
@@ -14,7 +14,7 @@
"skill_digests": [
{
"skill_id": "reverse-engineer-anything",
"sha256": "bdf7709872d33615788092bad12c5a79977ba257da1f836701cb2918dba00f68"
"sha256": "30e1c7251836d3c9d32d2dc17f5813320b36c22ea4f2516f2c8ee2022d1f76cd"
}
],
"claims": [
+1 -1
View File
@@ -4,7 +4,7 @@ description: Reverse engineer native, managed, Electron/JavaScript, packaged, an
metadata:
version: "23"
tool_count: 114
catalog_digest: "2c55a0742cca841ba58200cf87d851fdaeae24cfaadcd356d3642c67fd70a2a2"
catalog_digest: "5b09f9fa9e75e9cd520fa405cb869e1c6bdbbcc576f52d5a334482975ec7b1fe"
---
# REA
+76 -13
View File
@@ -34,22 +34,46 @@ export const getNavigationContext = async (
input: { readonly document?: string | undefined },
signal?: AbortSignal,
): Promise<Result<JsonValue, AnalysisError>> => {
const args = parameters(input.document);
const [document, address, procedure] = await Promise.all([
input.document === undefined
? analysis.execute("current_document", {}, executionOptions(signal))
: Promise.resolve(input.document),
analysis.execute("current_address", args, executionOptions(signal)),
analysis.execute("current_procedure", args, executionOptions(signal)),
]);
const document =
input.document ??
(await analysis.execute("current_document", {}, executionOptions(signal)));
if (typeof document !== "string" && !document.ok) return document;
const documentName =
typeof document === "string" ? document : document.value.result;
const documentArgs =
typeof documentName === "string" ? parameters(documentName) : {};
const address = await analysis.execute(
"current_address",
documentArgs,
executionOptions(signal),
);
if (!address.ok) return address;
if (!procedure.ok && !isMissingCurrentProcedure(procedure.error))
return procedure;
if (typeof address.value.result !== "string")
return ok({
document: documentName,
address: address.value.result,
procedure: null,
});
const procedure = await analysis.execute(
"resolve_containing_procedure",
parameters(
typeof documentName === "string" ? documentName : undefined,
address.value.result,
),
executionOptions(signal),
);
if (!procedure.ok) {
if (!isMissingCurrentProcedure(procedure.error)) return procedure;
return ok({
document: documentName,
address: address.value.result,
procedure: null,
});
}
return ok({
document: typeof document === "string" ? document : document.value.result,
document: documentName,
address: address.value.result,
procedure: procedure.ok ? procedure.value.result : null,
procedure: containingProcedureName(procedure.value.result),
});
};
@@ -76,6 +100,9 @@ export const inspectAddressContext = async (
),
),
);
for (const result of results) {
if (!result.ok && isRequestLevelFailure(result.error)) return result;
}
const name = facet(results[0], operations[0]);
const procedure = facet(results[1], operations[1]);
const comment = facet(results[2], operations[2]);
@@ -92,7 +119,10 @@ export const inspectAddressContext = async (
bookmarks.state === "available"
? {
state: "available",
value: matchingBookmarks(bookmarks.value, input.address),
value: matchingBookmarks(
bookmarks.value,
resolvedAddress(results[1], input.address),
),
}
: bookmarks,
});
@@ -113,6 +143,39 @@ const facet = (
const isMissingCurrentProcedure = (error: AnalysisError): boolean =>
/(?:no procedure exists|not in a procedure)/iu.test(error.message);
const isRequestLevelFailure = (error: AnalysisError): boolean =>
error._tag === "AnalysisCancelledError" ||
error._tag === "AnalysisTimeoutError" ||
error._tag === "NoBinaryOpenError" ||
error._tag === "PermissionRequiredError";
const containingProcedureName = (value: JsonValue): string | null => {
if (typeof value !== "object" || value === null || Array.isArray(value))
return null;
if (value.found !== true) return null;
const procedure = value.procedure;
if (
typeof procedure !== "object" ||
procedure === null ||
Array.isArray(procedure)
)
return null;
return typeof procedure.name === "string" ? procedure.name : null;
};
const resolvedAddress = (
result: Result<AnalysisExecution, AnalysisError> | undefined,
fallback: string,
): string => {
if (result?.ok !== true) return fallback;
const value = result.value.result;
if (typeof value !== "object" || value === null || Array.isArray(value))
return fallback;
return typeof value.query_address === "string"
? value.query_address
: fallback;
};
const matchingBookmarks = (value: JsonValue, address: string): JsonValue => {
if (!Array.isArray(value)) return [];
return value.filter(
+1
View File
@@ -189,6 +189,7 @@ export class BinarySession
const imported = this.importAnalysisSnapshot(options.snapshot);
if (!imported.ok) return imported;
}
this.resetSnapshotInvalidation();
return ok(target);
}
await this.#drainCalls();
+17 -5
View File
@@ -77,7 +77,9 @@ export abstract class BinarySessionRecords {
"added" | "duplicate",
EvidenceIntegrityError | EvidenceLimitError
> {
return this.#evidence.record(evidence);
const recorded = this.#evidence.record(evidence);
if (recorded.ok && recorded.value === "added") this.#emitSnapshotChanged();
return recorded;
}
hasEvidence(evidenceId: string): boolean {
@@ -138,7 +140,9 @@ export abstract class BinarySessionRecords {
importEvidenceBundle(
bundle: unknown,
): Result<number, EvidenceIntegrityError | EvidenceLimitError> {
return this.#evidence.import(bundle);
const imported = this.#evidence.import(bundle);
if (imported.ok && imported.value > 0) this.#emitSnapshotChanged();
return imported;
}
protected abstract activeAnalysisBinding(): ActiveAnalysisBinding | undefined;
@@ -229,7 +233,9 @@ export abstract class BinarySessionRecords {
}
protected resetSnapshotInvalidation(): void {
if (!this.#snapshotInvalidated) return;
this.#snapshotInvalidated = false;
this.#emitSnapshotChanged();
}
protected clearSnapshot(): void {
@@ -302,21 +308,25 @@ export abstract class BinarySessionRecords {
input: RecordUnknownInput,
): Result<ResidualUnknown, AnalysisError> {
const target = this.activeAnalysisBinding()?.target;
return this.#evidence.recordUnknown(
const recorded = this.#evidence.recordUnknown(
input,
unknownMutationEvidence(target, input),
);
if (recorded.ok) this.#emitSnapshotChanged();
return recorded;
}
recordEvidenceWithUnknown(
evidence: Evidence,
input: RecordUnknownInput,
): Result<ResidualUnknown | null, AnalysisError> {
return this.#evidence.recordWithUnknown(
const recorded = this.#evidence.recordWithUnknown(
evidence,
input,
unknownMutationEvidence(undefined, input),
);
if (recorded.ok) this.#emitSnapshotChanged();
return recorded;
}
updateUnknown(
@@ -338,7 +348,9 @@ export abstract class BinarySessionRecords {
"Registry mutation evidence records analyst intent, not proof of the answer.",
],
});
return this.#evidence.updateUnknown(input, evidence);
const updated = this.#evidence.updateUnknown(input, evidence);
if (updated.ok) this.#emitSnapshotChanged();
return updated;
}
listUnknowns(
+12 -12
View File
@@ -149,9 +149,9 @@ export const PROMPT_CONTRACTS = [
"Build function dossiers and corroborate references and call relationships. Keep indirect or truncated paths unknown.",
},
{
tools: ["export_evidence_bundle", "record_unknown"],
tools: ["snapshot_evidence_bundle", "record_unknown"],
instruction:
"Cite retained Evidence IDs. Record residual unknowns only after separate explicit approval for registry mutation.",
"Snapshot the canonical bundle, copy its opaque URI unchanged, and read that resource before citing retained Evidence IDs. Record residual unknowns only after separate explicit approval for registry mutation.",
},
],
},
@@ -198,9 +198,9 @@ export const PROMPT_CONTRACTS = [
"For JavaScript/Electron versions, reconstruct each approved artifact independently, optionally reconcile separately retained passive runtime Evidence, then compare authenticated graphs with unique-only identity tiers. Keep ambiguous and incomplete matches unknown.",
},
{
tools: ["export_evidence_bundle", "compare_artifacts"],
tools: ["snapshot_evidence_bundle", "compare_artifacts"],
instruction:
"Resolve any supplied manifest IDs to retained inventory Evidence, validate graph commitments, then compare the complete left and right Evidence page sets.",
"Snapshot and read the canonical bundle through its exact resource URI, resolve any supplied manifest IDs to retained inventory Evidence, validate graph commitments, then compare the complete left and right Evidence page sets.",
},
{
tools: ["open_binary", "analyze_function", "compare_functions"],
@@ -241,9 +241,9 @@ export const PROMPT_CONTRACTS = [
},
steps: [
{
tools: ["export_evidence_bundle"],
tools: ["snapshot_evidence_bundle"],
instruction:
"Load the current canonical bundle and verify that every selected comparison Evidence ID is present and compatible with the intended claim.",
"Snapshot the current canonical bundle, copy its opaque URI unchanged, read the resource, and verify that every selected comparison Evidence ID is present and compatible with the intended claim.",
},
{
tools: [
@@ -313,12 +313,12 @@ export const PROMPT_CONTRACTS = [
},
{
tools: [
"export_evidence_bundle",
"snapshot_evidence_bundle",
"capture_process_scenario",
"correlate_static_and_runtime",
],
instruction:
"Reuse a retained capture when supplied. Otherwise capture only after operator policy and per-call approval, then correlate through explicit hypotheses rather than timing or name coincidence.",
"Snapshot and read retained Evidence through the exact resource URI. Reuse a retained capture when supplied. Otherwise capture only after operator policy and per-call approval, then correlate through explicit hypotheses rather than timing or name coincidence.",
},
{
tools: ["record_unknown"],
@@ -354,9 +354,9 @@ export const PROMPT_CONTRACTS = [
"List current heads first and select only active, session-owned unknown IDs. Preserve their exact revision and requirements.",
},
{
tools: ["export_evidence_bundle", "verify_unknown_resolution"],
tools: ["snapshot_evidence_bundle", "verify_unknown_resolution"],
instruction:
"Inspect cited supporting, contradicting, and mutation Evidence, then validate any resolved head against bundle integrity and authority requirements.",
"Snapshot and read the canonical bundle through its exact resource URI, inspect cited supporting, contradicting, and mutation Evidence, then validate any resolved head against bundle integrity and authority requirements.",
},
{
tools: ["update_unknown"],
@@ -399,9 +399,9 @@ export const PROMPT_CONTRACTS = [
"Inspect capture capability, declared effects, and limits before proposing execution. An unavailable capability is not permission to widen policy.",
},
{
tools: ["export_evidence_bundle"],
tools: ["snapshot_evidence_bundle"],
instruction:
"Review any prior capture for unanswered dimensions, truncation, scenario commitments, and descendant settlement before designing a repeat.",
"Snapshot and read the canonical bundle through its exact resource URI, then review any prior capture for unanswered dimensions, truncation, scenario commitments, and descendant settlement before designing a repeat.",
},
{
tools: ["capture_process_scenario"],
+1 -1
View File
@@ -445,7 +445,7 @@ export const SESSION_TOOL_CONTRACTS = [
),
session(
"export_evidence_bundle",
"Return the session's deterministic Evidence v2 bundle, or atomically write it beneath an operator-approved root. Existing files require overwrite: true; records and manifests use canonical byte-stable ordering.",
"Atomically write the session's deterministic Evidence v2 bundle beneath an operator-approved root. Existing files require overwrite: true; records and manifests use canonical byte-stable ordering. For an in-session read, use snapshot_evidence_bundle and read its exact resource URI instead.",
exportEvidenceBundleInputSchema,
),
session(
+1 -1
View File
@@ -30,7 +30,7 @@ interface GeneratedPayload {
}
const GENERATED_PAYLOAD_JSON =
'{"catalog":[{"name":"address_name","analysisOperation":"address_name","title":"Address Name","description":"Resolve the primary analyzed name at a code or data address. Headless providers require an explicit address; GUI providers may default to their current cursor. Null means the provider has no primary name at that address.","kind":"official-proxy","requiresSession":false,"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"document":{"description":"The document name","type":"string"},"address":{"type":"string","description":"A provider-normalized address; default memory uses 0x-prefixed hexadecimal"},"unknown_registry_approved":{"description":"Explicit approval to record typed capability unavailability as a residual unknown","type":"boolean","const":true}},"additionalProperties":false,"examples":[{}]},"outputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"result":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Primary structured result returned by this operation."},"evidence_id":{"type":"string","pattern":"^ev_[a-f0-9]{64}$","description":"Stable identifier of the recorded Evidence v2 observation."},"evidence_uri":{"type":"string","pattern":"^rea:\\\\/\\\\/evidence\\\\/ev_[a-f0-9]{64}$","description":"Session resource URI for the recorded Evidence v2 observation."}},"required":["result","evidence_id","evidence_uri"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"effects":{"mutatesTarget":false,"mutatesSession":true,"writesFilesystem":false,"launchesProcess":false,"accessesNetwork":false,"changesUiState":false,"mayDiscardData":false,"idempotent":true}},{"name":"comment","analysisOperation":"comment","title":"Comment","description":"Read the regular analysis comment at an address, defaulting to the current cursor. This is read-only and returns null when no comment exists; use set_comment to persist a finding.","kind":"official-proxy","requiresSession":false,"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"document":{"description":"The document name","type":"string"},"address":{"type":"string","description":"A provider-normalized address; default memory uses 0x-prefixed hexadecimal"},"unknown_registry_approved":{"description":"Explicit approval to record typed capability unavailability as a residual unknown","type":"boolean","const":true}},"additionalProperties":false,"examples":[{}]},"outputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"result":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Primary structured result returned by this operation."},"evidence_id":{"type":"string","pattern":"^ev_[a-f0-9]{64}$","description":"Stable identifier of the recorded Evidence v2 observation."},"evidence_uri":{"type":"string","pattern":"^rea:\\\\/\\\\/evidence\\\\/ev_[a-f0-9]{64}$","description":"Session resource URI for the recorded Evidence v2 observation."}},"required":["result","evidence_id","evidence_uri"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"effects":{"mutatesTarget":false,"mutatesSession":true,"writesFilesystem":false,"launchesProcess":false,"accessesNetwork":false,"changesUiState":false,"mayDiscardData":false,"idempotent":true}},{"name":"current_address","analysisOperation":"current_address","title":"Current Address","description":"Return Hopper\'s current cursor address for the selected document. Use only for interactive navigation state; prefer explicit addresses in reproducible investigations.","kind":"official-proxy","requiresSession":false,"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"document":{"description":"The document name","type":"string"},"unknown_registry_approved":{"description":"Explicit approval to record typed capability unavailability as a residual unknown","type":"boolean","const":true}},"additionalProperties":false,"examples":[{}]},"outputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"result":{"type":"string","description":"Primary structured result returned by this operation."},"evidence_id":{"type":"string","pattern":"^ev_[a-f0-9]{64}$","description":"Stable identifier of the recorded Evidence v2 observation."},"evidence_uri":{"type":"string","pattern":"^rea:\\\\/\\\\/evidence\\\\/ev_[a-f0-9]{64}$","description":"Session resource URI for the recorded Evidence v2 observation."}},"required":["result","evidence_id","evidence_uri"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"effects":{"mutatesTarget":false,"mutatesSession":true,"writesFilesystem":false,"launchesProcess":false,"accessesNe Line truncated
'{"catalog":[{"name":"address_name","analysisOperation":"address_name","title":"Address Name","description":"Resolve the primary analyzed name at a code or data address. Headless providers require an explicit address; GUI providers may default to their current cursor. Null means the provider has no primary name at that address.","kind":"official-proxy","requiresSession":false,"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"document":{"description":"The document name","type":"string"},"address":{"type":"string","description":"A provider-normalized address; default memory uses 0x-prefixed hexadecimal"},"unknown_registry_approved":{"description":"Explicit approval to record typed capability unavailability as a residual unknown","type":"boolean","const":true}},"additionalProperties":false,"examples":[{}]},"outputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"result":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Primary structured result returned by this operation."},"evidence_id":{"type":"string","pattern":"^ev_[a-f0-9]{64}$","description":"Stable identifier of the recorded Evidence v2 observation."},"evidence_uri":{"type":"string","pattern":"^rea:\\\\/\\\\/evidence\\\\/ev_[a-f0-9]{64}$","description":"Session resource URI for the recorded Evidence v2 observation."}},"required":["result","evidence_id","evidence_uri"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"effects":{"mutatesTarget":false,"mutatesSession":true,"writesFilesystem":false,"launchesProcess":false,"accessesNetwork":false,"changesUiState":false,"mayDiscardData":false,"idempotent":true}},{"name":"comment","analysisOperation":"comment","title":"Comment","description":"Read the regular analysis comment at an address, defaulting to the current cursor. This is read-only and returns null when no comment exists; use set_comment to persist a finding.","kind":"official-proxy","requiresSession":false,"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"document":{"description":"The document name","type":"string"},"address":{"type":"string","description":"A provider-normalized address; default memory uses 0x-prefixed hexadecimal"},"unknown_registry_approved":{"description":"Explicit approval to record typed capability unavailability as a residual unknown","type":"boolean","const":true}},"additionalProperties":false,"examples":[{}]},"outputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"result":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Primary structured result returned by this operation."},"evidence_id":{"type":"string","pattern":"^ev_[a-f0-9]{64}$","description":"Stable identifier of the recorded Evidence v2 observation."},"evidence_uri":{"type":"string","pattern":"^rea:\\\\/\\\\/evidence\\\\/ev_[a-f0-9]{64}$","description":"Session resource URI for the recorded Evidence v2 observation."}},"required":["result","evidence_id","evidence_uri"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"effects":{"mutatesTarget":false,"mutatesSession":true,"writesFilesystem":false,"launchesProcess":false,"accessesNetwork":false,"changesUiState":false,"mayDiscardData":false,"idempotent":true}},{"name":"current_address","analysisOperation":"current_address","title":"Current Address","description":"Return Hopper\'s current cursor address for the selected document. Use only for interactive navigation state; prefer explicit addresses in reproducible investigations.","kind":"official-proxy","requiresSession":false,"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"document":{"description":"The document name","type":"string"},"unknown_registry_approved":{"description":"Explicit approval to record typed capability unavailability as a residual unknown","type":"boolean","const":true}},"additionalProperties":false,"examples":[{}]},"outputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"result":{"type":"string","description":"Primary structured result returned by this operation."},"evidence_id":{"type":"string","pattern":"^ev_[a-f0-9]{64}$","description":"Stable identifier of the recorded Evidence v2 observation."},"evidence_uri":{"type":"string","pattern":"^rea:\\\\/\\\\/evidence\\\\/ev_[a-f0-9]{64}$","description":"Session resource URI for the recorded Evidence v2 observation."}},"required":["result","evidence_id","evidence_uri"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"effects":{"mutatesTarget":false,"mutatesSession":true,"writesFilesystem":false,"launchesProcess":false,"accessesNe Line truncated
const generatedPayload: unknown = JSON.parse(GENERATED_PAYLOAD_JSON);
if (!isGeneratedPayload(generatedPayload))
throw new TypeError("Generated MCP catalog payload is invalid");
+1 -1
View File
@@ -184,7 +184,7 @@ const registerAnalysisSnapshotResource = (
state: "unavailable",
reason: snapshot.error.message,
remediation:
"Open a native target with a concrete analysis profile, or rerun analysis after metadata mutation.",
"Open a native target with a concrete analysis profile. After metadata mutation, call open_binary again for the current target to begin a fresh cache.",
});
return jsonResource(uri.href, {
state: "available",
+43 -6
View File
@@ -8,7 +8,10 @@ import {
createAnalysisExecution,
type AnalysisOperationPort,
} from "../src/application/AnalysisProvider.js";
import { AnalysisProtocolError } from "../src/domain/errors.js";
import {
AnalysisCancelledError,
AnalysisProtocolError,
} from "../src/domain/errors.js";
import { err, ok } from "../src/domain/result.js";
const provider = { id: "fixture", name: "Fixture", version: "1" };
@@ -30,9 +33,19 @@ describe("analysis context queries", () => {
return Promise.resolve(
ok(createAnalysisExecution("0x401000", provider)),
);
if (operation === "current_procedure")
if (operation === "resolve_containing_procedure")
return Promise.resolve(
err(new AnalysisProtocolError("cursor is not in a procedure")),
ok(
createAnalysisExecution(
{
query_address: "0x401000",
found: false,
procedure: null,
reason: "not_in_procedure",
},
provider,
),
),
);
return Promise.resolve(
err(new AnalysisProtocolError(`unexpected ${operation}`)),
@@ -51,6 +64,10 @@ describe("analysis context queries", () => {
operation: "current_document",
parameters: {},
});
expect(calls).toContainEqual({
operation: "resolve_containing_procedure",
parameters: { document: "fixture", address: "0x401000" },
});
});
it("uses an explicit document without passing it to current_document and preserves procedure failures", async () => {
@@ -70,7 +87,7 @@ describe("analysis context queries", () => {
await expect(
getNavigationContext(analysis, { document: "fixture" }),
).resolves.toEqual(err(failure));
expect(calls).toEqual(["current_address", "current_procedure"]);
expect(calls).toEqual(["current_address", "resolve_containing_procedure"]);
});
it("keeps unsupported address facets local and filters bookmarks", async () => {
@@ -90,7 +107,11 @@ describe("analysis context queries", () => {
return Promise.resolve(
ok(
createAnalysisExecution(
{ address: "0x401000", name: "main" },
{
query_address: "0x401000",
found: true,
procedure: { address: "0x401000", name: "main" },
},
provider,
),
),
@@ -119,7 +140,7 @@ describe("analysis context queries", () => {
};
const result = await inspectAddressContext(analysis, {
address: "0x401000",
address: "401000",
});
expect(result).toMatchObject({
ok: true,
@@ -137,4 +158,20 @@ describe("analysis context queries", () => {
});
expect(bookmarkInputs).toEqual([{}]);
});
it("propagates cancellation instead of degrading it to an unavailable facet", async () => {
const cancelled = new AnalysisCancelledError("comment");
const analysis: AnalysisOperationPort = {
execute: (operation) =>
Promise.resolve(
operation === "comment"
? err(cancelled)
: ok(createAnalysisExecution(null, provider)),
),
};
await expect(
inspectAddressContext(analysis, { address: "0x401000" }),
).resolves.toEqual(err(cancelled));
});
});
+5
View File
@@ -295,6 +295,11 @@ describe("binary session", () => {
).toBe(true);
expect(replayCalls).toEqual(["health", "set_address_name", "address_name"]);
expect(replay.exportAnalysisSnapshot().ok).toBe(false);
expect((await replay.open(first)).ok).toBe(true);
expect(replay.exportAnalysisSnapshot()).toMatchObject({
ok: true,
value: { entries: [{ operation: "address_name" }] },
});
await replay.close();
const mismatch = new BinarySession(cacheProvider([]));
+46
View File
@@ -12,6 +12,52 @@ import { createInvestigationWorkspace } from "../src/domain/investigationWorkspa
const provider = { id: "fixture", name: "Fixture", version: "1" };
describe("evidence MCP resources", () => {
it("publishes mutable snapshot changes for every Evidence mutation", () => {
const session = new BinarySession(() => ({
health: () => Promise.resolve(),
execute: () => Promise.resolve(observed(null)),
close: () => Promise.resolve(),
}));
let updates = 0;
session.onAnalysisSnapshotChanged(() => {
updates += 1;
});
const first = createEvidence(undefined, provider, {
operation: "notification_probe",
parameters: {},
result: 1,
});
expect(session.recordEvidence(first)).toEqual({ ok: true, value: "added" });
expect(session.recordEvidence(first)).toEqual({
ok: true,
value: "duplicate",
});
const imported = createEvidence(undefined, provider, {
operation: "import_notification_probe",
parameters: {},
result: 2,
});
expect(
session.importEvidenceBundle(createEvidenceBundle([imported])),
).toEqual({ ok: true, value: 1 });
expect(
session.recordUnknown({
approved: true,
question: "Does mutation publish a snapshot update?",
severity: "low",
domain: "notifications",
supporting_evidence_ids: [first.evidence_id],
contradicting_evidence_ids: [],
required_authority: "controlled-replay",
required_confidence: "observed",
required_environment: null,
recommended_probes: [],
relationships: [],
}).ok,
).toBe(true);
expect(updates).toBe(3);
});
it("retains bundles without eviction and rejects a seventeenth digest", () => {
const session = new BinarySession(() => ({
health: () => Promise.resolve(),