6.0 KiB
6.0 KiB
Tasks: enterprise-service-discovery
Change ID: enterprise-service-discovery
Package: @suaveplan/service-discovery
Target path: universal/network/service-discovery
Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/events, @suaveplan/network, @suaveplan/retry
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 directory
universal/network/service-discovery/withsrc/,tests/,src/__benchmarks__/, and all feature subdirectories per the layout inproposal.md. - Write
package.jsonat version0.1.0with"type": "module","sideEffects": false, fullexportsmap, deps:@suaveplan/error workspace:^,@suaveplan/types workspace:^,@suaveplan/events workspace:^,@suaveplan/network workspace:^,@suaveplan/retry workspace:^,zod catalog:forms,@opentelemetry/api catalog:observability. - Write
tsconfig.jsonextending@suaveplan/typescript-config/library.json. - Write
vitest.config.tsusingcreateTestConfig()from@suaveplan/testing/config. - Add
lint,typecheck,test,test:coverage,build,docs:checkscripts.
2. Types + errors
- Write
src/types/common.ts— all interfaces and type aliases from proposal. - Write
src/types/common.md. - Write
src/error-codes.ts—ServiceDiscoveryErrorCode,SERVICE_DISCOVERY_ERROR_METADATA,registerServiceDiscoveryErrorCodes(). - Write Zod schemas:
ServiceEndpointSchema,HealthCheckSchema,ServiceRegistrationSchema,ServiceInstanceSchema,ServiceRegistryOptionsSchema,ServiceResolverOptionsSchema.
3. MemoryRegistryBackend
- Implement
MemoryRegistryBackend implements RegistryBackend—Map<string, ServiceInstance>store,register/deregister/get/list/refresh/updateStatus.listsupports optionalnameandtagsfilters (tag filter = intersection). - Write
memory-backend.test.ts(≤500 lines): register, deregister, list with filters, refresh resets TTL, updateStatus. - Write
memory-backend.md.
4. Health checks
HTTP probe
- Implement
runHttpProbe(endpoint, check, httpClient): Promise<boolean>— GET<protocol>://<host>:<port><path>, returntrueif status 2xx, false otherwise; timeout fromcheck.timeoutMs. - Write
http-probe.test.ts(uses stubHttpClient) andhttp-probe.md.
TCP probe
- Implement
runTcpProbe(endpoint, check, connectFn): Promise<boolean>— injectableconnectFn(host, port): Promise<void>(abstractsnet.connect); returnstrueon success within timeout. - Write
tcp-probe.test.tsandtcp-probe.md.
Health scheduler
- Implement
HealthScheduler— for each registered instance with ahealthCheck, runs probes onintervalMsusing injectableClock; updatesServiceInstance.statusvia backend; tracks consecutive failure/success counts; emits events on status change. - Write
health-scheduler.test.ts(≤500 lines): probe triggers, failure threshold, success threshold, stop clears timers. - Write
health-scheduler.md.
5. Load balancing strategies
- Implement
roundRobin(instances): ServiceInstance— stateful cursor (closure-based, not class-level to keep it pure). - Implement
weighted(instances): ServiceInstance— weighted random selection (cumulative weight table + RNG via injectable function). - Write
round-robin.test.ts,round-robin.md,weighted.test.ts,weighted.md.
6. ServiceRegistry
- Implement
ServiceRegistry— delegates tobackend; TTL sweeper (setInterval onClock); emitsservice:registered,service:deregistered,service:status-changedoneventsbus;startHealthChecks/stopHealthChecks. - Wire OTel: counter
service_discovery.register_total, counterservice_discovery.health_check_total. - Write
service-registry.test.ts(≤500 lines): register, deregister, listHealthy, TTL expiry, event emission. - Write
service-registry.md.
7. ServiceResolver
- Implement
ServiceResolver—resolvecallsregistry.listHealthy, applies load balancing strategy, throwsSERVICE_DISCOVERY_NO_HEALTHY_INSTANCESif empty;resolveAllreturns full healthy list. - Wire OTel: counter
service_discovery.resolve_total, histogramservice_discovery.resolve_duration_ms. - Write
service-resolver.test.ts(≤500 lines): resolve with round-robin, weighted, random, first-healthy; empty list throws; abort signal. - Write
service-resolver.md.
8. Benchmarks
- Write
src/__benchmarks__/service-discovery.bench.ts—register100 services,resolvehot path round-robin 1000 instances, health check scheduler overhead.
9. Tests
- Write
tests/integration.test.ts— full lifecycle: register → health check → resolve → deregister. - Write
tests/property.test.ts(fast-check) — round-robin covers all instances in N calls, weighted distribution converges, register+list invariants.
10. Docs + verify + publish
- All co-located
.mdfiles (≥200 words each). - Write
README.mdat package root. bunx turbo lint typecheck test --filter=@suaveplan/service-discovery— green.bunx turbo test:coverage --filter=@suaveplan/service-discovery— 100%.bun scripts/publish.ts --package @suaveplan/service-discovery.- Archive to
openspec/archive/2026-04-24-enterprise-service-discovery/.