@git-stunts/alfred
Advanced tools
+7
-0
@@ -8,2 +8,9 @@ # Changelog | ||
| ## [0.8.0] - 2026-02-03 | ||
| ### Changed | ||
| - Repository converted to a monorepo with `@git-stunts/alfred` as a workspace package. | ||
| - Release tooling updated; no API changes in the core package. | ||
| ## [0.7.0] - 2026-02-02 | ||
@@ -10,0 +17,0 @@ |
+3
-4
| { | ||
| "name": "@git-stunts/alfred", | ||
| "version": "0.7.0", | ||
| "version": "0.8.0", | ||
| "description": "Production-grade resilience patterns for async ops: retry/backoff+jitter, circuit breaker, bulkhead, timeout.", | ||
| "license": "Apache-2.0", | ||
@@ -17,9 +16,9 @@ "exports": { | ||
| "examples", | ||
| "scripts", | ||
| "test", | ||
| "node_modules", | ||
| "coverage", | ||
| ".editorconfig", | ||
| "eslint.config.js", | ||
| "package-lock.json", | ||
| "ROADMAP.md", | ||
| "scripts", | ||
| "setup-hooks.sh" | ||
@@ -26,0 +25,0 @@ ] |
+1
-2
| { | ||
| "name": "@git-stunts/alfred", | ||
| "version": "0.7.0", | ||
| "version": "0.8.0", | ||
| "description": "Production-grade resilience patterns for async ops: retry/backoff+jitter, circuit breaker, bulkhead, timeout.", | ||
@@ -43,3 +43,2 @@ "type": "module", | ||
| "CHANGELOG.md", | ||
| "ROADMAP.md", | ||
| "jsr.json", | ||
@@ -46,0 +45,0 @@ "package.json" |
+6
-0
@@ -43,2 +43,8 @@ # @git-stunts/alfred | ||
| ## Roadmap | ||
| See the ecosystem roadmap at [ROADMAP.md](https://github.com/git-stunts/alfred/blob/main/ROADMAP.md). | ||
| --- | ||
| ## 20-second win | ||
@@ -45,0 +51,0 @@ |
-693
| <!-- SPDX-License-Identifier: Apache-2.0 --> | ||
| <!-- © 2026 James Ross Ω FLYING•ROBOTS --> | ||
| # ROADMAP — @git-stunts/alfred | ||
| Alfred is a **policy engine** for async resilience: composable, observable, testable — and eventually **operable** (live-tunable by ID without redeploy). | ||
| ## North Star | ||
| > "Need to adjust that bulkhead? No redeploy needed." | ||
| Alfred becomes a **runtime-controlled resilience layer**: | ||
| - **Policies are pure + composable** | ||
| - **Config is live + addressable by ID** | ||
| - **Telemetry is first-class** | ||
| - **Testing is deterministic** (no time-based flakiness) | ||
| ## Guiding Principles | ||
| 1. **Core stays dependency-free.** Heavy integrations live in satellites. | ||
| 2. **Deterministic under TestClock.** No "sleep in tests" nonsense. | ||
| 3. **No global state.** Control plane is an object you pass in. | ||
| 4. **Validated updates only.** Parse/validate before apply; reject bad writes. | ||
| 5. **Audit first.** Every config change attempt is recordable (even denied). | ||
| ## Version Milestones | ||
| - [x] v0.5 — Correctness & Coherence | ||
| - [x] v0.6 — Typed Knobs & Stable Semantics | ||
| - [x] v0.7 — Rate Limiting (Throughput) Policy | ||
| - [ ] v0.8 — Control Plane Core (In-Memory) | ||
| - [ ] v0.9 — Live Policies by ID (No Redeploy) | ||
| - [ ] v0.10 — Transport + Router + Audit Ordering | ||
| - [ ] v1.0 — Production Contract Release | ||
| --- | ||
| ## Milestone v0.5 — Correctness & Coherence | ||
| Goal: eliminate credibility leaks (docs inconsistencies, missing imports), and make the "algebra" obvious and documented. | ||
| ### v0.5.1 — README: Fix copy/paste correctness + feature discovery | ||
| **User story** | ||
| As a developer evaluating Alfred, I want the first example to run without edits so I can trust the library. | ||
| **Requirements** | ||
| - Quick Start imports match usage (bulkhead included). | ||
| - Quote uses valid, copy-safe JS (double quotes inside code). | ||
| - Add a minimal "What you get" line: retry/circuit/bulkhead/timeout/hedge + TestClock + telemetry. | ||
| **Acceptance criteria** | ||
| - README Quick Start runs as-is in Node 20. | ||
| - No missing import in any snippet. | ||
| - Hedge is mentioned at least once as a feature (even if not fully documented yet). | ||
| **Scope** | ||
| - README edits only. | ||
| **Out of scope** | ||
| - New API features. | ||
| - Major README restructure (save that for 0.5+). | ||
| **Test spec** | ||
| - Golden: copy/paste snippets into examples/readme-smoke.test.js and execute. | ||
| - Known failures: none acceptable. | ||
| - Fuzz/stress: N/A. | ||
| **Definition of done** | ||
| - CI includes a "README smoke" test that runs at least the Quick Start snippet. | ||
| --- | ||
| ### v0.5.2 — Docs: "Policy algebra" section (wrap/or/race semantics) | ||
| **User story** | ||
| As a user, I want to understand composition quickly so I can build real stacks without reading source. | ||
| **Requirements** | ||
| - Document equivalences: | ||
| - wrap ≈ sequential composition ("A wraps B") | ||
| - or ≈ fallback | ||
| - race/hedge ≈ concurrent first-success patterns | ||
| - Document precedence (or lack thereof) for fluent API vs functional compose. | ||
| **Acceptance criteria** | ||
| - README has an "Algebra" section with 3 examples: | ||
| - timeout + retry + bulkhead stack | ||
| - fast timeout fallback to slower stack | ||
| - hedge/race pattern | ||
| **Scope** | ||
| - README + small examples folder if needed | ||
| **Out of scope** | ||
| - Adding new operators or parser for expressions (like A + B | C text) | ||
| **Test spec** | ||
| - Golden: examples run in Node 20 and Deno/Bun if you've got CI matrix. | ||
| - Known failures: none. | ||
| - Fuzz/stress: N/A. | ||
| **Definition of done** | ||
| - Docs merged + examples pass CI. | ||
| --- | ||
| ### v0.5.3 — Timeout determinism: allow optional clock injection | ||
| **User story** | ||
| As a maintainer, I want deterministic timeout tests using TestClock so the test suite is fast and stable. | ||
| **Requirements** | ||
| - timeout(ms, fn, options) accepts options.clock (or options.timer) consistent with the rest of the library. | ||
| - Uses clock for scheduling and elapsed calculation (not Date.now()). | ||
| - Preserves current runtime behavior if no clock provided. | ||
| **Acceptance criteria** | ||
| - New tests prove timeout works without real delays. | ||
| - Existing timeout behavior remains unchanged in Node. | ||
| **Scope** | ||
| - timeout implementation + tests | ||
| - update testing exports if needed | ||
| **Out of scope** | ||
| - Deadline propagation across nested policies (that's later). | ||
| **Test spec** | ||
| - Golden: | ||
| - timeout triggers after virtual time advance | ||
| - timeout does not trigger if operation finishes before deadline | ||
| - Known failures: | ||
| - ensure AbortError / cancellation doesn't report as timeout | ||
| - Fuzz/stress: | ||
| - random operation durations under TestClock (100–500 iterations) | ||
| - ensure no leaked timers / pending promises | ||
| **Definition of done** | ||
| - Timeout tests run in <1s total, no sleeps. | ||
| - No flakiness across 20 CI runs. | ||
| --- | ||
| ## Milestone v0.6 — Typed Knobs & Stable Semantics | ||
| Goal: normalize option resolution/snapshot timing so future "live edit" doesn't create chaos. | ||
| ### v0.6.1 — Normalize Resolvable resolution rules across policies | ||
| **User story** | ||
| As a user, I want predictable semantics when passing functions/handles for options (e.g. limit, retries). | ||
| **Requirements** | ||
| - Document and implement: which options resolve "per execute" vs "per attempt" vs "per admission". | ||
| - Standardize: | ||
| - Retry: resolve most options once per execute (except maybe shouldRetry) | ||
| - Bulkhead: resolve limit/queueLimit per admission | ||
| - Circuit breaker: resolve thresholds per event | ||
| - Hedge: resolve delay/maxHedges per execute | ||
| **Acceptance criteria** | ||
| - Behavior is explicit in docs. | ||
| - Tests assert resolution timing for each policy. | ||
| **Scope** | ||
| - Policy implementations + tests + docs | ||
| **Out of scope** | ||
| - Full control plane integration | ||
| **Test spec** | ||
| - Golden: "resolution timing" tests with counters (no real delays) | ||
| - Known failures: inconsistent resolution across policy constructors | ||
| - Fuzz/stress: randomized options functions; ensure no repeated unexpected calls | ||
| **Definition of done** | ||
| - A single doc section "Resolution timing" exists + tests cover it. | ||
| --- | ||
| ### v0.6.2 — Hedge docs + guardrails | ||
| **User story** | ||
| As a user, I want to use hedged requests safely without accidentally DDOSing myself. | ||
| **Requirements** | ||
| - Docs include: | ||
| - "Only hedge idempotent operations" | ||
| - "Prefer hedge with bulkhead + circuit breaker" | ||
| - "Use AbortSignal to cancel losing hedges" | ||
| - Provide 1–2 recipes: | ||
| - hedgeRead (read-only) | ||
| - happyEyeballsFetch (two endpoints) | ||
| **Acceptance criteria** | ||
| - README includes one hedge example that composes with bulkhead + timeout | ||
| - Mention Promise.any requirement for older runtimes. | ||
| **Scope** | ||
| - Docs + examples | ||
| **Out of scope** | ||
| - Implementing automatic idempotency detection (lol) | ||
| **Test spec** | ||
| - Golden: hedge cancels losing branch; no unhandled rejections | ||
| - Known failures: losing branch continues running after winner resolves | ||
| - Fuzz/stress: 1000 races under TestClock/simulated clock | ||
| **Definition of done** | ||
| - Docs land + one "hedge example" test runs in CI. | ||
| --- | ||
| ## Milestone v0.7 — Rate Limiting Policy | ||
| Goal: add throughput control (token bucket / GCRA). Bulkhead limits concurrency; rate limit limits requests per time. | ||
| ### v0.7.1 — Implement rateLimit(options) policy | ||
| **User story** | ||
| As a user, I need to limit request rate (RPS) without external infrastructure. | ||
| **Requirements** | ||
| - Token bucket or GCRA implementation | ||
| - Options: | ||
| - rate (tokens/sec) | ||
| - burst (max tokens) | ||
| - optional clock (TestClock support) | ||
| - Failure mode: reject immediately with RateLimitExceededError (or optionally queue—default reject) | ||
| **Acceptance criteria** | ||
| - Deterministic tests for: | ||
| - steady-state throughput | ||
| - burst handling | ||
| - refill over time | ||
| - Works in Node/Bun/Deno (no Node-only APIs) | ||
| **Scope** | ||
| - New policy + error type + d.ts + tests | ||
| **Out of scope** | ||
| - Distributed rate limiting | ||
| - Adaptive/autotuning | ||
| **Test spec** | ||
| - Golden: | ||
| - allow N ops per second under TestClock | ||
| - burst allows immediate spikes then throttles | ||
| - Known failures: | ||
| - floating point drift (use integer math if possible) | ||
| - Fuzz/stress: | ||
| - random request schedules (10k events) under TestClock | ||
| - ensure no negative tokens / overflow | ||
| **Definition of done** | ||
| - New policy documented + covered + exports stable. | ||
| --- | ||
| ## Milestone v0.8 — Control Plane Core (In-Memory) | ||
| Goal: the minimum viable control plane, Nine Lives style: ReadConfig / WriteConfig against a registry. | ||
| ### v0.8.1 — Implement Adaptive<T> + ConfigRegistry | ||
| **User story** | ||
| As an operator/dev, I want to change policy parameters at runtime in-process, safely. | ||
| **Requirements** | ||
| - Adaptive<T>: | ||
| - get(): T | ||
| - set(next: T): void | ||
| - update(fn): void | ||
| - metadata: version, updatedAt | ||
| - ConfigRegistry: | ||
| - register(path, adaptive, { parse, format }) | ||
| - keys(), read(path), write(path, valueString) | ||
| - Validate before apply: parsing failure rejects, old value preserved. | ||
| **Acceptance criteria** | ||
| - Registry supports: | ||
| - list keys | ||
| - read current values | ||
| - write new values | ||
| - write rejects invalid value and does not mutate config | ||
| **Scope** | ||
| - In-memory only | ||
| **Out of scope** | ||
| - Persistence | ||
| - Networking | ||
| - Auth | ||
| **Test spec** | ||
| - Golden: | ||
| - write valid value updates version and updatedAt | ||
| - write invalid value returns structured error and preserves old | ||
| - Known failures: | ||
| - partial update leaving config in invalid state | ||
| - Fuzz/stress: | ||
| - 10k random writes including invalid; assert invariants hold | ||
| **Definition of done** | ||
| - Registry is stable, documented, and unit tested heavily. | ||
| --- | ||
| ### v0.8.2 — Define command model (ReadConfig/WriteConfig/ListConfig) | ||
| **User story** | ||
| As a tool author, I want a stable command API that can be transported over any protocol. | ||
| **Requirements** | ||
| - Command types: | ||
| - read_config { path } | ||
| - write_config { path, value } | ||
| - list_config { prefix? } | ||
| - Result model: | ||
| - ok: true, data | ||
| - ok: false, error { code, message, details } | ||
| **Acceptance criteria** | ||
| - Router executes commands against registry and returns canonical results. | ||
| **Scope** | ||
| - Internal router API only | ||
| **Out of scope** | ||
| - Wire format | ||
| - Auth/audit (next milestones) | ||
| **Test spec** | ||
| - Golden: read/write/list flows | ||
| - Known failures: unknown key, invalid key path, invalid value | ||
| - Fuzz/stress: random paths/prefixes | ||
| **Definition of done** | ||
| - Commands are stable + used by at least one example script. | ||
| --- | ||
| ## Milestone v0.9 — Live Policies by ID | ||
| Goal: Policies become operable: "liveBulkhead('bulkhead.api')" etc. | ||
| ### v0.9.1 — Add Policy.live\* constructors (retry/bulkhead/circuit/timeout) | ||
| **User story** | ||
| As an operator, I want to tune a live system without redeploying, using stable IDs. | ||
| **Requirements** | ||
| - Add constructors: | ||
| - Policy.liveRetry(id, plane, defaults?) | ||
| - Policy.liveBulkhead(id, plane, defaults?) | ||
| - Policy.liveCircuitBreaker(id, plane, defaults?) | ||
| - Policy.liveTimeout(id, plane, defaults?) | ||
| - Defaults: | ||
| - if key missing, register defaults automatically OR require explicit plane.define (pick one; I recommend explicit) | ||
| - Snapshot semantics: | ||
| - Retry/Timeout: per execute | ||
| - Bulkhead: per admission | ||
| - Circuit: per event | ||
| **Acceptance criteria** | ||
| - Demo: adjust bulkhead limit live and observe admission behavior change without restart. | ||
| **Scope** | ||
| - In-process control plane only | ||
| **Out of scope** | ||
| - Remote control protocols | ||
| - Auth/audit | ||
| **Test spec** | ||
| - Golden: | ||
| - update bulkhead limit from 10→1 blocks new admissions | ||
| - update retry retries from 3→6 changes future operations | ||
| - Known failures: | ||
| - changing retry mid-operation causes inconsistent attempt schedule (avoid by per-execute snapshot) | ||
| - Fuzz/stress: | ||
| - concurrent executions while config changes (1k iterations) | ||
| **Definition of done** | ||
| - Live policies are documented + tested + examples exist. | ||
| --- | ||
| ### v0.9.2 — Resizable bulkhead semantics (soft shrink) | ||
| **User story** | ||
| As an operator, I want to reduce concurrency safely without killing in-flight operations. | ||
| **Requirements** | ||
| - Implement safe shrink: | ||
| - decreasing limit prevents new admissions until active <= limit | ||
| - never cancels running tasks | ||
| - Queue limit updates only affect new enqueues | ||
| **Acceptance criteria** | ||
| - A test proves: | ||
| - start 10 ops, shrink to 2: no cancellations, but new admissions wait/reject correctly. | ||
| **Scope** | ||
| - Bulkhead internals + tests | ||
| **Out of scope** | ||
| - Preemptive cancellation | ||
| - Fairness guarantees beyond "reasonable FIFO" if queue exists | ||
| **Test spec** | ||
| - Golden: | ||
| - grow behaves immediately | ||
| - shrink is soft | ||
| - Known failures: | ||
| - deadlocks under shrink pressure | ||
| - Fuzz/stress: | ||
| - random limit changes under load for 5k ops | ||
| **Definition of done** | ||
| - No deadlocks, no leaked queued items, no incorrect stats. | ||
| --- | ||
| ## Milestone v0.10 — Transport + Router + Audit Ordering | ||
| Goal: make the control plane operable outside the process: JSONL transport + canonical envelope + audit-first ordering. | ||
| ### v0.10.1 — Canonical transport envelope + JSONL codec | ||
| **User story** | ||
| As a CLI/tooling user, I want to send commands to Alfred over stdin/stdout safely and predictably. | ||
| **Requirements** | ||
| - Envelope: | ||
| - id, cmd, args, optional auth | ||
| - JSONL codec: | ||
| - decode per line | ||
| - encode per result | ||
| - Strict validation: reject unknown fields and malformed payloads | ||
| **Acceptance criteria** | ||
| - node scripts/alfredctl.js can: | ||
| - list keys | ||
| - read config | ||
| - write config | ||
| **Scope** | ||
| - JSONL only | ||
| **Out of scope** | ||
| - HTTP/gRPC (future satellites) | ||
| **Test spec** | ||
| - Golden: | ||
| - round-trip encode/decode | ||
| - command execution via JSONL harness | ||
| - Known failures: | ||
| - partial lines, invalid JSON, huge payload lines | ||
| - Fuzz/stress: | ||
| - randomized JSONL lines and junk input (1k–5k lines) | ||
| **Definition of done** | ||
| - Transport is stable and exercised by CI integration tests. | ||
| --- | ||
| ### v0.10.2 — Audit-first pipeline + auth hooks | ||
| **User story** | ||
| As an operator, I need a complete audit trail of config change attempts (even denied or invalid). | ||
| **Requirements** | ||
| - Pipeline order: | ||
| 1. Audit (attempt) | ||
| 2. Auth (optional) | ||
| 3. Validate + Execute | ||
| 4. Audit (result) | ||
| - Auth provider interface (pluggable): | ||
| - start with allowAll / opaqueToken provider | ||
| **Acceptance criteria** | ||
| - Every command produces 2 audit events (attempt + result) OR one combined event with status. | ||
| - Invalid commands are audited too. | ||
| **Scope** | ||
| - Local audit sink(s): console + in-memory | ||
| - auth as hooks | ||
| **Out of scope** | ||
| - Real security claims (that's v1+ with threat model + external review) | ||
| **Test spec** | ||
| - Golden: | ||
| - audit sees denied write | ||
| - audit sees invalid JSON | ||
| - audit sees successful write | ||
| - Known failures: | ||
| - audit not firing on exceptions | ||
| - Fuzz/stress: | ||
| - throw errors in handlers; ensure audit still records "failed" | ||
| **Definition of done** | ||
| - Audit is impossible to bypass accidentally (tests enforce it). | ||
| --- | ||
| ## Milestone v1.0 — Production Contract Release | ||
| Goal: stop moving cheese. API stability, deterministic tests, docs complete, and "operable" story is real. | ||
| ### v1.0.1 — API freeze + deprecation policy | ||
| **User story** | ||
| As an adopter, I want confidence that upgrades won't silently break my system. | ||
| **Requirements** | ||
| - Declare public API surface: | ||
| - exports and subpath exports | ||
| - error types and event types | ||
| - control plane command schema | ||
| - Deprecation policy in README/CONTRIBUTING: | ||
| - how long deprecations live | ||
| - what constitutes breaking change | ||
| **Acceptance criteria** | ||
| - A "Public API" section exists and matches actual exports. | ||
| **Scope** | ||
| - Docs + minor code guards | ||
| **Out of scope** | ||
| - Big refactors | ||
| **Test spec** | ||
| - Golden: export snapshot test (assert export keys stable) | ||
| - Known failures: none | ||
| - Fuzz/stress: N/A | ||
| **Definition of done** | ||
| - Export snapshot is enforced by CI. | ||
| --- | ||
| ### v1.0.2 — Deterministic test suite: no sleeps, no flakes | ||
| **User story** | ||
| As a maintainer, I want tests that are fast and don't fail randomly in CI. | ||
| **Requirements** | ||
| - All time-based policies support TestClock (retry/backoff/jitter/timeout/rateLimit/hedge delays). | ||
| - No setTimeout-based tests except maybe one smoke test. | ||
| **Acceptance criteria** | ||
| - Full suite runs in <10s | ||
| - 50 consecutive CI runs show no flake | ||
| **Scope** | ||
| - tests + small refactors | ||
| **Out of scope** | ||
| - Benchmarking | ||
| **Test spec** | ||
| - Golden: deterministic virtual time assertions | ||
| - Known failures: none acceptable | ||
| - Fuzz/stress: stress suite that runs 5k randomized scenarios under TestClock | ||
| **Definition of done** | ||
| - "No sleeps" rule documented and enforced (lint/grep in CI). | ||
| --- | ||
| ### v1.0.3 — Operability proof: real example + control plane demo | ||
| **User story** | ||
| As a user, I want an end-to-end example proving live editing works. | ||
| **Requirements** | ||
| - Provide examples/control-plane/: | ||
| - starts a service with live policies | ||
| - changes bulkhead/retry via JSONL control | ||
| - prints telemetry events | ||
| **Acceptance criteria** | ||
| - One command changes live behavior demonstrably (throughput/latency/rejections). | ||
| **Scope** | ||
| - examples + docs | ||
| **Out of scope** | ||
| - hosted service, cloud infra | ||
| **Test spec** | ||
| - Golden: run example in CI and assert output contains "config version changed" and behavior changed | ||
| - Known failures: non-determinism (use scripted sequence) | ||
| - Fuzz/stress: N/A | ||
| **Definition of done** | ||
| - Example is runnable in <2 minutes by a new user. |
911
0.66%132158
-11.23%22
-4.35%