mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 09:21:16 +00:00
fix(snapshot): replay validated workflow evidence (#984)
* fix(snapshot): bind replay to validated cache entry * fix(snapshot): persist composed workflow results * fix(snapshot): enforce workflow replay identity * fix(snapshot): honor cancellation after route resolution * test(snapshot): make cancellation race deterministic * fix(snapshot): replay all composed workflow evidence * fix(snapshot): index workflow evidence once per snapshot Validate workflow entries against a query and content index instead of scanning the evidence bundle for every entry. * fix(snapshot): reuse previewed routes and bound workflow cache --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
1 parent
96a46ada0a
commit
1d62e54e1f
11 files changed
+1462
-80
No files matched your search
@@ -10,15 +10,137 @@ import { createEvidence } from "../../domain/evidence.js";
|
||||
import { createEvidenceBundle } from "../../domain/evidenceBundle.js";
|
||||
import { ok } from "../../domain/result.js";
|
||||
import { createAnalysisExecution } from "../AnalysisProvider.js";
|
||||
import {
|
||||
createAnalysisSnapshotWorkflowEntry,
|
||||
parseAnalysisSnapshot,
|
||||
snapshotBinding,
|
||||
snapshotTarget,
|
||||
} from "../../domain/analysisSnapshot.js";
|
||||
import {
|
||||
REA_WORKFLOW_PROVIDER,
|
||||
workflowAnalysisProfile,
|
||||
} from "../InvestigationProviders.js";
|
||||
import type {
|
||||
AnalysisOperation,
|
||||
CapabilityDescriptor,
|
||||
} from "../AnalysisProvider.js";
|
||||
import {
|
||||
AnalysisSnapshotCache,
|
||||
SNAPSHOT_CACHE_ENTRY_CEILING,
|
||||
isSnapshotCacheable,
|
||||
} from "./AnalysisSnapshotCache.js";
|
||||
|
||||
describe("analysis snapshot cache capacity", () => {
|
||||
it("shares capacity across query kinds while allowing replacements and fresh partitions", () => {
|
||||
const cache = new AnalysisSnapshotCache();
|
||||
const target = ANALYSIS_SNAPSHOT_TARGET;
|
||||
const profile = ANALYSIS_SNAPSHOT_PROFILE;
|
||||
const execution = {
|
||||
...createAnalysisExecution("workflow", REA_WORKFLOW_PROVIDER, {
|
||||
subject: target,
|
||||
}),
|
||||
analysisProfile: workflowAnalysisProfile(profile),
|
||||
};
|
||||
const workflowInput = {
|
||||
target,
|
||||
profile,
|
||||
operation: "trace_feature",
|
||||
parameters: { query: "existing" },
|
||||
execution,
|
||||
};
|
||||
for (let index = 0; index < SNAPSHOT_CACHE_ENTRY_CEILING - 1; index += 1)
|
||||
cache.recordWorkflow({
|
||||
...workflowInput,
|
||||
parameters: { query: String(index) },
|
||||
});
|
||||
// Staging and recording both spend the same budget; an existing query can
|
||||
// still be refreshed when no space remains for a new binding.
|
||||
const directInput = {
|
||||
target,
|
||||
profile,
|
||||
operation: "address_name" as const,
|
||||
parameters: { address: "0x1000", document: "fixture" },
|
||||
execution: createAnalysisExecution(
|
||||
"initial",
|
||||
ANALYSIS_SNAPSHOT_PROVIDER,
|
||||
{ subject: target, analysisProfile: profile },
|
||||
),
|
||||
};
|
||||
cache.record(directInput);
|
||||
cache.record({
|
||||
...directInput,
|
||||
parameters: { address: "0x2000", document: "fixture" },
|
||||
});
|
||||
cache.recordWorkflow(workflowInput);
|
||||
expect(cache.entries()).toHaveLength(1);
|
||||
expect(cache.workflowEntries()).toHaveLength(
|
||||
SNAPSHOT_CACHE_ENTRY_CEILING - 1,
|
||||
);
|
||||
|
||||
cache.record({
|
||||
...directInput,
|
||||
execution: { ...directInput.execution, result: "updated" },
|
||||
});
|
||||
cache.recordWorkflow({
|
||||
...workflowInput,
|
||||
parameters: { query: "0" },
|
||||
execution: { ...execution, result: "updated" },
|
||||
});
|
||||
expect(
|
||||
cache.lookup(target, profile, "address_name", directInput.parameters)
|
||||
?.result,
|
||||
).toBe("updated");
|
||||
expect(
|
||||
cache.workflowEntries().find(({ parameters }) => parameters.query === "0")
|
||||
?.execution.result,
|
||||
).toBe("updated");
|
||||
|
||||
const incoming = createAnalysisSnapshotWorkflowEntry({
|
||||
...workflowInput,
|
||||
target: snapshotTarget(target),
|
||||
binding: snapshotBinding(profile),
|
||||
});
|
||||
const evidence = createEvidence(target, REA_WORKFLOW_PROVIDER, {
|
||||
operation: workflowInput.operation,
|
||||
parameters: workflowInput.parameters,
|
||||
result: execution.result,
|
||||
rawResult: execution.rawResult,
|
||||
analysisProfile: execution.analysisProfile,
|
||||
limitations: execution.limitations,
|
||||
locations: execution.locations,
|
||||
});
|
||||
cache.stage(
|
||||
parseAnalysisSnapshot({
|
||||
target: snapshotTarget(target),
|
||||
binding: snapshotBinding(profile),
|
||||
entries: [],
|
||||
workflow_entries: [incoming],
|
||||
evidence_bundle: createEvidenceBundle([evidence]),
|
||||
}),
|
||||
);
|
||||
expect(cache.workflowEntries()).toHaveLength(
|
||||
SNAPSHOT_CACHE_ENTRY_CEILING - 1,
|
||||
);
|
||||
|
||||
const otherTarget = { ...target, sha256: "b".repeat(64) };
|
||||
cache.recordWorkflow({
|
||||
...workflowInput,
|
||||
target: otherTarget,
|
||||
execution: { ...execution, subject: otherTarget },
|
||||
});
|
||||
expect(cache.entries()).toEqual([]);
|
||||
expect(cache.workflowEntries()).toHaveLength(1);
|
||||
cache.clear();
|
||||
cache.record({
|
||||
...directInput,
|
||||
target: otherTarget,
|
||||
execution: { ...directInput.execution, subject: otherTarget },
|
||||
});
|
||||
expect(cache.entries()).toHaveLength(1);
|
||||
expect(cache.workflowEntries()).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("analysis snapshot cache partitioning", () => {
|
||||
it.each<{
|
||||
operation: Exclude<AnalysisOperation, "health">;
|
||||
|
||||
Reference in new issue
Block a user