# Proposal: complete-drive — fill VFS-core gaps in @suaveplan/drive - **Change ID**: `complete-drive` - **Capability**: `drive` - **Package**: `@suaveplan/drive` - **Status**: PROPOSED - **Design**: ./design.md - **Spec delta**: ./specs/drive/spec.md - **Companions** (parallel, share surface, scope-disjoint): `complete-drive-uri`, `complete-types-drive-promotion` ## Why The current genesis `@suaveplan/drive` ships the VFS spine (constructor overloads, init/dispose FSM, plugin slot, middleware chain for read/write/delete/stat/list, template hydration via `hydrateFrom`/`getKeyStatus`/`getPresence`, `DRIVE_*` codes) but several capabilities implied by the package's identity as a **file-VFS core** are either partial or missing. They block downstream packages (`drive-mesh`, `drive-sharing`, `drive-react-sharing`, `drive-plugin-cache`) from composing against a stable surface. ## What ### Audit findings (genesis vs. upstream + CLAUDE.md identity) | # | Gap | Today | Required | |---|---|---|---| | 1 | `createReadStream` signature | `(path, signal?)` positional | overload `(path, options?: { offset?; length?; signal? })` so range reads and aborts cohabit | | 2 | `createWriteStream` signature | `(path, signal?)` positional | overload `(path, options?: { signal? })` for parity, no behaviour drift | | 3 | `Drive.copy(src, dst, opts?)` | missing | atomic copy via middleware chain, emits `drive:copy:before/after` | | 4 | `Drive.move(src, dst, opts?)` | missing | rename = copy+delete on storages without native rename; middleware-wrapped | | 5 | Op-set width for `delete`/`stat` | `signal?` positional | accept `{ signal? }` options bundle (matches `read`/`write`) | | 6 | `Drive.list` return type | typed `unknown` | typed `AsyncIterable` + page result; respect `recursive`/`limit`/`cursor` | | 7 | `Drive.watch(path, opts?)` | missing | EventBus-backed change subscription returning `AsyncIterable` + `unsubscribe()` | | 8 | Hydration progress events | callback only | also emit `drive:hydrate:progress` on the EventBus (delivery to multiple subscribers) | | 9 | Middleware coverage | read/write/delete/stat/list | add `copy`, `move`, `watch` middleware slots so plugins can wrap every op | | 10 | Plugin lifecycle API | install + dispose-on-drive-dispose | `unloadPlugin(name)`, `listPlugins()`, `findPlugin(name)`, ordered handle map, idempotent unload | | 11 | Quota / usage | absent | `Drive.usage()` aggregating per-storage byte counts; `drive:quota:exceeded` event when caller-configured soft/hard caps trip | | 12 | Sharing integration hook | absent | `DriveShareLike` interface in drive (no drive-sharing dep) + `drive.shareHook?: DriveShareLike` slot | | 13 | `DRIVE_CODES` registry | 11 codes | add `COPY_FAILED`, `MOVE_FAILED`, `WATCH_FAILED`, `QUOTA_EXCEEDED`, `OPERATION_ABORTED`, `STORAGE_READONLY` | | 14 | Snapshot/transaction API | upstream has `cow`, `journal`, `snapshots` siblings | **OUT OF SCOPE** — handled by future `drive-snapshot` / `drive-journal` plugin packages; we land only the middleware seam they will plug into | | 15 | OTEL spans on every op | partial | spans per public entrypoint (`drive.read`, `drive.write`, `drive.copy`, …) named `drive.` with `drive.storage.id` attribute | | 16 | 100% coverage | currently below 100% | hard gate at 100/100/100/100 (statements/branches/functions/lines), istanbul | ### Out of scope - **Sharing-specific UX** — owned by `@suaveplan/drive-sharing` and `@suaveplan/drive-react-sharing`. We expose only the `DriveShareLike` interface (no runtime dep on either package). - **Mesh transport details** — owned by `@suaveplan/drive-mesh-protocol`, `@suaveplan/drive-mesh`, `@suaveplan/drive-transport-https`, `@suaveplan/drive-transport-webrtc`, `@suaveplan/drive-webrtc`. We expose only the plugin slot + middleware seam they consume. - **Template signing / nonce verification** — owned by `@suaveplan/drive-template`. We continue to invoke its pipeline from `hydrateFrom`; no signing logic moves into drive itself. - **URI / locator** — owned by `@suaveplan/drive-uri` (covered by sibling change `complete-drive-uri`). - **Shared cross-package types** — promoted into `@suaveplan/types` by sibling change `complete-types-drive-promotion`; this change imports them once they land but does not block on the promotion (the local types stay valid until then). - **Encryption / chunking / dedup / journal / extents / snapshots / transforms / metadata-store** — every one of these is a sibling package today (`drive-plugin-chunks`, future `drive-plugin-*`); not a drive-core responsibility. ## Impact ### Affected dependents Run `bunx turbo ls --filter=...@suaveplan/drive` to enumerate. For each dependent: - `@suaveplan/drive-template` — [x] tests run / [x] docs reviewed / [ ] no action needed — consumes `hydrateFrom` + nonce LRU; new progress events are additive, no breakage. - `@suaveplan/drive-plugin-chunks` — [x] tests run / [x] docs reviewed / [ ] no action needed — plugin/middleware contract widens (additive); existing plugin keeps working. - `@suaveplan/drive-plugin-cache` — [x] tests run / [x] docs reviewed / [ ] no action needed — new `copy`/`move` middleware slots are opt-in. - `@suaveplan/drive-mesh-protocol` — [ ] tests run / [ ] docs reviewed / [x] no action needed (reason: pure wire-protocol, does not touch Drive surface). - `@suaveplan/drive-mesh` — [x] tests run / [x] docs reviewed / [ ] no action needed — adopts widened plugin lifecycle (`unloadPlugin`). - `@suaveplan/drive-transport-https` — [x] tests run / [x] docs reviewed / [ ] no action needed — passes through middleware, no API impact. - `@suaveplan/drive-transport-webrtc` — [x] tests run / [x] docs reviewed / [ ] no action needed. - `@suaveplan/drive-webrtc` — [x] tests run / [x] docs reviewed / [ ] no action needed. - `@suaveplan/drive-sharing` — [x] tests run / [x] docs reviewed / [ ] no action needed — gains optional `DriveShareLike` integration; legacy path unchanged. - `@suaveplan/drive-react-sharing` — [ ] tests run / [ ] docs reviewed / [x] no action needed (reason: re-exports drive-sharing types only). ## Migration - **Old**: `drive.createReadStream(path, signal)` / `createWriteStream(path, signal)` — positional `signal`. - **New**: `drive.createReadStream(path, { offset?, length?, signal? })` / `createWriteStream(path, { signal? })` — options bundle. - **Compatibility**: both forms accepted via overload; positional-`signal` form remains valid for one minor cycle, then becomes a TypeScript error in `0.1.0`. - **Search command for consumers**: `grep -rn "createReadStream\|createWriteStream" packages/ | grep -v "/dist/\|/coverage/"` - **Recommended replacement**: always pass the options bundle. - **Old**: `drive.delete(path, signal)` / `drive.stat(path, signal)` positional. - **New**: `drive.delete(path, { signal? })` / `drive.stat(path, { signal? })` options bundle (positional kept as overload for one cycle). - **Search**: `grep -rn "drive\.\(delete\|stat\)(" packages/ | grep -v "/dist/"`. ## Parity Checklist | Capability | Upstream (`main`) | Genesis after this change | Notes | |---|---|---|---| | POSIX `open/close/read/write/lseek/fsync` file-descriptor model | ✅ | 🚫 | Scoped out: genesis Drive is a high-level VFS, not POSIX FD emulation. The upstream FD API is unused in any genesis dependent and superseded by `read`/`write` + streams. | | `readFile`/`writeFile`/`appendFile` | ✅ | ⬆️ | Renamed to `read`/`write`; `append` via streams. | | `copyFile`/`rename`/`truncate` | ✅ | ⬆️ | Becomes `copy`/`move`; `truncate` via streams or future plugin. | | `mkdir`/`rmdir`/`rm`/`readdir`/`readdirWithTypes` | ✅ | 🚫 | Scoped out: genesis storage substrate is key-value (no real directories). `list(prefix)` covers the use case; directories are a sibling-plugin concern. | | `stat`/`lstat`/`access`/`exists`/`chmod`/`chown`/`utimes` | ✅ | ⬆️ | `stat` with options bundle stays; permissions/ownership are sibling-plugin concerns (no POSIX permission model in genesis universal tier). | | `symlink`/`readlink`/`realpath` | ✅ | 🚫 | Scoped out: symlinks belong to a future `drive-plugin-symlinks` if needed. | | `cd`/`pwd` cwd model | ✅ | 🚫 | Scoped out: genesis Drive forbids relative paths (path validator rejects `..` and leading `/`); cwd has no meaning. | | `createReadStream`/`createWriteStream` | ✅ | ⬆️ | Options-bundle overload; range reads via `{ offset, length }`. | | Plugin manager (add/remove/list) | ✅ | ⬆️ | `usePlugin` + new `unloadPlugin`/`listPlugins`/`findPlugin`. | | Storage failover | ✅ | 🚫 | Scoped out: failover belongs to `drive-mesh` server-fallback path or a future `drive-plugin-failover`. | | `chunks` / `cow` / `dedup` / `encryption` / `extents` / `journal` / `metadata-store` / `snapshots` / `transforms` subsystems | ✅ | 🚫 | Each is or will be a sibling package (`drive-plugin-chunks` already ships). VFS core only exposes the plugin/middleware seam. | | Template hydration (`hydrateFrom`/`getKeyStatus`/`getPresence`) | 🚫 | ⬆️ | Genesis-original capability, added by `drive-template`. | | EventBus integration | ✅ (direct emitter) | ⬆️ | Genesis uses injectable `EventBusLike` from `@suaveplan/types`; same contract, no upstream coupling. | | Quota / usage | 🚫 | ⬆️ | New: aggregates per-storage usage + emits soft/hard threshold events. | | Sharing-integration hook | 🚫 | ⬆️ | New: `DriveShareLike` interface (drive owns the contract; `drive-sharing` implements). |