# Tasks: enterprise-network-failover **Change ID**: enterprise-network-failover **Package**: `@suaveplan/network-failover` **Target path**: `universal/network/network-failover` **Prerequisite packages on Verdaccio**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/logger`, `@suaveplan/retry`, `@suaveplan/timing` --- ## 1. Scaffold - [x] Create `universal/network/network-failover/` with `src/`, `tests/`, and all feature subdirectories. - [x] Write `package.json` at version `0.1.0`: `"sideEffects": false`, exports map, deps per `proposal.md`. - [x] Write `tsconfig.json`, `vitest.config.ts`, scripts. --- ## 2. Types and error codes - [x] Define all public types in `src/types/common.ts`: `FailoverTarget`, `HealthCheckConfig`, `HealthStatus`, `FailoverState`, `FailoverStrategy`, `FailoverEvent`, `FailoverEventData`, `FailoverGroupOptions`. - [x] Write `src/types/common.md` (≥200 words, simple + advanced examples). - [x] Define error codes in `src/error-codes.ts`. - [x] Write Zod schemas in `src/types/schemas.ts` + `src/types/schemas.md`. --- ## 3. Failover strategies (`src/strategies/`) ### 3a. Round-robin - [x] Implement `roundRobin(targets: FailoverTarget[], lastIndex: number): FailoverTarget | null`: - Returns next healthy target in round-robin order; wraps around. - [x] Write `round-robin.test.ts` + `round-robin.md`. ### 3b. Fastest - [x] Implement `fastest(targets: FailoverTarget[]): FailoverTarget | null`: - Returns healthy target with lowest `health.latencyMs`; falls back to any healthy target. - [x] Write `fastest.test.ts` + `fastest.md`. ### 3c. Weighted - [x] Implement `weighted(targets: FailoverTarget[], rng: Rng): FailoverTarget | null`: - Normalised-weight random selection among healthy targets only. - [x] Write `weighted.test.ts` + `weighted.md`. ### 3d. Priority - [x] Implement `priority(targets: FailoverTarget[]): FailoverTarget | null`: - Returns healthy target with highest `priority` value. - [x] Write `priority.test.ts` + `priority.md`. --- ## 4. Health checker (`src/core/health-checker/`) - [x] Implement `createHealthChecker(target, options, clock)`: - Runs `options.healthCheck.probe ?? defaultProbe` on `intervalMs` interval. - Track `consecutiveFailures` / `consecutiveSuccesses`. - Emit `target-failed` after `failureThreshold` consecutive failures. - Emit `target-recovered` after `successThreshold` consecutive successes. - [x] Write `health-checker.test.ts` (≤500 lines): failure threshold fires, recovery fires, intervals. - [x] Write `health-checker.md` (≥200 words). --- ## 5. Failover group (`src/core/failover-group/`) - [x] Implement `createFailoverGroup(options)`: - Validates options with Zod. - Maintains `primary` and `targets[]`; starts health checkers for all. - On `target-failed` for primary: wait `failoverDelayMs` then run strategy to select new primary. - On `target-recovered` for non-primary: wait `failBackDelayMs`; consider fail-back only if score is higher. - OTel: span `failover.execute`, counter `failover.events_total`, histogram `failover.latency_ms`. - [x] Implement `group.execute(fn, signal?)`: - Call `fn(primary, signal)`. - On failure, immediately promote next target via strategy and retry `fn` once. - Throw `NetworkFailoverError(FAILOVER_EXECUTE_FAILED)` after all targets exhausted. - [x] Implement `group.probe(targetId, signal?)` — runs single health probe. - [x] Write `failover-group.test.ts` (≤500 lines): execute on healthy primary, primary fails → failover, all fail throws, probe. - [x] Write `failover-group.md` (≥200 words). --- ## 6. Property-based tests (`tests/property.test.ts`) - [x] Strategy completeness: when all targets are healthy, strategy always returns a target. - [x] Failover selects next healthy target after primary failure. --- ## 7. Docs and verify - [x] All per-file `.md` files per layout in `proposal.md`. - [x] `universal/network/network-failover/README.md`. - [x] `bunx turbo lint typecheck test test:coverage --filter=@suaveplan/network-failover` — all green, 100%. - [x] `bun scripts/publish.ts --package @suaveplan/network-failover`. - [x] Update `CLAUDE.md`. Archive change folder.