Skip to content

Commit c4633b7

Browse files
committed
merge: shadow-embedding backfill on fingerprint rotation
2 parents e1fe14a + 3487e22 commit c4633b7

4 files changed

Lines changed: 827 additions & 7 deletions

File tree

packages/plugin/scripts/backfill-embeddings.ts

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,98 @@
99
* Run: bun scripts/backfill-embeddings.ts [--directory <cwd>] [--project <project_path>]
1010
* --directory Project directory used to resolve config and identity.
1111
* --project Only backfill memories for this project_path (must match --directory identity unless --force-project-path).
12+
* --shadow Re-embed the HISTORICAL corpus under the current Synapse shadow
13+
* identity instead of backfilling primary memories. Use this after
14+
* a fingerprint rotation orphaned the shadow measurement cohort;
15+
* it drives the same bounded enqueue path the plugin runs, but to
16+
* completion with progress output.
1217
*/
1318
import { Database } from "../src/shared/sqlite";
14-
import { loadPluginConfig } from "../src/config";
19+
import { loadPluginConfig, loadPluginConfigDetailed } from "../src/config";
1520
import {
1621
embedBatchForProject,
22+
flushShadowEmbeddingBacklog,
1723
getProjectEmbeddingSnapshot,
24+
getShadowBackfillRemaining,
1825
registerProjectEmbedding,
26+
registerProjectShadowEmbedding,
1927
} from "../src/features/magic-context/memory/embedding";
2028
import { resolveProjectIdentity } from "../src/features/magic-context/memory/project-identity";
2129
import { saveEmbedding } from "../src/features/magic-context/memory/storage-memory-embeddings";
30+
import { isConfigLoadUntrusted } from "../src/plugin/embedding-bootstrap-helpers";
31+
import { resolveEmbeddingRouting } from "../src/plugin/embedding-routing";
2232

2333
const DB_PATH = `${process.env.HOME}/.local/share/opencode/storage/plugin/magic-context/context.db`;
2434
function getArg(name: string): string | null {
2535
const index = process.argv.indexOf(name);
2636
return index >= 0 ? (process.argv[index + 1] ?? null) : null;
2737
}
2838

39+
/**
40+
* Drive the shadow historical backfill to completion with progress output.
41+
* Registering the (possibly rotated) shadow identity arms the backfill inside
42+
* the registry; this just resolves the live routing, registers both lanes the
43+
* same way the boot path does, then flushes the bounded queue until the missing
44+
* set is empty.
45+
*/
46+
async function runShadowBackfill(
47+
db: Database,
48+
directory: string,
49+
projectIdentity: string,
50+
): Promise<void> {
51+
const detailed = loadPluginConfigDetailed(directory);
52+
if (isConfigLoadUntrusted(detailed)) {
53+
console.error(
54+
"Config load is untrusted for this project; refusing to run the shadow backfill off a config we don't trust.",
55+
);
56+
process.exitCode = 1;
57+
return;
58+
}
59+
const routing = await resolveEmbeddingRouting({
60+
config: detailed.config,
61+
projectRoot: directory,
62+
session: `shadow-backfill:${projectIdentity}`,
63+
});
64+
for (const warning of routing.warnings) console.warn(`[shadow] ${warning}`);
65+
if (!routing.shadow) {
66+
console.error(
67+
"No shadow lane is armed for this project (shadow_embedding disabled or unavailable). Nothing to do.",
68+
);
69+
return;
70+
}
71+
registerProjectEmbedding(
72+
db,
73+
projectIdentity,
74+
routing.primary,
75+
{
76+
memoryEnabled: detailed.config.memory.enabled,
77+
gitCommitEnabled: detailed.config.memory.git_commit_indexing.enabled,
78+
},
79+
directory,
80+
);
81+
registerProjectShadowEmbedding(db, projectIdentity, routing.shadow, directory);
82+
83+
const before = getShadowBackfillRemaining(db, projectIdentity);
84+
console.log(
85+
`Shadow backfill for ${projectIdentity}: ${before.memory} memories, ${before.commit} commits, ${before.chunk} chunks missing under the current shadow identity.`,
86+
);
87+
if (before.memory === 0 && before.commit === 0 && before.chunk === 0) {
88+
console.log("Nothing to do.");
89+
return;
90+
}
91+
await flushShadowEmbeddingBacklog(projectIdentity, () => {
92+
const remaining = getShadowBackfillRemaining(db, projectIdentity);
93+
console.log(
94+
` remaining: ${remaining.memory} memories, ${remaining.commit} commits, ${remaining.chunk} chunks`,
95+
);
96+
});
97+
const after = getShadowBackfillRemaining(db, projectIdentity);
98+
console.log(
99+
`Done. Embedded ${before.memory - after.memory} memories, ${before.commit - after.commit} commits, ${before.chunk - after.chunk} chunks. ` +
100+
`Remaining: ${after.memory} memories, ${after.commit} commits, ${after.chunk} chunks.`,
101+
);
102+
}
103+
29104
async function main() {
30105
const directory = getArg("--directory") ?? process.cwd();
31106
const projectFilter = getArg("--project");
@@ -42,6 +117,13 @@ async function main() {
42117

43118
const db = new Database(DB_PATH);
44119
db.exec("PRAGMA journal_mode=WAL");
120+
121+
if (process.argv.includes("--shadow")) {
122+
await runShadowBackfill(db, directory, projectIdentity);
123+
db.close();
124+
return;
125+
}
126+
45127
const config = loadPluginConfig(directory);
46128
registerProjectEmbedding(
47129
db,

packages/plugin/src/features/magic-context/memory/embedding.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ export {
2424
embedUnembeddedCompartmentChunksForProject,
2525
embedUnembeddedMemoriesForProject,
2626
enqueueShadowEmbeddingItems,
27+
flushShadowEmbeddingBacklog,
2728
getPrimaryEmbeddingMeasurementCohort,
2829
getProjectEmbeddingSnapshot,
30+
getShadowBackfillRemaining,
2931
getShadowEmbeddingMeasurementCohort,
3032
markProjectLoadUntrusted,
3133
registerProjectEmbedding,

0 commit comments

Comments
 (0)