fix: reliable, self-healing AI feature-bundle installs (#472)

Make on-demand AI feature-bundle installs reliable and self-healing, closing
the failure modes behind most "some tool doesn't work" reports.

Multi-bundle installs: tools needing more than one bundle (Passport Photo,
Enhance Faces) install every required bundle from one action and stay
not-installed until all are present. Verified across all 19 AI tools.

Downloads: self-heal the accelerated Hugging Face (Xet) client so an upgraded
venv no longer silently falls back to slow urllib; restart instead of
corrupting a resumed partial when a proxy ignores Range and returns 200;
verify the completed size; fail fast on disk-full and HTTP 4xx; retry
transient errors five times; add hf_transfer fallback and document Xet egress.

Install integrity: crash-atomic venv writes so a killed or out-of-space
install can no longer tear the shared venv and break other tools; a boot
breadcrumb reseeds a torn venv to a clean state automatically; a post-install
smoke import test refuses to record a bundle whose libraries cannot load; an
install watchdog stops a wedged installer that would otherwise hold the venv
writer lock forever.

Adds unit and end-to-end tests for every failure mode above.
This commit is contained in:
SnapOtter
2026-07-10 07:32:48 +00:00
committed by GitHub
parent ffeacd4b3c
commit a731c3d1fe
33 changed files with 1913 additions and 125 deletions
@@ -388,6 +388,16 @@ describe("Feature status queries", () => {
expect(mod.isToolInstalled("passport-photo")).toBe(true);
});
it("isToolInstalled is false for enhance-faces when only upscale-enhance is installed", () => {
mod.markInstalled("upscale-enhance", "1.0.0", []);
expect(mod.isToolInstalled("enhance-faces")).toBe(false);
});
it("getFirstMissingBundleForTool names face-detection for enhance-faces when only upscale-enhance is installed", () => {
mod.markInstalled("upscale-enhance", "1.0.0", []);
expect(mod.getFirstMissingBundleForTool("enhance-faces")).toBe("face-detection");
});
it("getFirstMissingBundleForTool names face-detection when only background-removal is installed", () => {
mod.markInstalled("background-removal", "1.0.0", []);
expect(mod.getFirstMissingBundleForTool("passport-photo")).toBe("face-detection");
@@ -559,6 +569,23 @@ describe("Crash recovery - recoverInterruptedInstalls", () => {
expect(existsSync(lockPath)).toBe(false);
});
it("consumes a surviving venv.writing breadcrumb (interrupted venv write)", () => {
const marker = join(aiDir, "venv.writing");
writeFileSync(
marker,
JSON.stringify({ bundleId: "ocr", startedAt: "2026-01-01T00:00:00.000Z" }),
);
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
expect(() => mod.recoverInterruptedInstalls()).not.toThrow();
// The breadcrumb is always consumed so it can't retrigger recovery forever.
expect(existsSync(marker)).toBe(false);
// And the interruption is surfaced in the logs.
expect(warn.mock.calls.flat().join(" ")).toMatch(/interrupted|venv/i);
warn.mockRestore();
});
it("handles missing directories gracefully", async () => {
vi.resetModules();
const emptyTemp = mkdtempSync(join(tmpdir(), "snapotter-empty-"));