fix: require exact capability evidence match

This commit is contained in:
lda
2026-08-11 20:59:58 +07:00 Verified
parent ecf72a8603
commit 5549978a29
2 changed files with 246 additions and 29 deletions
@@ -65,13 +65,19 @@ const capabilityEvidence = (
qualifiedName: string,
durationMs: number,
evidenceTarget = target,
payload: Record<string, unknown> = {},
deploymentId: string | null = null,
): EvidenceRecord => ({
id,
target: evidenceTarget,
operation: "workflow.capabilities.call",
label: "Call capability",
equivalentCli: "wf capability call",
request: { qualified_name: qualifiedName, payload: {} },
request: {
qualified_name: qualifiedName,
payload,
...(deploymentId === null ? {} : { deployment_id: deploymentId }),
},
response: {},
durationMs,
});
@@ -303,7 +309,14 @@ describe("CapabilityPlayground", () => {
mockedUseCapabilityPlayground.mockReturnValue(activeController);
mockedUseConsoleWorkspace.mockReturnValue(
workspaceWithEvidence([
capabilityEvidence("call-submitted", wrapperCapability.name, 18),
capabilityEvidence(
"call-submitted",
wrapperCapability.name,
18,
target,
{ query: "README.md" },
"docs.default",
),
]),
);
view.rerender(
@@ -354,7 +367,11 @@ describe("CapabilityPlayground", () => {
});
mockedUseCapabilityPlayground.mockReturnValue(activeController);
mockedUseConsoleWorkspace.mockReturnValue(
workspaceWithEvidence([capabilityEvidence("call-output", nodeCapability.name, 18)]),
workspaceWithEvidence([
capabilityEvidence("call-output", nodeCapability.name, 18, target, {
query: "README.md",
}),
]),
);
view.rerender(
<CapabilityPlayground
@@ -399,7 +416,9 @@ describe("CapabilityPlayground", () => {
mockedUseConsoleWorkspace.mockReturnValue(
workspaceWithEvidence([
capabilityEvidence("call-1", "local.documents.other", 18),
capabilityEvidence("call-2", nodeCapability.name, 24),
capabilityEvidence("call-2", nodeCapability.name, 24, target, {
query: "README.md",
}),
]),
);
view.rerender(
@@ -432,7 +451,9 @@ describe("CapabilityPlayground", () => {
mockedUseConsoleWorkspace.mockReturnValue(
workspaceWithEvidence([
capabilityEvidence("call-1", "local.documents.other", 18),
capabilityEvidence("call-runtime", nodeCapability.name, 31),
capabilityEvidence("call-runtime", nodeCapability.name, 31, target, {
query: "README.md",
}),
]),
);
view.rerender(
@@ -493,7 +514,9 @@ describe("CapabilityPlayground", () => {
mockedUseConsoleWorkspace.mockReturnValue(
workspaceWithEvidence([
capabilityEvidence("call-1", "local.documents.other", 18),
capabilityEvidence("call-clear", nodeCapability.name, 22),
capabilityEvidence("call-clear", nodeCapability.name, 22, target, {
query: "README.md",
}),
]),
);
rerender(
@@ -531,7 +554,9 @@ describe("CapabilityPlayground", () => {
mockedUseCapabilityPlayground.mockReturnValue(activeController);
mockedUseConsoleWorkspace.mockReturnValue(
workspaceWithEvidence([
capabilityEvidence("call-submitted", nodeCapability.name, 24),
capabilityEvidence("call-submitted", nodeCapability.name, 24, target, {
query: "README.md",
}),
capabilityEvidence("call-interleaved", "local.documents.other", 99),
]),
);
@@ -547,11 +572,140 @@ describe("CapabilityPlayground", () => {
expect(screen.queryByText("99 ms")).not.toBeInTheDocument();
});
it("matches the exact submitted payload among same-target calls", async () => {
let activeController = controller({ acknowledged: true });
mockedUseCapabilityPlayground.mockImplementation(() => activeController);
const view = renderPlayground();
await submitNodeCall();
activeController = controller({
phase: "result",
acknowledged: true,
result: callResult(),
});
mockedUseCapabilityPlayground.mockReturnValue(activeController);
mockedUseConsoleWorkspace.mockReturnValue(
workspaceWithEvidence([
capabilityEvidence("call-payload-match", nodeCapability.name, 24, target, {
query: "README.md",
}),
capabilityEvidence("call-payload-other", nodeCapability.name, 99, target, {
query: "CHANGELOG.md",
}),
]),
);
view.rerender(
<CapabilityPlayground
capability={nodeCapability}
executor={writeExecutor}
target={target}
/>,
);
expect(screen.getByText("24 ms")).toBeInTheDocument();
expect(screen.queryByText("99 ms")).not.toBeInTheDocument();
});
it("matches the normalized submitted deployment", async () => {
let activeController = controller({
acknowledged: true,
deploymentId: " docs.default ",
});
mockedUseCapabilityPlayground.mockImplementation(() => activeController);
const view = renderPlayground(wrapperCapability);
const user = userEvent.setup();
await user.click(screen.getByRole("tab", { name: "Try capability" }));
await user.type(screen.getByRole("textbox", { name: "Query" }), "README.md");
await user.click(screen.getByRole("button", { name: "Call capability" }));
activeController = controller({
phase: "result",
acknowledged: true,
deploymentId: " docs.default ",
result: callResult({
qualifiedName: wrapperCapability.name,
sourceId: wrapperCapability.sourceId,
kind: wrapperCapability.kind,
deploymentId: "docs.default",
}),
});
mockedUseCapabilityPlayground.mockReturnValue(activeController);
mockedUseConsoleWorkspace.mockReturnValue(
workspaceWithEvidence([
capabilityEvidence(
"call-deployment-match",
wrapperCapability.name,
27,
target,
{ query: "README.md" },
"docs.default",
),
capabilityEvidence(
"call-deployment-other",
wrapperCapability.name,
99,
target,
{ query: "README.md" },
"docs.other",
),
]),
);
view.rerender(
<CapabilityPlayground
capability={wrapperCapability}
executor={writeExecutor}
target={target}
/>,
);
expect(screen.getByText("27 ms")).toBeInTheDocument();
expect(screen.queryByText("99 ms")).not.toBeInTheDocument();
});
it("omits provenance when identical matching calls are ambiguous", async () => {
let activeController = controller({ acknowledged: true });
mockedUseCapabilityPlayground.mockImplementation(() => activeController);
const view = renderPlayground();
await submitNodeCall();
activeController = controller({
phase: "result",
acknowledged: true,
result: callResult(),
});
mockedUseCapabilityPlayground.mockReturnValue(activeController);
mockedUseConsoleWorkspace.mockReturnValue(
workspaceWithEvidence([
capabilityEvidence("call-identical-a", nodeCapability.name, 24, target, {
query: "README.md",
}),
capabilityEvidence("call-identical-b", nodeCapability.name, 31, target, {
query: "README.md",
}),
]),
);
view.rerender(
<CapabilityPlayground
capability={nodeCapability}
executor={writeExecutor}
target={target}
/>,
);
expect(screen.queryByText("24 ms")).not.toBeInTheDocument();
expect(screen.queryByText("31 ms")).not.toBeInTheDocument();
expect(screen.getByText("Call evidence was not retained for this connection.")).toBeInTheDocument();
});
it("matches only evidence added after the current repeated call", async () => {
let activeController = controller({ acknowledged: true });
mockedUseCapabilityPlayground.mockImplementation(() => activeController);
mockedUseConsoleWorkspace.mockReturnValue(
workspaceWithEvidence([capabilityEvidence("call-first", nodeCapability.name, 11)]),
workspaceWithEvidence([
capabilityEvidence("call-first", nodeCapability.name, 11, target, {
query: "first.md",
}),
]),
);
const view = renderPlayground();
await submitNodeCall("first.md");
@@ -564,7 +718,11 @@ describe("CapabilityPlayground", () => {
}),
);
mockedUseConsoleWorkspace.mockReturnValue(
workspaceWithEvidence([capabilityEvidence("call-first", nodeCapability.name, 11)]),
workspaceWithEvidence([
capabilityEvidence("call-first", nodeCapability.name, 11, target, {
query: "first.md",
}),
]),
);
view.rerender(
<CapabilityPlayground
@@ -590,8 +748,12 @@ describe("CapabilityPlayground", () => {
mockedUseCapabilityPlayground.mockReturnValue(activeController);
mockedUseConsoleWorkspace.mockReturnValue(
workspaceWithEvidence([
capabilityEvidence("call-first", nodeCapability.name, 11),
capabilityEvidence("call-second", nodeCapability.name, 22),
capabilityEvidence("call-first", nodeCapability.name, 11, target, {
query: "first.md",
}),
capabilityEvidence("call-second", nodeCapability.name, 22, target, {
query: "second.md",
}),
]),
);
view.rerender(
@@ -642,7 +804,11 @@ describe("CapabilityPlayground", () => {
});
mockedUseCapabilityPlayground.mockReturnValue(activeController);
mockedUseConsoleWorkspace.mockReturnValue(
workspaceWithEvidence([capabilityEvidence("call-connected", nodeCapability.name, 24)]),
workspaceWithEvidence([
capabilityEvidence("call-connected", nodeCapability.name, 24, target, {
query: "README.md",
}),
]),
);
view.rerender(
<CapabilityPlayground
@@ -23,8 +23,9 @@ type PlaygroundTab = "contract" | "try";
type SubmittedCall = {
readonly baselineEvidenceIds: ReadonlySet<string>;
readonly deploymentId: string;
readonly deploymentId: string | null;
readonly executor: ConsoleWriteExecutor | null;
readonly payload: Record<string, unknown>;
readonly payloadText: string;
readonly qualifiedName: string;
readonly target: string | null;
@@ -46,33 +47,82 @@ const formatKind = (kind: CapabilityDetail["kind"]): string =>
const isRecord = (value: unknown): value is Record<string, unknown> =>
typeof value === "object" && value !== null && !Array.isArray(value);
const requestQualifiedName = (request: unknown): string | null => {
type CapabilityRequestProjection = {
readonly deploymentId: string | null;
readonly payload: Record<string, unknown>;
readonly qualifiedName: string;
};
const normalizeDeploymentId = (value: unknown): string | null => {
if (typeof value !== "string") return null;
const normalized = value.trim();
return normalized === "" ? null : normalized;
};
const capabilityRequestProjection = (
request: unknown,
): CapabilityRequestProjection | null => {
if (!isRecord(request) || typeof request.qualified_name !== "string") {
return null;
}
return request.qualified_name;
if (!isRecord(request.payload)) return null;
if (
request.deployment_id !== undefined &&
request.deployment_id !== null &&
typeof request.deployment_id !== "string"
) {
return null;
}
return {
deploymentId: normalizeDeploymentId(request.deployment_id),
payload: request.payload,
qualifiedName: request.qualified_name,
};
};
const jsonDeepEqual = (left: unknown, right: unknown): boolean => {
if (Object.is(left, right)) return true;
if (left === null || right === null) return false;
if (typeof left !== typeof right) return false;
if (Array.isArray(left) || Array.isArray(right)) {
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
return false;
}
return left.every((value, index) => jsonDeepEqual(value, right[index]));
}
if (!isRecord(left) || !isRecord(right)) return false;
const leftKeys = Object.keys(left).sort();
const rightKeys = Object.keys(right).sort();
if (leftKeys.length !== rightKeys.length) return false;
return leftKeys.every(
(key, index) => key === rightKeys[index] && jsonDeepEqual(left[key], right[key]),
);
};
const submittedCallEvidence = (
evidence: ReadonlyArray<EvidenceRecord>,
submittedCall: SubmittedCall,
): EvidenceRecord | null => {
// Evidence is an append-only stream, so the baseline prevents an older
// matching call from being mistaken for the call that produced this receipt.
if (submittedCall.target === null) return null;
for (let index = evidence.length - 1; index >= 0; index -= 1) {
const record = evidence[index];
if (record === undefined) continue;
const matches = evidence.filter((record) => {
if (
!submittedCall.baselineEvidenceIds.has(record.id) &&
record.operation === "workflow.capabilities.call" &&
record.target === submittedCall.target &&
requestQualifiedName(record.request) === submittedCall.qualifiedName
submittedCall.baselineEvidenceIds.has(record.id) ||
record.operation !== "workflow.capabilities.call" ||
record.target !== submittedCall.target
) {
return record;
return false;
}
}
return null;
const request = capabilityRequestProjection(record.request);
return (
request !== null &&
request.qualifiedName === submittedCall.qualifiedName &&
request.deploymentId === submittedCall.deploymentId &&
jsonDeepEqual(request.payload, submittedCall.payload)
);
});
// Concurrent identical calls are indistinguishable here; fail closed rather
// than presenting one call's duration as proof for another call.
return matches.length === 1 ? matches[0] ?? null : null;
};
const schemaText = (value: Record<string, unknown>): string =>
@@ -196,7 +246,7 @@ const ResultReceipt = ({
<div>
<dt>Deployment</dt>
<dd aria-label="Submitted deployment">
{submittedCall.deploymentId || "default"}
{submittedCall.deploymentId ?? "default"}
</dd>
</div>
</dl>
@@ -312,8 +362,9 @@ const TryView = ({
}
setSubmittedCall({
baselineEvidenceIds: new Set(connection.evidence.map((record) => record.id)),
deploymentId: controller.deploymentId.trim(),
deploymentId: normalizeDeploymentId(controller.deploymentId),
executor,
payload: result.value,
payloadText: formatBoundedJson(result.value),
qualifiedName: capability.name,
target,