SPB Git

spb/autoscholar

Public

Storage: digest sidecars renamed to *.~digest (no collision with research *.sha256 files)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Oct 30, 2026) parent b122954 Browse files

2 changed files +12 −2

modified packages/storage/src/index.ts +3 −2
@@ -55,8 +55,9 @@ export class FilesystemStorage implements StorageBackend {
55 55 return abs;
56 56 }
57 57
58 + /** Digest sidecar. The suffix cannot collide with user artifact names (research code often writes `*.sha256`). */
58 59 private sidecar(abs: string): string {
59 − return `${abs}.sha256`;
60 + return `${abs}.~digest`;
60 61 }
61 62
62 63 async put(key: string, data: Buffer | Readable, expectedSha256?: string): Promise<StoredObject> {
@@ -155,7 +156,7 @@ export class FilesystemStorage implements StorageBackend {
155 156 }
156 157 for (const e of entries) {
157 158 const name = String(e.name);
158 − if (name.endsWith(".sha256") || name.includes(".tmp-")) continue;
159 + if (name.endsWith(".~digest") || name.includes(".tmp-")) continue;
159 160 const r = rel ? `${rel}/${name}` : name;
160 161 if (e.isDirectory()) await walk(join(dir, name), r);
161 162 else out.push(`${assertSafeKey(prefix)}/${r}`.replace(/^\.\//, ""));
modified packages/storage/src/storage.test.ts +9 −0
@@ -38,6 +38,15 @@ describe("FilesystemStorage", () => {
38 38 expect(() => store.absolutePath("runs/x/../../../y")).toThrow();
39 39 });
40 40
41 + it("accepts artifacts named *.sha256 next to their siblings (no sidecar collision)", async () => {
42 + await store.put("c/tool.c", Buffer.from("int main(){}"));
43 + const obj = await store.put("c/tool.c.sha256", Buffer.from("abc tool.c\n"));
44 + expect(obj.bytes).toBe(12);
45 + expect((await store.get("c/tool.c")).toString()).toBe("int main(){}");
46 + expect((await store.head("c/tool.c"))?.sha256).toBe(sha256Hex("int main(){}"));
47 + expect(await store.list("c")).toEqual(["c/tool.c", "c/tool.c.sha256"]);
48 + });
49 +
41 50 it("lists keys under a prefix", async () => {
42 51 await store.put("p/a.txt", Buffer.from("a"));
43 52 await store.put("p/sub/b.txt", Buffer.from("b"));
44 53
Menu

Theme