5.5 KiB
5.5 KiB
Tasks: enterprise-server-cache-redis
Change ID: enterprise-server-cache-redis
Package: @suaveplan/cache-redis
Target path: server/data/cache-redis
Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/retry, @suaveplan/logger
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin 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.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
1. Scaffold
- Create
server/data/cache-redis/with full directory tree perproposal.mdlayout. - Write
package.jsonat version0.1.0,"type": "module","sideEffects": false, exports map with"."barrel plus subpaths foradapter,utils/key-builder. - Add dependencies:
@suaveplan/error workspace:^,@suaveplan/types workspace:^,@suaveplan/retry workspace:^,@suaveplan/logger workspace:^,zod catalog:forms,@opentelemetry/api catalog:observability,ioredis catalog:data,tslib catalog:build. - Add devDependencies:
vitest catalog:testing,@vitest/coverage-istanbul catalog:testing,@suaveplan/testing workspace:^. - Write
tsconfig.jsonextending@suaveplan/typescript-config/library-node.json. - Write
vitest.config.tsusingcreateTestConfig(). - Add
lint,typecheck,test,test:coverage,build,docs:checkscripts.
2. Types and errors
- Define
RedisCacheOptions,RedisCacheOptionsSchemainsrc/types/cache-redis.types.ts. - Write
cache-redis.types.md(≥200 words, both examples, API reference). - Implement
src/error-codes.ts:RedisCacheErrorCodeunion, metadata,registerRedisCacheErrorCodes(), eager invocation.
3. Key builder utility
- Implement
src/utils/key-builder.ts:buildKey(namespace: string, key: string): string— joins with:separator; rejects empty keys and*-glob injection; throwsCACHE_REDIS_INVALID_KEY. extractKey(namespace: string, full: string): string— strips namespace prefix for list results.- Write
key-builder.test.ts: namespace join, empty key rejection, glob injection, extract round-trip. - Write
key-builder.md(≥200 words).
4. Core adapter
- Implement
RedisCacheAdapterinsrc/adapter/redis-cache-adapter/redis-cache-adapter.ts:- Constructor validates options via
RedisCacheOptionsSchema; createsioredis.Redis(single) orioredis.Cluster(whenclusterNodespresent). connect()/disconnect()/ping(): lifecycle methods; wrap with retry on first connect; OTel spancache_redis.connect.get(key):redis.getBuffer(buildKey(...))→Uint8Array | undefined; OTel span + counter.set(key, value, ttlMs?):redis.set(k, v, 'PX', ttlMs)orredis.set(k, v)when no TTL; OTel span.delete(key):redis.del(k)→ boolean; OTel span.has(key):redis.exists(k) > 0; OTel span.list(prefix?): SCAN withMATCH namespace:prefix*using async cursor loop; OTel span.clear(): SCAN all keys in namespace then DEL in batches of 100; OTel span.ttl(key):redis.pttl(k)→ number in ms orundefinedif no TTL.- All commands wrapped with
commandTimeoutviaredis.calltimeout orAbortSignal; retry onCACHE_REDIS_CONNECT_FAILEDusing@suaveplan/retry.
- Constructor validates options via
- Export
createRedisCacheAdapter(options). - Write
redis-cache-adapter.types.tswith internal-only types. - Write
redis-cache-adapter.test.ts(≤500 lines): unit tests with a real Redis running locally — verify all CRUD, TTL expiry, namespace isolation, cluster option path (use single-node Redis). No vi.mock of ioredis. - Write
redis-cache-adapter.md(≥200 words, simple + advanced, full API reference).
5. Integration tests
- Write
tests/integration.test.ts(≤500 lines): full lifecycle — connect, set, get, has, list with prefix, delete, clear, disconnect; concurrent SET race condition; TTL expiry (use short TTL + real sleep). - Guard tests with
if (!process.env.REDIS_URL) test.skip(...)to allow CI-skip when Redis is unavailable.
6. OTel instrumentation
- Counter
cache_redis.ops_totalwithoperationandhit(bool for get) attributes. - Histogram
cache_redis.duration_msper operation. - Span per public method; record exception; end in
finally.
7. Docs
- All
.mdper layout; rootREADME.mdwith quick-start, docker-compose snippet for Redis, API table, deps table.
8. Verify and publish
bunx turbo lint --filter=@suaveplan/cache-redis— zero errors.bunx turbo typecheck --filter=@suaveplan/cache-redis— zero errors.bunx turbo test --filter=@suaveplan/cache-redis— all tests pass (start Redis before running).bunx turbo test:coverage --filter=@suaveplan/cache-redis— 100% on all touched modules.- Confirm no
TODO,FIXME,any,ts-ignore, or.skipinsrc/. bun scripts/publish.ts --package @suaveplan/cache-redis.- Update
CLAUDE.mdpackage listing. - Archive: move to
openspec/archive/2026-04-24-enterprise-server-cache-redis/.