feat(net): stack() assembly + HttpConfig (Slice 2)#88
Conversation
Adds the runtime-free assembly design (stack() + HttpConfig) derived from the construction-surface spec and ADR-0031, and commits the previously-untracked Slice 0 PR4 (rate-coverage) implementation plan that shipped as #72. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the task-by-task stack()/HttpConfig plan and folds in the design refinements surfaced while planning against the shipped layer APIs: HttpConfig gains rate_limit_max_wait (RateLimitLayer::new's 3rd arg), validation is delegated to RateLimitLayer::new (no separate call), and tests use an inline recording leaf (not MockClient, a dev-dep cycle). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the non-generic HttpConfig aggregate and the stack() function that validates pacing coverage (RateLimitLayer::new) before composing the ADR-0031 canonical resilience order over any leaf: Tracing(CircuitBreaker(Retry(RateLimit(Timeout(SetHeaders(Auth(leaf))))))). Includes a transparency smoke test (request flows through all layers, body untouched) and a boot-coverage test proving a non-total RateLimitConfig fails closed as a BuildError before anything is built. Deviations from the brief, needed to compile/lint clean: - Added `S::Body: Send` to stack()'s where clause: RateLimit's Service impl requires the body to be Send, which the bare HttpClient bound does not imply. - Test module imports `oath_adapter_net_api::Timer` (needed for ScriptLeaf's Step::Hang arm to call `timer.sleep`). - `#[allow(clippy::needless_pass_by_value)]` on stack(): rate_limits is only borrowed internally, but the public signature intentionally takes ownership to mirror HttpConfig's "consumed once at boot" shape. - Second test uses `let-else` instead of `.unwrap_err()`, which would require Debug on the opaque `impl HttpClient + ...` Ok type. - `#[allow(dead_code)]` on Step::Err/Hang, ScriptLeaf::calls/seen_auth, and CounterAuth: scaffolding consumed by Task 2's full-stack tests landing later in this same file. Closes #87
Final whole-branch review flagged the amendment enumerated the bound refinements but omitted S::Body: Send (transitively required by Retry/RateLimit's B: Send). Records it so the ADR matches the shipped stack() signature. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughAdds Changesstack() implementation and wiring
Planning and design documentation
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related issues
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/superpowers/specs/2026-07-05-net-http-stack-assembly-design.md`:
- Around line 120-131: The public stack() contract is missing a required body
bound for the composed client to remain Send. Update the stack() signature and
its where-clause to include S::Body: Send alongside the existing S, T, A, and K
constraints, so the HttpClient composition requirements are fully reflected in
the API spec.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 6dad1b22-17cb-42d8-920c-2559600874d9
📒 Files selected for processing (7)
CHANGELOG.mdcrates/adapter/net/http/api/src/lib.rscrates/adapter/net/http/api/src/stack.rsdocs/adr/0034-http-construction-surface-auth-guarded-boot-coverage.mddocs/superpowers/plans/2026-07-04-net-http-rate-coverage.mddocs/superpowers/plans/2026-07-05-net-http-stack-assembly.mddocs/superpowers/specs/2026-07-05-net-http-stack-assembly-design.md
Closes #87
Slice 2 (assembly, runtime-free) of the net-http construction surface (spec: docs/superpowers/specs/2026-07-05-net-http-stack-assembly-design.md; ADR-0031 §1, ADR-0034).
HttpConfig— non-generic aggregate:timeout,retry,circuit_breaker,headers,rate_limit_max_wait.stack<S, T, A, K>()— builds the fallibleRateLimitlayer first (validates coverage + concurrency-singleton), then composesTracing(CircuitBreaker(Retry(RateLimit(Timeout(SetHeaders(Auth(leaf)))))))over any leaf. ReturnsResult<impl HttpClient + Clone + Send + Sync + 'static, BuildError>, so a layer regression is a compile error atstack().MockTimer(noMockClient— dev-dep cycle): CircuitBreaker-outside-Retry, RateLimit-inside-Retry, send-Timeout, per-attempt Auth re-stamp, Scope fail-closed, plus a transparency smoke and a boot-coverageBuildError.No new dependency; no runtime; no existing-layer change.
BufferOrStreamis leaf-side (not a layer). Next: the hyper-backend slice —build()+hyper_leaf()+TokioTimer, delegating to thisstack().🤖 Generated with Claude Code
Summary by CodeRabbit
stack()configuration with an exposedHttpConfigto control timeout, retry, circuit breaker, headers, and rate limiting.