Skip to content

Commit 7a1a0f2

Browse files
committed
shadow backfill: name the stop reason when a scope retires early
A scope that stops with items remaining previously reported only a bare count; a stall (provider failure/timeout hitting the no-progress guard) was indistinguishable from an unembeddable-item bug and cost a diagnosis cycle. Retirement now records drained vs stalled_no_progress, logs the stall with its usual cause and the resume path, and the manual --shadow script prints an explicit resume note.
1 parent 7640f6d commit 7a1a0f2

2 files changed

Lines changed: 35 additions & 0 deletions

File tree

packages/plugin/scripts/backfill-embeddings.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
flushShadowEmbeddingBacklog,
2424
getProjectEmbeddingSnapshot,
2525
getShadowBackfillRemaining,
26+
getShadowBackfillStopReason,
2627
registerProjectEmbedding,
2728
registerProjectShadowEmbedding,
2829
} from "../src/features/magic-context/memory/embedding";
@@ -104,6 +105,17 @@ async function runShadowBackfill(
104105
`Done. Embedded ${before.memory - after.memory} memories, ${before.commit - after.commit} commits, ${before.chunk - after.chunk} chunks. ` +
105106
`Remaining: ${after.memory} memories, ${after.commit} commits, ${after.chunk} chunks.`,
106107
);
108+
// A nonzero remaining count with a stalled stop reason is honest backlog the
109+
// provider failed to serve this run, not an unembeddable class — say so, and
110+
// say what to do, instead of leaving a bare number that reads like a bug.
111+
for (const scope of ["memory", "commit", "chunk"] as const) {
112+
if (after[scope] > 0 && getShadowBackfillStopReason(projectIdentity, scope) === "stalled_no_progress") {
113+
console.log(
114+
` note: ${scope} stopped early after a no-progress batch (provider failure/timeout). ` +
115+
`Re-run --shadow to resume; progress is banked per item.`,
116+
);
117+
}
118+
}
107119
}
108120

109121
async function main() {

packages/plugin/src/features/magic-context/project-embedding-registry.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,17 @@ const pendingShadowBackfills = new Map<string, Set<ShadowScope>>();
196196
* that cannot succeed. Any real progress changes the set and clears the stall.
197197
*/
198198
const shadowBackfillLastIds = new Map<string, string>();
199+
/** Why a (project:scope) backfill stopped: "drained" or "stalled_no_progress". */
200+
const shadowBackfillStopReasons = new Map<string, ShadowBackfillStopReason>();
201+
export type ShadowBackfillStopReason = "drained" | "stalled_no_progress";
202+
203+
/** Stop reason for a scope's last backfill retirement, for status surfaces. */
204+
export function getShadowBackfillStopReason(
205+
projectIdentity: string,
206+
scope: "memory" | "commit" | "chunk",
207+
): ShadowBackfillStopReason | undefined {
208+
return shadowBackfillStopReasons.get(`${projectIdentity}:${scope}`);
209+
}
199210

200211
const loadUnembeddedMemoriesStatements = new WeakMap<Database, PreparedStatement>();
201212
const upsertActiveIdentityStatements = new WeakMap<Database, PreparedStatement>();
@@ -1301,13 +1312,25 @@ function pumpShadowBackfill(): void {
13011312
SHADOW_MAX_ITEMS_PER_TICK,
13021313
);
13031314
if (ids.length === 0) {
1315+
shadowBackfillStopReasons.set(stallKey, "drained");
13041316
scopes.delete(scope);
13051317
shadowBackfillLastIds.delete(stallKey);
13061318
continue;
13071319
}
13081320
const signature = ids.join(",");
13091321
if (shadowBackfillLastIds.get(stallKey) === signature) {
13101322
// No progress since the last pump for this scope; stop retrying.
1323+
// Record WHY so status surfaces can distinguish "honest backlog,
1324+
// provider was failing" from "nothing left" — a bare remaining
1325+
// count after a silent stop reads as an unembeddable-item bug and
1326+
// costs a diagnosis cycle (2026-07-24: 437 chunks, transient
1327+
// provider timeouts under load, zero recorded reason).
1328+
shadowBackfillStopReasons.set(stallKey, "stalled_no_progress");
1329+
log(
1330+
`[shadow] backfill scope ${scope} for ${projectIdentity} retired without progress — ` +
1331+
`the last batch produced no writes (provider failure or timeout is the usual cause); ` +
1332+
`${ids.length}+ items remain and retry on the next registration or manual --shadow run`,
1333+
);
13111334
scopes.delete(scope);
13121335
shadowBackfillLastIds.delete(stallKey);
13131336
continue;

0 commit comments

Comments
 (0)