Files
suaveplan/openspec/archive/2026-04-28-drive-port/specs/drive/spec.md
2026-05-27 23:29:53 +01:00

3.9 KiB

Drive — Spec Delta (drive-port)

ADDED Requirements

DRIVE-01 — Drive consumes AbstractStorage directly

Drive MUST accept any concrete AbstractStorage from @suaveplan/storage as its storage substrate. There MUST NOT be a separate StorageAdapter shim. Drive MUST narrow capabilities through a DriveStorage view that extends AbstractStorage with id: string and optional capabilities?: { partialReads?, nativeStreams?, atomicWrites?, contentAddressable?, partialWrites?, requiresLifecycle? } plus optional connect?(), dispose?(), isConnected?() lifecycle hooks.

Verified by: src/drive/drive.spec-conformance.test.ts (DRIVE-01).

DRIVE-02 — initialize() is idempotent and ownership-tracking

Drive.initialize() MUST be idempotent. For each registered storage, drive MUST call storage.connect?.() ONLY when storage.isConnected?.() !== true, and MUST record those storages in an internal connectedByMe: WeakSet<DriveStorage>. Connection failures MUST surface as DriveError(DRIVE.STORAGE_CONNECT_FAILED, { cause }).

Verified by: src/drive/drive.spec-conformance.test.ts (DRIVE-02).

DRIVE-03 — dispose() is idempotent and ownership-respecting

Drive.dispose() MUST be idempotent. Drive MUST only close storages in connectedByMe; storages pre-connected by the caller MUST be left open. After dispose(), the drive transitions to a terminal disposed state.

Verified by: src/drive/drive.spec-conformance.test.ts (DRIVE-03).

DRIVE-04 — Operations after dispose() throw

Any file operation invoked on a disposed drive (read, write, delete, stat, list, createReadStream, createWriteStream) MUST throw DriveError(DRIVE.DISPOSED). Operations invoked before initialize() MUST throw DriveError(DRIVE.NOT_INITIALIZED).

Verified by: src/drive/drive.spec-conformance.test.ts (DRIVE-04).

DRIVE-05 — Error-code registry is namespaced and frozen

DRIVE_CODES MUST be declared via defineCodes("DRIVE", [...]) from @suaveplan/error. Object.isFrozen(DRIVE_CODES) MUST be true. Every code value MUST start with DRIVE.. DriveError MUST extend SuaveplanError<DriveErrorCode>.

Verified by: src/error-codes.spec-conformance.test.ts (DRIVE-05).

DRIVE-06 — Plugin middleware order

drive.use(plugin) and drive.useMiddleware(mw) MUST install in registration order. Middleware chains for read, write, delete, stat, list MUST execute outer-first on the request path and outer-last on the response path. plugin.dispose() MUST be called in reverse-registration order on Drive.dispose(). Plugin install/dispose throws MUST surface as DriveError(DRIVE.PLUGIN_FAILED, { cause }).

Verified by: src/plugins/registry.spec-conformance.test.ts (DRIVE-06).

DRIVE-07 — Capability fallbacks for streams

drive.createReadStream(path) MUST use storage.createReadStream when storage.capabilities?.nativeStreams === true, otherwise MUST fall back to the inherited AbstractStorage.defaultCreateReadStream. Same rule for createWriteStream.

Verified by: src/file-operations/streams.spec-conformance.test.ts (DRIVE-07).

DRIVE-08 — Generic storage typing

Drive<TStorageMap> MUST narrow TStorageMap through drive.use<K, S>(id, storage) so that drive.getStorage(id) returns TStorageMap[K] exactly (no widening to the base DriveStorage). Calls with an unknown id MUST be a TypeScript error.

Verified by: src/drive/drive.spec-conformance.test.ts (DRIVE-08, type-level via expectTypeOf).

DRIVE-09 — Constructor overloads

new Drive(), new Drive(storage), new Drive(storage, options), and new Drive({ defaultStorage, plugins, ... }) MUST all be valid. The bare-storage form MUST be observably equivalent to passing { defaultStorage: storage } and MUST register the storage under id "default".

Verified by: src/drive/drive.spec-conformance.test.ts (DRIVE-09).