95 lines
7.3 KiB
Markdown
95 lines
7.3 KiB
Markdown
# Tasks: complete-drive
|
|
|
|
> All tasks land in `packages/drive/drive/`. Follow CLAUDE.md `### Quality` and
|
|
> `openspec/AGENTS.md` §13 pre-ship gates. Coverage gate is 100/100/100/100.
|
|
|
|
## 0. Pre-flight (non-negotiable)
|
|
|
|
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
|
|
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
|
|
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
|
|
|
|
## 1. Audit & spec lock-in
|
|
|
|
- [ ] 1.1 Snapshot current public surface — `bunx turbo build --filter=@suaveplan/drive` then grep `dist/index.d.ts` for the exported symbol list; paste into design.md `## Today's surface`.
|
|
- [ ] 1.2 Confirm sibling-change scope boundaries with `complete-drive-uri` (URI/locator owner) and `complete-types-drive-promotion` (cross-package types owner) — no duplicate requirements.
|
|
|
|
## 2. Error-code registry
|
|
|
|
- [ ] 2.1 Extend `src/error-codes.ts` `DRIVE_CODES` with `COPY_FAILED`, `MOVE_FAILED`, `WATCH_FAILED`, `QUOTA_EXCEEDED`, `OPERATION_ABORTED`, `STORAGE_READONLY` (keeps `defineCodes("DRIVE", [...])` frozen).
|
|
- [ ] 2.2 Update `src/error-codes.md` and the conformance test `src/error-codes.spec-conformance.test.ts` to assert the wider set.
|
|
|
|
## 3. Op surface widening
|
|
|
|
- [ ] 3.1 Add `DeleteOptions`, `StatOptions`, `ReadStreamOptions`, `WriteStreamOptions` to `src/types/drive.types.ts`; export them from `src/index.ts`.
|
|
- [ ] 3.2 Add overload signatures on `Drive.delete`/`Drive.stat`/`Drive.createReadStream`/`Drive.createWriteStream` accepting the options bundle (positional `signal?` kept as a second overload for one minor cycle).
|
|
- [ ] 3.3 Implement range-read in `src/file-operations/streams.ts` using `storage.createReadStream({ offset, length })` when `capabilities.rangeReads === true`, else slice in the default fallback.
|
|
|
|
## 4. Copy / move
|
|
|
|
- [ ] 4.1 New module `src/file-operations/copy.ts` + `.md`: middleware-wrapped copy; uses `storage.copy?` when available, else read-then-write; emits `drive:copy:before`/`drive:copy:after`. Errors surface as `DriveError(DRIVE.COPY_FAILED, { cause })`.
|
|
- [ ] 4.2 New module `src/file-operations/move.ts` + `.md`: same shape; uses `storage.move?` (rename), else copy + delete with rollback on failure; emits `drive:move:before`/`drive:move:after`. Errors surface as `DriveError(DRIVE.MOVE_FAILED, { cause })`.
|
|
- [ ] 4.3 Wire `Drive.copy(src, dst, opts?)` and `Drive.move(src, dst, opts?)` through `PluginRegistry.runMiddleware`. Add `copy?`/`move?` slots to `DriveMiddleware`.
|
|
|
|
## 5. List typing
|
|
|
|
- [ ] 5.1 Replace `Promise<unknown>` return on `Drive.list` with `Promise<DriveListPage>` where `DriveListPage = { entries: DriveEntry[]; cursor?: string }`.
|
|
- [ ] 5.2 Add `Drive.scan(prefix?, opts?): AsyncIterable<DriveEntry>` that auto-paginates and respects `signal`.
|
|
|
|
## 6. Watch
|
|
|
|
- [ ] 6.1 New module `src/file-operations/watch.ts` + `.md`: `Drive.watch(path, opts?)` returns `{ events: AsyncIterable<DriveChangeEvent>; unsubscribe(): void }`. Backed by the injected `EventBusLike`. Falls back to a per-drive in-memory bus when none injected (drive-local fan-out only).
|
|
- [ ] 6.2 Emitters on `write`/`delete`/`copy`/`move` publish `DriveChangeEvent` to bus topic `drive:change:<path>`.
|
|
|
|
## 7. Plugin lifecycle
|
|
|
|
- [ ] 7.1 Extend `PluginRegistry` with `unload(name)`, `list()`, `find(name)` and an ordered handle map keyed by `plugin.name`.
|
|
- [ ] 7.2 Expose `Drive.unloadPlugin(name)`, `Drive.listPlugins()`, `Drive.findPlugin(name)` on the class. Unload is idempotent; unknown name throws `DriveError(DRIVE.PLUGIN_FAILED, ...)`.
|
|
- [ ] 7.3 Document install/unload ordering invariants in `src/plugins/registry.md`.
|
|
|
|
## 8. Quota / usage
|
|
|
|
- [ ] 8.1 New module `src/observability/usage.ts` + `.md`: `Drive.usage(): Promise<DriveUsage>` aggregating per-storage byte counts via `storage.usage?()` when available, else by summing `stat().size` over `scan()`. Cached for `options.usageCacheMs` (default 5s).
|
|
- [ ] 8.2 New module `src/observability/quota.ts` + `.md`: `Drive.setQuota({ soft?, hard? })`. On every `write`/`copy`/`move`, post-op `usage()` check; emit `drive:quota:warning` on soft trip and throw `DriveError(DRIVE.QUOTA_EXCEEDED)` on hard.
|
|
|
|
## 9. Sharing-integration hook
|
|
|
|
- [ ] 9.1 In `src/types/drive.types.ts` add `DriveShareLike` interface (`share(path, audience): Promise<ShareHandle>`, `revoke(handle): Promise<void>`, `listShares(path?): Promise<ShareHandle[]>`). Drive owns the interface; concrete impl lives in `drive-sharing`.
|
|
- [ ] 9.2 Add `options.shareHook?: DriveShareLike` to `DriveOptions`; expose `Drive.share`/`Drive.revoke`/`Drive.listShares` that delegate to the hook or throw `DriveError(DRIVE.PLUGIN_FAILED, "no share hook")`.
|
|
|
|
## 10. Hydration progress events
|
|
|
|
- [ ] 10.1 In `src/drive/hydrate-from.ts`, alongside the existing `onProgress` callback, publish `drive:hydrate:progress` events to the EventBus when injected. Add `drive:hydrate:complete` and `drive:hydrate:aborted`.
|
|
- [ ] 10.2 Update `hydrate-from.md` examples + add a watch-the-bus advanced example.
|
|
|
|
## 11. OTEL spans
|
|
|
|
- [ ] 11.1 Wrap each public op (`read`, `write`, `delete`, `stat`, `list`, `scan`, `copy`, `move`, `watch`, `hydrateFrom`, `usage`) in a span named `drive.<op>` with attributes `drive.path`, `drive.storage.id`, `drive.bytes` where applicable. Use `@opentelemetry/api` directly; no-op when no exporter registered.
|
|
|
|
## 12. Tests
|
|
|
|
- [ ] 12.1 Per-feature unit tests (`.test.ts`) for every new module.
|
|
- [ ] 12.2 Extend `drive.spec-conformance.test.ts` with scenarios for every new requirement (one scenario per `**Verified by:**` line in the spec delta).
|
|
- [ ] 12.3 fast-check property tests: `copy(a,b);read(b) === read(a)`, `move` invariant `(after) !exists(a) && exists(b)`, `usage()` monotonicity across N writes, `watch` delivers every change.
|
|
- [ ] 12.4 Verify `bunx turbo test:coverage --filter=@suaveplan/drive` reports 100/100/100/100 (istanbul). No `c8 ignore`.
|
|
|
|
## 13. Docs
|
|
|
|
- [ ] 13.1 Sibling `.md` for every new `.ts`, ≥200 words, basic + advanced examples (CLAUDE.md `### Documentation`).
|
|
- [ ] 13.2 Update `packages/drive/drive/README.md` Modules section with every new feature.
|
|
- [ ] 13.3 Refresh `src/drive/drive.md` to list the widened surface and the new option fields.
|
|
|
|
## 14. Pre-ship gate
|
|
|
|
- [ ] 14.1 `bunx turbo lint typecheck test --filter=@suaveplan/drive` green.
|
|
- [ ] 14.2 `bunx turbo test:coverage --filter=@suaveplan/drive` 100%.
|
|
- [ ] 14.3 `bunx turbo build --filter=@suaveplan/drive` green.
|
|
- [ ] 14.4 Banned-pattern + barrel-shape + test-runner-import scans clean (AGENTS §13 steps 4, 4a, 4b).
|
|
- [ ] 14.5 Dependent sweep: `bunx turbo test --filter=...@suaveplan/drive` green.
|
|
- [ ] 14.6 `bun scripts/folder-reorg/generate-manifest.ts` + `bun scripts/generate-missing.ts`; refresh `STATUS.md` / `SUMMARY.md`; `bunx markdownlint-cli2 --fix`.
|
|
|
|
## 15. Archive
|
|
|
|
- [ ] 15.1 Fold `specs/drive/spec.md` into `openspec/specs/drive/spec.md`.
|
|
- [ ] 15.2 Move `openspec/changes/complete-drive/` → `openspec/archive/<yyyy-mm-dd>-complete-drive/`.
|