Skip to content

Requirements index

Comments and tests throughout this codebase cite requirement IDs (F4.5), acceptance criteria (AC6), and milestones (Phase 07 (M4)). Those identifiers come from a private planning document that is not part of this repository, which left every reference here unresolvable from the outside. This page closes that gap.

What this is: each identifier, with the requirement stated in plain English, and where the implementation and its proof live. It is reconstructed from the code and the tests rather than copied from the original document - the running system and its acceptance suite are the public source of truth, and where the two could ever disagree, the tests win.

Reading the citations: a comment citing F4.5 names the requirement below. One citing PRD section 12.2 names a section of that private document - the requirement it carried is restated here, so the section number is provenance rather than something you need to go and read. One citing Phase 07 (M4) names the milestone in the table below.

Acceptance criteria AC1-AC9 are measured, not described, so they live with their data in BENCHMARKS.md and benchmark/results/ac-scorecard.md.

Each milestone is a working slice, gated by its own Cucumber feature file in gateway-acceptance-tests/src/test/resources/features/.

Milestone Scope Acceptance feature
M0 OpenAI-shaped chat completions forwarded to a provider m0_basic_forwarding.feature
M1 SSE streaming with client-disconnect cancellation m1_streaming.feature
M2 Routing policy, provider chains, failover, hot reload m2_routing_and_resilience.feature
M3 API keys, quotas, budgets, rate limiting m3_quotas_and_rate_limiting.feature
M4 Two-tier cache: exact match plus semantic m4_semantic_cache.feature
M5 Metrics, request log, cost accounting m5_observability.feature
M6 Benchmarking harness and the AC scorecard (no feature file - see BENCHMARKS.md)
M7 Prompt registry, admin API, operator console m7_prompt_registry.feature
M8 Kubernetes deployment and zero-downtime rolling updates (no feature file - see Kubernetes deployment)
M9 Real provider adapters and security hardening m9_ssrf_protection.feature
ID Requirement Where
F1.1 POST /v1/chat/completions accepts and returns the OpenAI wire shape unchanged, so an existing SDK works by changing baseUrl alone ChatCompletionController, ChatCompletionDtoMapper
F1.2 Streaming requests relay provider chunks as SSE ChatCompletionController#streamingResponse
F1.3 A stream terminates with data: [DONE] same
F1.4 A client disconnecting mid-stream cancels the upstream provider call, so tokens stop being paid for Reactor cancellation through the Flux chain (ADR-002)
F1.5 A cancelled stream still records the usage it accrued before cancelling ChatCompletionController, chunk-count fallback
F1.6 GET /v1/models lists models from the loaded routing policy, without querying providers ModelsController
F1.8 POST /v1/embeddings - not built, deferred see API contract
ID Requirement Where
F2.1 Every provider is reached through one ProviderAdapter port gateway-core port, gateway-providers implementations
F2.2 Adapters normalise each provider’s own wire format to the domain model OllamaAdapter, GeminiAdapter, AnthropicAdapter, OpenAiCompatibleAdapter
F2.3 routing.yaml maps a model alias to an ordered provider chain RouteConfig, routing.yaml
F2.4 Predicate-based routing (API-key tag, request size) - not built; matching is by model alias only -
F2.5 A chain is walked in its configured attempt order Router
F2.7 POST /admin/routes/reload applies a changed routing.yaml with no restart RoutesController, m2_routing_and_resilience.feature
ID Requirement Where
F3.1 A circuit breaker is keyed per provider and model gateway-router, Resilience4j
F3.2 An unhealthy provider fails over to the next chain member m2_routing_and_resilience.feature, AC6
F3.3 Retries use exponential backoff with jitter gateway-router
F3.4 A bulkhead per provider caps concurrent in-flight calls gateway-router
F3.5 Terminal failures (client errors) return immediately - no retry, no failover gateway-router
F3.6 Retry-After from a provider is honoured gateway-router
F3.7 One request-level timeout budget spans every attempt, failovers included gateway-router
F3.8 Breaker state is exported as a metric for the dashboard timeline BreakerMetricsPoller
F3.9 Breaker state is local per replica, with a Redis advisory hint rather than shared consensus ADR-004
ID Requirement Where
F4.1 A cache key covers the canonicalised prompt, model, and request parameters cache_entry schema, gateway-cache
F4.2 A semantic hit requires cosine similarity above a configured threshold PgVectorCacheStore, m4_semantic_cache.feature
F4.3 Embeddings are generated in-process via ONNX, no network call on the request path gateway-cache, ADR-007
F4.4 Cache entries never cross namespaces - one API key can never read another’s m4_semantic_cache.feature
F4.5 An entity/numeric guard rejects near-misses that similarity alone would accept EntityNumericGuard, cache correctness
F4.6 Only a genuine miss with a successful completion is written to cache - never a bypass, never an error CacheAdapter
F4.7 Per-route cache config, plus manual invalidation by model prefix RouteCacheConfig, CacheAdminController
F4.8 A cache hit for a streaming request is replayed as SSE, not returned whole ChatCompletionController#cacheHitResponse
F4.9 A cache hit records the cost it avoided, not zero saved_usd in request_log
ID Requirement Where
F5.1 Per-key requests-per-second token bucket token_bucket.lua
F5.2 Per-key monthly token budget budget_reserve.lua
F5.3 Per-key concurrency cap concurrency_acquire.lua
F5.4 A soft warning threshold, distinct from hard rejection gateway-quota
F5.5 Under concurrency the limit is exact - no over-issue and no under-issue RedisQuotaAdapterConcurrencyIntegrationTest, AC8
F5.6 Output tokens are unknown up front, so budget is reserved pessimistically and reconciled against the real count afterwards budget_reconcile.lua, ADR-006
ID Requirement Where
F6.1 Micrometer metrics on requests, latency, tokens, cost, breaker state, in-flight streams MicrometerMetricsAdapter
F6.2 Every request is logged asynchronously, never blocking the request path JdbcRequestLogWriter
F6.3 Per-provider, per-model pricing from an external hot-reloadable cost-model.yaml CostModelRepository
F6.4 Cost and avoided cost are attributed per request request_log.cost_usd / saved_usd
F6.5 Distributed tracing via Micrometer’s OpenTelemetry bridge gateway-proxy build config
F6.6 Grafana dashboard and Prometheus datasource ship as committed provisioning config docker/grafana/provisioning/
ID Requirement Where
F7.1 Prompts are stored centrally, versioned, immutable once published gateway-registry, V4__prompt_registry.sql
F7.2 The resolved prompt version is returned as a response header X-Aether-Prompt-Version
F7.3 Prompts and versions are managed through the admin API PromptAdminController
F7.4 Repointing an alias takes effect on the very next request, no restart m7_prompt_registry.feature
F7.6 {{placeholder}} substitution with strict variable validation PromptTemplateRenderer
ID Requirement Where
F8.1 API key CRUD ApiKeysAdminController, V1__api_key.sql
F8.2 Read-only view of the loaded routing policy RoutesController
F8.3 Cache statistics per namespace CacheAdminController
F8.4 Usage aggregates grouped by key, provider, model, or route UsageAdminController
F8.5 Admin auth is fail-closed and separate from gateway API keys - no key configured means every admin request is refused AdminAuthFilter, m7_prompt_registry.feature
ID Requirement Where
F9.1 API keys are stored as salted hashes, never plaintext ApiKeyHasher
F9.2 routing.yaml names the environment variable holding a provider credential, never the credential - the file stays safe to commit routing.yaml
F9.3 Provider base URLs are validated against private and loopback address ranges (SSRF) ProviderBaseUrlValidator, m9_ssrf_protection.feature
F9.4 Malformed requests surface as 4xx with an RFC 9457 body, not 500 ChatCompletionController
F9.5 Anonymous requests, where enabled, are rate-limited per source IP IpRateLimitPort. Anonymous access is off by default - see Usage
ID Requirement Where
F13.1 X-Aether-Cache-Threshold overrides the similarity threshold for one request ChatCompletionController

Requirements referenced somewhere in the codebase that are deliberately unbuilt: F1.8 (embeddings endpoint) and F2.4 (predicate-based routing). AnthropicAdapter is built and passes the shared provider contract suite against recorded fixtures, but has never been run against the live Anthropic API - it is unproven, not proven. Measured criteria that miss their target - AC3, AC4, AC5 - are documented with their real numbers in the scorecard rather than quietly dropped.