25 KiB
Complete @suaveplan/drive-uri — full per-scheme write + range-read parity, every operation, every option
Package: @suaveplan/drive-uri (+ MODIFIED surface on @suaveplan/drive)
Status: PROPOSED
Type: capability-completion (single change supersedes the two narrow follow-ups)
Supersedes: openspec/changes/_superseded-narrow-deferrals-2026-05-17/add-drive-uri-writes/ AND …/add-drive-range-reads/ — both deferrals land in this single change.
Depends on: add-drive-uri (archived 2026-05-16), add-storage-uri (archived).
- Design: ./design.md
- Spec deltas:
- ./specs/drive-uri/spec.md (primary)
- ./specs/drive/spec.md (MODIFIED —
createReadStreamrange overload)
Why
add-drive-uri shipped read-only with two explicit deferrals — writes and range reads — plus a documented gap on buffered-read AbortSignal threading (carried as a footnote in the canonical spec). Two narrow follow-ups (add-drive-uri-writes, add-drive-range-reads) were drafted, then superseded so the package can land at functional completeness in a single coherent change. This change closes every gap and brings drive-uri to full parity with what Drive itself exposes, so every URI operation is supported with first-class semantics.
Concretely, the gaps closed:
| # | Gap | Source of deferral |
|---|---|---|
| 1 | drive:// writes / removes / write-streams |
add-drive-uri proposal § Out of scope (line 32) |
| 2 | drive+mesh:// writes / removes / write-streams |
add-drive-uri proposal § Out of scope (line 32) |
| 3 | drive+template:// writes permanently locked-down with own code |
add-drive-uri-writes proposal — DRIVE_URI.TEMPLATE_READ_ONLY rather than the temporary WRITE_UNSUPPORTED |
| 4 | Range reads { offset, length } on all three schemes |
add-drive-uri proposal § Out of scope (line 35); add-drive-range-reads |
| 5 | Chunk-aware mesh range optimisation (specific ↔ bitset pivot) |
add-drive-range-reads proposal |
| 6 | AbortSignal threading through buffered get/set/delete/stat/exists/list paths |
Existing canonical spec footnote (lines 7-9 of openspec/specs/drive-uri/spec.md) |
| 7 | createWriteStream(key, body, signal?) adapter surface |
Not surfaced today — StorageUriResolver.write falls back to buffered drain |
| 8 | First-class stat returning StatResult (size / lastModified) |
getMetadata hook on the adapter — never wired |
| 9 | First-class list with prefix + pagination via Drive.list |
DriveStorageLikeAdapter.list exists but lacks resolver re-serialisation through UriPlugin.build |
| 10 | Cheap exists via Drive.getPresence / getKeyStatus (no buffered read) |
Today's exists calls Drive.stat — fine for local, expensive for template-backed |
| 11 | Per-scheme query-string options (?nonce=… for templates, `?advertise=true |
false` for mesh) |
| 12 | Mesh re-advertise opt-in via ?advertise=true query param + EventBus emission |
add-drive-uri-writes left this consumer-controlled by default; this change keeps the default but adds an opt-in |
| 13 | OTel span attributes per op with backend identification + range tag | add-drive-range-reads proposal |
| 14 | EventBus emission on every write/delete for downstream sync | add-drive-uri-writes design tangentially |
| 15 | Exhaustive DRIVE_URI.* error registry (read-side, write-side, template-read-only, range, mesh-propagation, capability) |
Today's registry is 7 codes; ships at 14 |
| 16 | runUriPluginConformance re-run against full read/write/delete/stat/list/exists matrix on all three plugins |
add-drive-uri-writes proposal |
The composition model is unchanged: three UriPlugin factories plug into the existing StorageUriResolver. No new resolver class. No re-implementation of any chunk codec, mesh transport, or template-signing logic — those stay inside Drive and its installed plugins.
What changes
@suaveplan/drive (MODIFIED)
- MODIFIED:
Drive.createReadStream(path, signalOrOptions?)— overloaded. BareAbortSignalpreserved; newRangeReadOptionsform accepts{ offset?, length?, signal? }.instanceof AbortSignaldiscriminator at runtime. Synchronous validation:Number.isInteger(x) && x >= 0 && Number.isFinite(x); failures throwDRIVE.RANGE_INVALIDBEFORE stream construction. - MODIFIED:
Drive.createWriteStream(path, signal?)— unchanged signature; reused by adapter'screateWriteStreamwrapper. - ADDED:
RangeReadOptionsinterface onsrc/types/drive.types.ts. - ADDED:
DRIVE.RANGE_INVALID,DRIVE.RANGE_OUT_OF_BOUNDScodes inDRIVE_CODESregistry (11 → 13 codes). - MODIFIED:
DriveStorage.capabilities?.nativeRange?: booleanflag.defaultCreateReadStreamhonours range via slicing transform; native-range storages forward{ offset, length }tostorage.createReadStream. - MODIFIED:
DriveMiddlewarecontext gainsrange?: { offset: number; length?: number }. Chunks-plugin reassemble path (out of scope for THIS package; covered in design.md as a follow-up to land within the same wave) honours it.
@suaveplan/drive-uri (the bulk of the change)
- ADDED — per-scheme per-op matrix (full):
| Operation | drive:// |
drive+mesh:// |
drive+template:// |
|---|---|---|---|
get |
Drive.read(key, { signal }) |
Drive.read(key, { signal }) |
hydrate-then-read |
set (write) |
Drive.write(key, bytes, { signal }) |
Drive.write + optional mesh.advertise(…) via ?advertise=true opt-in |
DRIVE_URI.TEMPLATE_READ_ONLY |
delete |
Drive.delete(key, signal) |
Drive.delete + optional mesh.unadvertise(fileId) on opt-in |
DRIVE_URI.TEMPLATE_READ_ONLY |
stat |
Drive.stat(key, signal) → StatResult |
same; cache-aware on template hits | same (post-hydrate) |
exists |
Drive.getPresence(key) cheap path → fallback stat |
same | same (post-hydrate) |
list |
Drive.list(prefix, { signal, limit, cursor }) → keys re-serialised via UriPlugin.build |
same | same |
createReadStream |
Drive.createReadStream(key, { offset, length, signal }) |
same; chunk-aware mesh request via ChunkRequest.specific ↔ bitset pivot |
same |
createWriteStream |
Drive.createWriteStream(key, signal) |
same | DRIVE_URI.TEMPLATE_READ_ONLY |
clear |
DRIVE_URI.WRITE_UNSUPPORTED (footgun) |
same | same |
- ADDED —
DRIVE_URI_CODESregistry grows 7 → 14 codes viadefineCodes("DRIVE_URI", […] as const). New codes:TEMPLATE_READ_ONLY— permanent template-mutation rejection.RANGE_INVALID— invalid{ offset, length }per-scheme echo ofDRIVE.RANGE_INVALID.RANGE_OUT_OF_BOUNDS— per-scheme echo ofDRIVE.RANGE_OUT_OF_BOUNDS/CHUNKS.RANGE_OUT_OF_BOUNDS.MESH_PROPAGATION_TIMEOUT— explicit?advertise=truewrite where the consumer-supplied advertise hook rejects or times out.STAT_UNAVAILABLE— underlying storage cannot answerstatand the URI surface needs a positive answer.LIST_UNSUPPORTED— the boundDrivedoes not exposelist(the AbstractStorage backing it lacks the method).INVALID_QUERY— query-string option is malformed (e.g.?advertise=maybe).- Retained:
INVALID_URI,DRIVE_ID_MISMATCH,TEMPLATE_NOT_AVAILABLE,TEMPLATE_INVALID,WRITE_UNSUPPORTED(now narrowed toclear()call site only),CAPABILITY_MISSING,ABORTED.
- ADDED —
DriveUriErrordiscriminants extend withrange?: { offset: number; length?: number },fileId?: string. - ADDED —
AbortSignalthreading on every buffered op:get/set/delete/stat/exists/listaccept and forwardAbortSignalto the underlyingDrivemethod. Pre-aborted signal →DRIVE_URI.ABORTEDshort-circuit before anyDrivecall. - ADDED —
InstrumentedDriveAdapter.createWriteStream(key, body, signal?): per-scheme dispatch (drive/drive+mesh→ forward;drive+template→ throwTEMPLATE_READ_ONLY). - ADDED —
InstrumentedDriveAdapter.getMetadata(key, signal?): Promise<StatResult | undefined>: translatesDrive.statintoStatResult { size, lastModified, etag?, contentType? }usingDriveStats.checksum(etag) andDrive.stat().modifiedAt(lastModified).StorageUriResolver.stat()picks this up automatically via the existingStreamingStorageExtensions.getMetadataduck-type. - ADDED — Per-scheme
UriPlugin.buildround-tripsmeta(driveId/swarmId/templateId) and preserves any incoming query string when supplied viameta.query. The resolver'slistre-serialiser receives prefix-listed keys and produces fully-qualified URIs that round-trip throughparse. - ADDED — Query-string parsing on every plugin:
drive+mesh://swarm/path?advertise=true|false→ opt-in mesh re-advertise on write/delete.drive+template://template/path?nonce=…→ forwardsnoncetotemplateProvider.fetchTemplate(templateId, { nonce, signal })(the provider is widened with an optional secondoptionsbag — back-compat preserved).?signal-timeout-ms=<int>(any scheme) → upper bound on a single-op latency before raisingDRIVE_URI.ABORTED.- Unknown query keys →
DRIVE_URI.INVALID_QUERYwith the offending key.
- ADDED — Query-string redaction policy: every
drive.urispan attribute, every error's.uri, and every emitted EventBus event strips the query string (redactUrialready exists; extended to also strip the fragment). Sensitive keys (nonce,peers, futureauth=) are never logged or surfaced. - ADDED — Mesh re-advertise via
?advertise=true: when set on adrive+mesh://write, the plugin invokes the consumer-suppliedMeshAdvertiseHookfromDriveMeshUriPluginOptions.advertise?. When the hook is absent, the plugin raisesDRIVE_URI.CAPABILITY_MISSINGwithreason: "advertise"synchronously at construction time only if the consumer opts in at the URI level — wiring stays purely additive. The hook is invoked after local persistence is durable; failure is wrapped asDRIVE_URI.MESH_PROPAGATION_TIMEOUT. - ADDED —
MeshAdvertiseHookinterface (universal-tier, lives intypes/plugin-options.types.ts):advertise(fileId, bitset, totalChunks, signal?): Promise<void>+unadvertise(fileId, signal?): Promise<void>. The hook wraps the consumer'smesh.advertise(...)/mesh.unadvertise(...)calls;drive-urinever depends on@suaveplan/drive-meshdirectly. - ADDED — EventBus emission on every write/delete success/failure when the wrapped
Driveexposesevents: topicsdrive_uri:write/drive_uri:deletecarrying{ scheme, key, uri (redacted), outcome, durationMs, fileId?, range? }. Used by downstream sync engines (@suaveplan/vault-sync, futuredrive-sharingflows) to drive incremental updates. EventBus is read off theDrivereference passed at construction; absence is a no-op. - ADDED — Span event
drive_uri.mesh_write_local_onlyon everydrive+mesh://write where?advertise=trueis NOT set, so operators can audit that the consumer-controlled gossip contract holds. - MODIFIED —
DRIVE_URI.WRITE_UNSUPPORTEDretained but narrowed toDriveStorageLikeAdapter.clear()only. Every other call site that previously raised it now succeeds or raises a more specific code per the matrix above. - MODIFIED —
DriveStorageLikeAdapter:get(key, signal?),set(key, value, signal?),delete(key, signal?),exists(key, signal?),list(options & { signal? }),stat(key, signal?)all accept and forwardAbortSignal.createReadStream(key, options?: { offset?, length?, signal? })forwards range toDrive.createReadStream(key, options).createWriteStream(key, body, signal?): consumes aReadableStream<Uint8Array>, pipes intoDrive.createWriteStream(key, signal).getMetadata(key, signal?)→StatResult | undefinedperStreamingStorageExtensions.capabilities.nativeRange === trueadvertised soStorageUriResolveruses the range-awarecreateReadStreamdirectly.
- MODIFIED — Conformance:
runUriPluginConformancere-run with full read/write/delete/stat/list/exists/range matrices for all three plugins. Fixtures:drive://— full cycle passes.drive+mesh://— full cycle passes; mesh-advertise spy asserts zero calls when?advertiseis omitted, exactly one call when?advertise=true.drive+template://— read/stat/exists/list pass;set/delete/createWriteStreamreject withDRIVE_URI.TEMPLATE_READ_ONLY.
- ADDED — Per-package OTel surface extensions:
drive_uri.stream_chunks_fetchedhistogram tagged{scheme}— chunk count percreateReadStreamcall (range narrows this).drive.uri.range = "{offset}-{end}"span attribute, omitted when no range.drive.uri.has_advertise = "true"|"false"span attribute ondrive+mesh://write spans.- All existing counters preserved.
Out of scope
Per user instruction, only two carve-outs:
- NEW URI schemes. Adding a fourth scheme (e.g.
drive+cdn://) is a separate change. The three existing schemes ship at full functional completeness here. - Templating policy. Whether templates are mutable (re-issue, multi-version, key rotation policy) lives in
@suaveplan/drive-template. This change codifies the URI surface's read-only treatment viaTEMPLATE_READ_ONLYbut does not alter template semantics themselves.
Source mapping
This is a capability-completion change for a genesis-original package. Surface anchors:
packages/drive/drive-uri/src/plugins/drive/drive.streams.ts:69-84— current unconditionalsetrejection (replaced).packages/drive/drive-uri/src/adapter/drive-storage-like.ts:75-86—set/deletealready pass-through internally; the instrumented override now per-scheme dispatches.packages/drive/drive-uri/src/adapter/drive-storage-like.ts:88-119—listexists; resolver re-serialisation viaUriPlugin.buildnewly required.packages/drive/drive/src/drive/drive.ts:180-240—Drive.write/delete/stat/list/createReadStream/createWriteStreamconsumed verbatim.packages/drive/drive/src/drive/drive.ts:222-230—createReadStreamoverload landing zone.packages/drive/drive/src/file-operations/streams.ts:50-70—nativeRangeslicing-transform insertion point; mirrors existingnativeStreamscapability flag pattern.packages/drive/drive-mesh/src/mesh/drive-mesh/drive-mesh.ts:176-193—advertise(fileId, bitset, totalChunks)/unadvertise(fileId)shape consumed structurally byMeshAdvertiseHook(no direct dep).packages/drive/drive-mesh-protocol/src/types/protocol.types.ts:38-42—ChunkRequest.specific | bitsetshapes; pivot at 64 chunks perMAX_CHUNKS_DEFAULT.packages/storage/storage-uri/src/resolver/resolver.writes.ts:30-75— confirmsSuaveplanErrorsubclasses pass throughwriteViaPluginunchanged, so every newDRIVE_URI.*code reaches the caller without aSTORAGE_URI.*envelope.packages/storage/storage-uri/src/resolver/resolver.streams.ts:24-54—openViaPluginalready forwards toadapter.createReadStream(no fallback) when present; range bag passes through via the existing options-args shape.packages/storage/storage-uri/src/types/plugin.types.ts:41-67—StreamingStorageExtensions.getMetadata/createReadStream/createWriteStreamduck-type —drive-uriadapter implements all three.openspec/archive/2026-05-16-add-drive-uri/proposal.mdlines 30-38 — original deferral text.openspec/changes/_superseded-narrow-deferrals-2026-05-17/add-drive-uri-writes/proposal.md— superseded research, reused.openspec/changes/_superseded-narrow-deferrals-2026-05-17/add-drive-range-reads/proposal.md— superseded research, reused.
Requirements with no direct source line
| Requirement | Justification |
|---|---|
| Single coherent change instead of two narrow follow-ups | The deferrals were carved fine-grained on the assumption write semantics were "non-trivial"; revisiting per-scheme showed they are not. Landing both together avoids two passes through the conformance harness, two rounds of doc churn, and two dependent-sweep cycles. The supersede notice (_superseded-narrow-deferrals-2026-05-17/) records the consolidation. |
?advertise=true query opt-in rather than always-advertise on mesh write |
Direct Drive.write does not auto-advertise — the URI surface MUST preserve that contract by default. The opt-in gives the URI an explicit knob without bending the default. |
MeshAdvertiseHook lives in drive-uri not drive-mesh |
drive-uri MUST NOT depend on drive-mesh (it would couple the URI tier to a concrete transport). The structural hook lets consumers wire their mesh.advertise(...) without drive-uri ever importing @suaveplan/drive-mesh. |
EventBus emission off the wrapped Drive's events rather than a separate bus |
The Drive already carries `events: EventBusLike |
TEMPLATE_READ_ONLY distinct from WRITE_UNSUPPORTED rather than reusing |
WRITE_UNSUPPORTED was a temporary state code from add-drive-uri; templates are permanently read-only via an architectural constraint (authority re-signing). Distinct codes let consumers branch correctly. |
Range overload via instanceof AbortSignal discriminator |
The only other shape that satisfies the position-1 type is RangeReadOptions; AbortSignal.prototype is a stable cross-runtime brand. Duck-typing on signal?.aborted would falsely match RangeReadOptions. |
Range registry mirrored on DRIVE_URI rather than only DRIVE |
Consumers branching on the URI surface should not need to know which underlying error class fired; the URI-tier echoes the canonical drive codes via wrapping while preserving cause. |
Cheap exists via Drive.getPresence over Drive.stat |
getPresence returns a synchronous ChunkPresence from the in-memory FileLocationIndex for hydrated files (template path) and falls back to stat for raw paths — strictly cheaper for drive+template:// reads. |
| Query-string redaction always-on | A future opt-in ?auth=… is plausible; codifying redaction now prevents a leak when that opt-in lands. |
Verification approach
Every requirement in the spec deltas carries a **Verified by:** line referencing concrete it(...) titles in planned test files. The pre-archive gate (§13 of AGENTS.md) re-runs the full conformance matrix against all three plugins. Integration tests (no mocks per CLAUDE.md rule 2) cover real-Drive round-trips for read, write, delete, stat, list, exists, range, and template hydration paths.
Migration
| Old | New |
|---|---|
resolver.write("drive://…", bytes) → DRIVE_URI.WRITE_UNSUPPORTED |
succeeds; returns void |
resolver.write("drive+mesh://…", bytes) → DRIVE_URI.WRITE_UNSUPPORTED |
succeeds; mesh advertise opt-in via ?advertise=true |
resolver.write("drive+template://…", bytes) → DRIVE_URI.WRITE_UNSUPPORTED |
DRIVE_URI.TEMPLATE_READ_ONLY |
resolver.remove("drive+template://…") → silent footgun (forwarded to Drive.delete) |
DRIVE_URI.TEMPLATE_READ_ONLY |
resolver.stat("drive://…") → undefined (no getMetadata) |
StatResult { size, lastModified, etag? } |
resolver.open("drive+mesh://…/clip.mov", signal) → whole-file fetch |
same; open(..., { offset, length, signal }) fetches only in-range chunks |
DRIVE_URI.WRITE_UNSUPPORTED raised for set/delete |
only raised for DriveStorageLikeAdapter.clear() |
Consumer search: grep -rln "DRIVE_URI.WRITE_UNSUPPORTED" packages/ | grep -v "/dist/\|/coverage/\|/drive-uri/". Branch new code on err.code === "DRIVE_URI.TEMPLATE_READ_ONLY" for the immutable template surface; on DRIVE_URI.RANGE_INVALID / RANGE_OUT_OF_BOUNDS for range errors; on DRIVE_URI.MESH_PROPAGATION_TIMEOUT for opt-in advertise failures.
Impact
Affected dependents
Run bunx turbo ls --filter=...@suaveplan/drive-uri to enumerate. The package's downstream surface is currently empty (drive-uri is consumed only via integration tests at the time of writing); the broader ...@suaveplan/drive dependents drive verification:
@suaveplan/drive-uri— [x] tests run / [x] docs reviewed / [ ] no action needed@suaveplan/drive-plugin-chunks— [x] tests run / [x] docs reviewed / [ ] no action needed (middleware honoursctx.range;#reassembleextended)@suaveplan/drive-mesh— [x] tests run / [x] docs reviewed / [ ] no action needed (pull path constructsChunkRequest.specific/bitsetfrom range;advertiseconsumed via structural hook)@suaveplan/drive-mesh-protocol— [ ] tests run / [x] docs reviewed / [x] no action needed (no protocol wire change; existing union shapes used)@suaveplan/drive-transport-https— [ ] tests run / [x] docs reviewed / [x] no action needed (transport carries envelopes opaquely)@suaveplan/drive-transport-webrtc— [ ] tests run / [x] docs reviewed / [x] no action needed (same)@suaveplan/drive-template— [ ] tests run / [x] docs reviewed / [x] no action needed (hydration unchanged)@suaveplan/drive-sharing— [ ] tests run / [x] docs reviewed / [x] no action needed (callsDrive.read)@suaveplan/storage-uri— [x] tests run / [x] docs reviewed / [ ] no action needed (writeViaPluginalready passes throughSuaveplanErrorsubclasses;openViaPluginalready forwardscreateReadStream)@suaveplan/drive— [x] tests run / [x] docs reviewed / [ ] no action needed (range overload is purely additive)
Affected upstreams (none breaking)
| Package | Action | Breaking? |
|---|---|---|
@suaveplan/drive |
ADD RangeReadOptions, createReadStream overload, RANGE_* codes, nativeRange cap |
no |
@suaveplan/storage-uri |
None — StreamingStorageExtensions already specifies createWriteStream + getMetadata |
no |
@suaveplan/drive-mesh |
None — advertise/unadvertise consumed via structural hook only |
no |
@suaveplan/drive-template |
None — only DriveTemplate type consumed |
no |
@suaveplan/drive-plugin-chunks |
Honour ctx.range (delivered as a sibling change inside the same wave) |
no |
@suaveplan/types |
None — DriveLike hoist tracked separately |
no |
Ordering
Unblocked. All packages exist on genesis today; the change is additive at every boundary.