Files
2026-05-27 23:29:53 +01:00

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 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. Scaffold

  • Create directory universal/network/service-discovery/ with src/, tests/, src/__benchmarks__/, and all feature subdirectories per the layout in proposal.md.
  • Write package.json at version 0.1.0 with "type": "module", "sideEffects": false, full exports map, 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.json extending @suaveplan/typescript-config/library.json.
  • Write vitest.config.ts using createTestConfig() from @suaveplan/testing/config.
  • Add lint, typecheck, test, test:coverage, build, docs:check scripts.

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. list supports optional name and tags filters (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>, return true if status 2xx, false otherwise; timeout from check.timeoutMs.
  • Write http-probe.test.ts (uses stub HttpClient) and http-probe.md.

TCP probe

  • Implement runTcpProbe(endpoint, check, connectFn): Promise<boolean> — injectable connectFn(host, port): Promise<void> (abstracts net.connect); returns true on success within timeout.
  • Write tcp-probe.test.ts and tcp-probe.md.

Health scheduler

  • Implement HealthScheduler — for each registered instance with a healthCheck, runs probes on intervalMs using injectable Clock; updates ServiceInstance.status via 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 to backend; TTL sweeper (setInterval on Clock); emits service:registered, service:deregistered, service:status-changed on events bus; startHealthChecks/stopHealthChecks.
  • Wire OTel: counter service_discovery.register_total, counter service_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 — resolve calls registry.listHealthy, applies load balancing strategy, throws SERVICE_DISCOVERY_NO_HEALTHY_INSTANCES if empty; resolveAll returns full healthy list.
  • Wire OTel: counter service_discovery.resolve_total, histogram service_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 — register 100 services, resolve hot 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 .md files (≥200 words each).
  • Write README.md at 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/.