Skip to content

Commit 92ae593

Browse files
authored
perf(semantic-search): snapshot manifest partitions (#65)
## Summary Hardens the per-run manifest snapshot layer so scans are pinned to the manifest captured at index creation time. This protects the KV-only scan path from accidentally falling back to a later live catalog read. ## Changes - Add regression coverage for create-time manifest snapshot stability. - Prove `scan` does not reread a changed live manifest after `create`. - Assert the scanned job keeps the original snapshot fingerprint and source revision. ## Call Stack ```text ToolSearchIndex.create() -> listToolManifests() once -> partition manifests -> write index-manifest/v1/{runId}/{partition} to executor.cache ToolSearchIndex.scan() -> read partition snapshot from executor.cache -> never call executor.tools.manifest() -> materialize jobs from the snapshot ``` ## Tests - `bun run --cwd packages/plugins/semantic-search test -- src/sdk/tool-search-index.test.ts` - `bun run --cwd packages/plugins/semantic-search typecheck` - `bunx oxfmt --check packages/plugins/semantic-search/src/sdk/tool-search-index.test.ts` - `bunx oxlint -c .oxlintrc.jsonc --deny-warnings packages/plugins/semantic-search/src/sdk/tool-search-index.test.ts` ## Stack Base: #64 <!-- stack:links:start --> ### [Stack](https://github.com/aryasaatvik/stack) 1. #63 2. #64 3. **#65** 👈 current <!-- stack:links:end -->
1 parent 40d5680 commit 92ae593

1 file changed

Lines changed: 39 additions & 0 deletions

File tree

packages/plugins/semantic-search/src/sdk/tool-search-index.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,45 @@ describe("ToolSearchIndex manifest snapshot", () => {
10321032
}),
10331033
);
10341034

1035+
it.effect("scans the create-time manifest snapshot when the live manifest changes", () =>
1036+
Effect.gen(function* () {
1037+
const [tool] = makeTools(1);
1038+
expect(tool).toBeDefined();
1039+
if (tool === undefined) return;
1040+
let liveManifests = [manifestForTool(tool, "fp-at-create", "source-at-create")];
1041+
const counters = { manifest: 0 };
1042+
const executor: Pick<Executor, "tools" | "cache"> = {
1043+
tools: {
1044+
list: () => Effect.succeed([tool]),
1045+
manifest: () => {
1046+
counters.manifest++;
1047+
return Effect.succeed(liveManifests);
1048+
},
1049+
schema: () => Effect.succeed(null),
1050+
},
1051+
cache: makeMemoryCache(),
1052+
};
1053+
const base = makeBase(executor as Executor);
1054+
1055+
yield* create({ ...base, runId: "run-snapshot-stability", partitionCount: 1 });
1056+
liveManifests = [manifestForTool(tool, "fp-after-create", "source-after-create")];
1057+
1058+
const scanned = yield* scan({
1059+
...base,
1060+
runId: "run-snapshot-stability",
1061+
partition: 0,
1062+
limit: 10,
1063+
});
1064+
1065+
expect(scanned).toMatchObject({ processed: 1, changed: 1, skipped: 0 });
1066+
expect(counters.manifest).toBe(1);
1067+
expect([...base.jobs.data.values()][0]).toMatchObject({
1068+
fingerprint: "fp-at-create",
1069+
sourceRevision: "source-at-create",
1070+
});
1071+
}),
1072+
);
1073+
10351074
it.effect("fails the scan when the run snapshot is missing (no D1 fallback)", () =>
10361075
Effect.gen(function* () {
10371076
const { executor, counters } = makeCountingExecutor(makeTools(2));

0 commit comments

Comments
 (0)