07a29aa33b
Storage gains list and delete: a browser build has no OS file manager standing behind it, so enumerating and removing documents has to be the backend's job. Both are implemented for PhysicalFs and MemoryFs, and a shared storage_contract test runs against both so they cannot drift. OpfsFs (wasm32 only) implements the same *synchronous* trait. That works because createSyncAccessHandle() yields a handle whose read, write, truncate, flush and close are genuinely blocking -- available only inside a dedicated Web Worker, which is where improvise-9x6 puts the app. So the trait did not have to become async. What is still async is pulled out into four methods the worker awaits from its message loop: open(), attach(path), refresh() and commit_deletes(). attach must precede any read or write of a document, because acquiring a handle is async even though using it is not. Two consequences worth knowing. delete() drops the name from the index and queues a removeEntry, so it is visible to read and list immediately and lands in OPFS when commit_deletes is awaited -- the alternative was making the whole trait async for one operation. And handles are exclusive per file entry, so attach closes any handle it replaces and detach exists to release the lock. Checked before writing it: Safari 16.4+ has synchronous access handle methods (an early spec wrongly called them async, and every current browser implements them sync). So the IndexedDB fallback the ticket called for is not needed, per the user's "not unless recent Safari needs it" -- and the async question it would have forced does not arise. Compile- and clippy-clean for wasm32; there is no browser here, so improvise-xf1 tracks verifying it against one. storage_contract is the sequence it must satisfy. Closes improvise-i34 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V69PGQfMJuGRmtSWpfVAqn