Initial Commit
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
# OpenSpec Change: drive-template-drive-surface
|
||||
|
||||
- **Change ID**: drive-template-drive-surface
|
||||
- **Capability**: `drive` (extending the existing surface) + cross-cutting consumption of `drive-template`
|
||||
- **Status**: SHIPPED
|
||||
- **Date**: 2026-05-04
|
||||
- **Follow-up to**: `drive-template-v1` (archived 2026-05-04)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Lands the `@suaveplan/drive` surface additions deferred from `drive-template-v1`: `Drive.hydrateFrom(template, options)`, `Drive.getKeyStatus(path)`, `Drive.getPresence(path)`, the `bus:key:grant` listener that flips encrypted-locked entries to encrypted-unlocked when a valid retroactive grant arrives, and the `FileLocationIndex` projection that backs all three. Adds the four hydration error codes (`DRIVE.NOT_FOUND`, `DRIVE.UNAVAILABLE`, `DRIVE.ALREADY_HYDRATED`, `DRIVE.HYDRATE_ABORTED`), extends `DriveEntry` with optional `keyStatus` / `presence` fields, and ships 15 integration tests across hydrate / get-key-status / get-presence / retroactive-key-grant / file-location-index test files at 100/100/100/100 coverage.
|
||||
|
||||
This change does NOT touch `@suaveplan/drive-plugin-chunks` (the inline-sig modification, DTP-25, lives in `drive-plugin-chunks-inline-sig` follow-up) and does NOT implement the cache-eviction loop (DTP-13..15 retention LRU / max-age GC) — both ship as separate changes since they require touching sibling packages.
|
||||
|
||||
---
|
||||
|
||||
## Why
|
||||
|
||||
`drive-template-v1` shipped the standalone `@suaveplan/drive-template` package (12 of 26 spec requirements package-local). The remaining requirements (DTP-22..24, DTP-21 listener integration) require modifying `@suaveplan/drive` because they extend its public surface. Splitting those into this follow-up change keeps the original drive-template ship reviewable while letting the surface additions go through their own gate cycle.
|
||||
|
||||
## What changes
|
||||
|
||||
- ADDED: `Drive.hydrateFrom(template: DriveTemplate, options?: HydrateOptions): Promise<HydrateResult>` — 8-step validation pipeline reused from `@suaveplan/drive-template`, populates `FileLocationIndex`, schedules byte fetches per `flags.autoDownload`.
|
||||
- ADDED: `Drive.getKeyStatus(path: string): KeyStatus` — synchronous three-state lookup; throws `DRIVE.NOT_FOUND` for unknown paths.
|
||||
- ADDED: `Drive.getPresence(path: string): ChunkPresence` — chunk-level progress lookup; throws `DRIVE.NOT_FOUND` for unknown paths.
|
||||
- ADDED: `Drive.fileLocationIndex` getter — exposes the index for instrumentation and tests.
|
||||
- ADDED: `FileLocationIndex` class with `set` / `getByPath` / `getByFileId` / `hasPath` / `deleteByPath` / `clear` / `size` / `[Symbol.iterator]`.
|
||||
- ADDED: `KeyStatus`, `ChunkPresence`, `HydrateOptions`, `HydrateResult`, `HydrateProgressEvent` types.
|
||||
- ADDED: `DriveOptions.ourPubkey?: PeerId` so the `key:grant` listener can filter to grants addressed to us.
|
||||
- ADDED: bus-listener registration during `hydrateFrom` for `kind: 'retroactive'` files; flips `keyStatus` from `'encrypted-locked'` to `'encrypted-unlocked'` on validated grants.
|
||||
- ADDED: 4 new `DRIVE_CODES`: `NOT_FOUND`, `UNAVAILABLE`, `ALREADY_HYDRATED`, `HYDRATE_ABORTED`.
|
||||
- MODIFIED: `DriveEntry` — optional `keyStatus?: KeyStatus` and `presence?: ChunkPresence` fields. Strictly additive at the consumer surface.
|
||||
- MODIFIED: `Drive` constructor — new optional `ourPubkey` field threaded into the hydration context.
|
||||
|
||||
Mid-iteration abort handling: the hydration loop yields between iterations via `await Promise.resolve()` so caller-side aborts scheduled via `queueMicrotask` actually land and trigger rollback. This is a behavior improvement over the synchronous-loop original draft.
|
||||
|
||||
## Out of scope (deferred to follow-ups)
|
||||
|
||||
- DTP-13..15 retention LRU / `{ maxAgeMs }` cache-eviction loop — needs deeper integration with `@suaveplan/drive-plugin-chunks` to know per-file chunk inventory.
|
||||
- DTP-25 `FileLocation.storageHints` inline-sig modification — lives on `@suaveplan/drive-plugin-chunks` (follow-up change `drive-plugin-chunks-inline-sig`).
|
||||
- DTP-13 chunk-arrival accounting (the chunks plugin updates `entry.presence` directly) — needs the chunks plugin to import `@suaveplan/drive`'s `FileLocationIndex` type.
|
||||
- CDN bad-signature mesh fall-back (DTP-18) — needs `drive-transport-https` and `drive-mesh` integration; ships with the transport packages.
|
||||
|
||||
## Migration impact
|
||||
|
||||
None. The API additions are strictly additive and the `DriveEntry` extensions are optional. Existing code that ignores the new fields continues to work unchanged. Consumers that opt in to `Drive.hydrateFrom` MUST also pass `ourPubkey` if they want retroactive key grants to land.
|
||||
|
||||
## Verification approach
|
||||
|
||||
- `tests/hydrate-from.test.ts` — successful hydration, abort cancellation, encrypted-vs-plain counting, preFetch modes (`'none'` / `'auto-download-files'`), already-hydrated rejection, progress events, mid-iteration abort with rollback.
|
||||
- `tests/get-key-status.test.ts` — three-state contract + NOT_FOUND.
|
||||
- `tests/get-presence.test.ts` — progress shape contract + NOT_FOUND.
|
||||
- `tests/retroactive-key-grant.test.ts` — listener wiring, defensive branches (unencrypted file / already-unlocked file / no-ourPubkey), bad-issuer rejection.
|
||||
- `tests/file-location-index.test.ts` — direct unit tests for the index methods.
|
||||
|
||||
15 new tests across 5 files at 100/100/100/100 coverage. Includes restoration of the previously-removed `rollback` path with a real test exercising it via `queueMicrotask`-scheduled abort (per AGENTS.md §7a).
|
||||
|
||||
## Side effect: AGENTS.md §2b and §7a additions
|
||||
|
||||
This change folder authored two new sections in `openspec/AGENTS.md`:
|
||||
|
||||
- **§2b "NEVER ship stubs, placeholders, or work-in-progress code"** — captures the user-driven rule that genesis is an enterprise-grade collection of fully working libraries, not a workspace. Bans stubs, TODOs, tautological tests, half-baked docs, and "Phase 1 / Phase 2" splits where Phase 2 is unwritten.
|
||||
- **§7a "Never delete code that serves a purpose to make tests / coverage pass"** — captures the rule that defensive guards, rollback helpers, and forward-defensive catches must be tested, not deleted. Includes an explicit list of banned anti-patterns and the priority order for what to do instead (write a test → restructure to make the path reachable → annotate as unreachable).
|
||||
|
||||
Both rules ride along here because the work that surfaced them happened during this change's implementation; future changes will reference them directly.
|
||||
@@ -0,0 +1,65 @@
|
||||
# Tasks: drive-template-drive-surface
|
||||
|
||||
**Change ID**: drive-template-drive-surface
|
||||
**Capability**: extends `drive` capability + cross-cutting consumption of `drive-template`
|
||||
**Target paths**: `packages/drive/drive/`
|
||||
|
||||
> Every checkbox MUST reflect work actually done in this change.
|
||||
|
||||
---
|
||||
|
||||
## 1. Spec
|
||||
|
||||
- [x] 1.1 `proposal.md` drafted with §Why / §What changes / §Out of scope / §Migration impact / §Verification approach
|
||||
- [x] 1.2 No spec delta file — the additions extend the existing `drive` capability spec; the canonical `openspec/specs/drive/spec.md` will fold the additions on archive
|
||||
|
||||
## 2. Implementation — `@suaveplan/drive` surface additions
|
||||
|
||||
- [x] 2.1 `src/drive/file-location-index.ts` — `FileLocationIndex` class + `FileLocationEntry` interface + `emptyPresence()` helper
|
||||
- [x] 2.2 `src/drive/hydrate-from.ts` — `Drive.hydrateFrom` orchestrator with 8-step validation pipeline, AbortSignal support, mid-iteration rollback via `await Promise.resolve()` yields
|
||||
- [x] 2.3 `src/drive/get-key-status.ts` — `Drive.getKeyStatus(path)` synchronous three-state lookup
|
||||
- [x] 2.4 `src/drive/get-presence.ts` — `Drive.getPresence(path)` chunk-level progress lookup
|
||||
- [x] 2.5 Update `src/types/drive.types.ts` — add `KeyStatus` / `ChunkPresence` / `HydrateOptions` / `HydrateResult` / `HydrateProgressEvent` types; extend `DriveEntry` with optional `keyStatus` / `presence`; extend `DriveOptions` with `ourPubkey`
|
||||
- [x] 2.6 Update `src/drive/drive.ts` — wire `hydrateFrom` / `getKeyStatus` / `getPresence` methods, expose `fileLocationIndex` getter, accept `ourPubkey` constructor option
|
||||
- [x] 2.7 Update `src/error-codes.ts` — add `NOT_FOUND` / `UNAVAILABLE` / `ALREADY_HYDRATED` / `HYDRATE_ABORTED` codes
|
||||
- [x] 2.8 Bus listener registration in `hydrateFrom` for `kind: 'retroactive'` files, with `validateKeyGrant` from `@suaveplan/drive-template`
|
||||
- [x] 2.9 Sibling `.md` for every new implementation file (`hydrate-from.md`, `get-key-status.md`, `get-presence.md`, `file-location-index.md`)
|
||||
- [x] 2.10 Update `src/index.ts` barrel — export new types and surface
|
||||
- [x] 2.11 Update `package.json` — add `@suaveplan/drive-template` runtime dep + `@suaveplan/crypto-curve` / `@suaveplan/crypto-utils` dev deps for tests
|
||||
- [x] 2.12 Update `vite.config.ts` externals
|
||||
- [x] 2.13 ~~Cache-eviction loop honoring RetentionPolicy per file~~ — DEFERRED. Needs tighter integration with `@suaveplan/drive-plugin-chunks` to know per-file chunk inventory; ships in `drive-template-retention-loop` follow-up.
|
||||
|
||||
## 3. Tests
|
||||
|
||||
- [x] 3.1 `tests/hydrate-from.test.ts` — successful hydration, abort cancellation (pre + mid-iteration with rollback), encrypted-vs-plain counting, preFetch modes, already-hydrated rejection, progress events, NOT_INITIALIZED guard
|
||||
- [x] 3.2 `tests/get-key-status.test.ts` — three-state contract + NOT_FOUND
|
||||
- [x] 3.3 `tests/get-presence.test.ts` — progress shape contract + NOT_FOUND
|
||||
- [x] 3.4 `tests/retroactive-key-grant.test.ts` — valid grant flips lock, defensive branches (unknown fileId / unencrypted / already-unlocked / no-ourPubkey), bad-issuer rejection
|
||||
- [x] 3.5 `tests/file-location-index.test.ts` — direct unit tests for the index methods (set / getByPath / getByFileId / deleteByPath / clear / iterate / emptyPresence)
|
||||
- [x] 3.6 ~~`tests/policy-meshallowed-false.test.ts`~~ — DEFERRED with the cache-eviction work; no current code path emits `cd:request` directly from drive proper
|
||||
- [x] 3.7 ~~`tests/policy-cdnallowed-false.test.ts`~~ — DEFERRED; needs `drive-transport-https` integration
|
||||
- [x] 3.8 ~~`tests/policy-autodownload.test.ts`~~ — covered by hydrate-from's `preFetch:'auto-download-files'` test
|
||||
- [x] 3.9 ~~`tests/retention-lru.test.ts`~~ — DEFERRED with cache-eviction work
|
||||
- [x] 3.10 ~~`tests/retention-max-age.test.ts`~~ — DEFERRED with cache-eviction work
|
||||
- [x] 3.11 ~~`tests/inline-key-unlock.test.ts`~~ — covered by retroactive-key-grant; inline (sealed-box) key unwrap lives on `@suaveplan/crypto` not drive
|
||||
- [x] 3.12 `tests/list-with-presence.test.ts` — covered by getKeyStatus / getPresence direct tests; `Drive.list` enrichment ships with the chunks-plugin integration
|
||||
|
||||
## 4. Pre-archive verification
|
||||
|
||||
- [x] 4.1 `bunx turbo lint typecheck test --filter=@suaveplan/drive` — green (88 tests across 13 files)
|
||||
- [x] 4.2 `bunx turbo test:coverage --filter=@suaveplan/drive` — 100/100/100/100
|
||||
- [x] 4.3 `bunx turbo build --filter=@suaveplan/drive` — green
|
||||
- [x] 4.4 `bunx turbo lint typecheck test:coverage --filter=@suaveplan/drive-template` — green (drive-template's verify catch arms restored with injectable `verifyFn` per AGENTS.md §7a; verify-catch-arms.test.ts exercises the catch path)
|
||||
- [x] 4.5 `bun run docs:check --filter=@suaveplan/drive` — every implementation `.ts` has a sibling ≥200-word `.md` (pre-existing stub docs were filled in as part of this change)
|
||||
|
||||
## 5. AGENTS.md additions (cross-cutting)
|
||||
|
||||
- [x] 5.1 §2b "NEVER ship stubs, placeholders, or work-in-progress code" added to `openspec/AGENTS.md`
|
||||
- [x] 5.2 §7a "Never delete code that serves a purpose to make tests / coverage pass" added to `openspec/AGENTS.md`
|
||||
- [x] 5.3 §7's "Tautological tests" bullet strengthened with explicit `expect(true).toBe(true)` ban + reviewer guidance
|
||||
|
||||
## 6. Archive
|
||||
|
||||
- [x] 6.1 Move `openspec/changes/drive-template-drive-surface/` → `openspec/archive/2026-05-04-drive-template-drive-surface/`
|
||||
- [x] 6.2 Update `CLAUDE.md` `@suaveplan/drive` package bullet with new surface
|
||||
- [x] 6.3 Refresh `MANIFEST.md`
|
||||
Reference in New Issue
Block a user