Skip to content

Commit 4bae835

Browse files
committed
plans
1 parent 07cefb6 commit 4bae835

21 files changed

Lines changed: 11903 additions & 0 deletions

docs/superpowers/plans/2026-05-13-h2h3-redesign-phase1-multiplexed-core.md

Lines changed: 745 additions & 0 deletions
Large diffs are not rendered by default.

docs/superpowers/plans/2026-05-13-h2h3-redesign-phase2-multiplexed-encoding-reconnect.md

Lines changed: 484 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
# Phase 3: MultiplexedOptions Implementation Plan
2+
3+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4+
5+
**Goal:** Create `MultiplexedOptions` — the shared configuration record for multiplexed protocols (HTTP/2 and HTTP/3).
6+
7+
**Architecture:** A C# `record` with a static `Default` member and a `Validate()` method. Contains `SharedHttpOptions` (from H/1.0 Redesign) plus multiplexed-specific settings. Construction via `with`-expression.
8+
9+
**Tech Stack:** .NET, xUnit v3
10+
11+
**Prerequisite:** Phase 2 complete. `SharedHttpOptions` must exist (from H/1.0 Redesign Phase 3).
12+
13+
---
14+
15+
### Task 1: MultiplexedOptions Record
16+
17+
**Files:**
18+
- Create: `src/TurboHTTP/Protocol/Multiplexed/Options/MultiplexedOptions.cs`
19+
- Test: `src/TurboHTTP.Tests/Multiplexed/MultiplexedOptionsSpec.cs`
20+
21+
- [ ] **Step 1: Write the failing tests**
22+
23+
```csharp
24+
// src/TurboHTTP.Tests/Multiplexed/MultiplexedOptionsSpec.cs
25+
using TurboHTTP.Protocol.Multiplexed.Options;
26+
27+
namespace TurboHTTP.Tests.Multiplexed;
28+
29+
public sealed class MultiplexedOptionsSpec
30+
{
31+
[Fact(Timeout = 5000)]
32+
public void Default_should_have_100_max_concurrent_streams()
33+
{
34+
var options = MultiplexedOptions.Default;
35+
Assert.Equal(100, options.MaxConcurrentStreams);
36+
}
37+
38+
[Fact(Timeout = 5000)]
39+
public void Default_should_have_16k_max_header_size()
40+
{
41+
var options = MultiplexedOptions.Default;
42+
Assert.Equal(16 * 1024, options.MaxHeaderSize);
43+
}
44+
45+
[Fact(Timeout = 5000)]
46+
public void Default_should_have_64k_max_total_header_size()
47+
{
48+
var options = MultiplexedOptions.Default;
49+
Assert.Equal(64 * 1024, options.MaxTotalHeaderSize);
50+
}
51+
52+
[Fact(Timeout = 5000)]
53+
public void Default_should_have_1s_reconnect_backoff()
54+
{
55+
var options = MultiplexedOptions.Default;
56+
Assert.Equal(TimeSpan.FromSeconds(1), options.ReconnectBackoff);
57+
}
58+
59+
[Fact(Timeout = 5000)]
60+
public void Default_should_have_3_max_reconnect_attempts()
61+
{
62+
var options = MultiplexedOptions.Default;
63+
Assert.Equal(3, options.MaxReconnectAttempts);
64+
}
65+
66+
[Fact(Timeout = 5000)]
67+
public void Validate_should_throw_when_MaxConcurrentStreams_is_zero()
68+
{
69+
var options = MultiplexedOptions.Default with { MaxConcurrentStreams = 0 };
70+
var ex = Assert.Throws<ArgumentException>(() => options.Validate());
71+
Assert.Contains("MaxConcurrentStreams", ex.Message);
72+
}
73+
74+
[Fact(Timeout = 5000)]
75+
public void Validate_should_throw_when_MaxConcurrentStreams_is_negative()
76+
{
77+
var options = MultiplexedOptions.Default with { MaxConcurrentStreams = -1 };
78+
var ex = Assert.Throws<ArgumentException>(() => options.Validate());
79+
Assert.Contains("MaxConcurrentStreams", ex.Message);
80+
}
81+
82+
[Fact(Timeout = 5000)]
83+
public void Validate_should_throw_when_MaxHeaderSize_is_zero()
84+
{
85+
var options = MultiplexedOptions.Default with { MaxHeaderSize = 0 };
86+
var ex = Assert.Throws<ArgumentException>(() => options.Validate());
87+
Assert.Contains("MaxHeaderSize", ex.Message);
88+
}
89+
90+
[Fact(Timeout = 5000)]
91+
public void Validate_should_throw_when_MaxTotalHeaderSize_is_less_than_MaxHeaderSize()
92+
{
93+
var options = MultiplexedOptions.Default with
94+
{
95+
MaxHeaderSize = 1024,
96+
MaxTotalHeaderSize = 512
97+
};
98+
var ex = Assert.Throws<ArgumentException>(() => options.Validate());
99+
Assert.Contains("MaxTotalHeaderSize", ex.Message);
100+
}
101+
102+
[Fact(Timeout = 5000)]
103+
public void Validate_should_throw_when_ReconnectBackoff_is_negative()
104+
{
105+
var options = MultiplexedOptions.Default with { ReconnectBackoff = TimeSpan.FromSeconds(-1) };
106+
var ex = Assert.Throws<ArgumentException>(() => options.Validate());
107+
Assert.Contains("ReconnectBackoff", ex.Message);
108+
}
109+
110+
[Fact(Timeout = 5000)]
111+
public void Validate_should_throw_when_MaxReconnectAttempts_is_negative()
112+
{
113+
var options = MultiplexedOptions.Default with { MaxReconnectAttempts = -1 };
114+
var ex = Assert.Throws<ArgumentException>(() => options.Validate());
115+
Assert.Contains("MaxReconnectAttempts", ex.Message);
116+
}
117+
118+
[Fact(Timeout = 5000)]
119+
public void Validate_should_accept_zero_MaxReconnectAttempts()
120+
{
121+
var options = MultiplexedOptions.Default with { MaxReconnectAttempts = 0 };
122+
options.Validate();
123+
}
124+
125+
[Fact(Timeout = 5000)]
126+
public void Validate_should_accept_zero_ReconnectBackoff()
127+
{
128+
var options = MultiplexedOptions.Default with { ReconnectBackoff = TimeSpan.Zero };
129+
options.Validate();
130+
}
131+
132+
[Fact(Timeout = 5000)]
133+
public void Validate_should_pass_for_default()
134+
{
135+
MultiplexedOptions.Default.Validate();
136+
}
137+
138+
[Fact(Timeout = 5000)]
139+
public void With_expression_should_create_modified_copy()
140+
{
141+
var options = MultiplexedOptions.Default with { MaxConcurrentStreams = 50 };
142+
Assert.Equal(50, options.MaxConcurrentStreams);
143+
Assert.Equal(100, MultiplexedOptions.Default.MaxConcurrentStreams);
144+
}
145+
}
146+
```
147+
148+
- [ ] **Step 2: Run tests to verify they fail**
149+
150+
Run: `dotnet run --project src/TurboHTTP.Tests/TurboHTTP.Tests.csproj -- -class "TurboHTTP.Tests.Multiplexed.MultiplexedOptionsSpec"`
151+
Expected: Compilation error — `MultiplexedOptions` does not exist.
152+
153+
- [ ] **Step 3: Implement MultiplexedOptions**
154+
155+
```csharp
156+
// src/TurboHTTP/Protocol/Multiplexed/Options/MultiplexedOptions.cs
157+
namespace TurboHTTP.Protocol.Multiplexed.Options;
158+
159+
public sealed record MultiplexedOptions
160+
{
161+
public int MaxConcurrentStreams { get; init; } = 100;
162+
public int MaxHeaderSize { get; init; } = 16 * 1024;
163+
public int MaxTotalHeaderSize { get; init; } = 64 * 1024;
164+
public TimeSpan ReconnectBackoff { get; init; } = TimeSpan.FromSeconds(1);
165+
public int MaxReconnectAttempts { get; init; } = 3;
166+
167+
public static MultiplexedOptions Default { get; } = new();
168+
169+
public void Validate()
170+
{
171+
if (MaxConcurrentStreams <= 0)
172+
{
173+
throw new ArgumentException("MaxConcurrentStreams must be greater than zero.", nameof(MaxConcurrentStreams));
174+
}
175+
176+
if (MaxHeaderSize <= 0)
177+
{
178+
throw new ArgumentException("MaxHeaderSize must be greater than zero.", nameof(MaxHeaderSize));
179+
}
180+
181+
if (MaxTotalHeaderSize < MaxHeaderSize)
182+
{
183+
throw new ArgumentException("MaxTotalHeaderSize must be greater than or equal to MaxHeaderSize.", nameof(MaxTotalHeaderSize));
184+
}
185+
186+
if (ReconnectBackoff < TimeSpan.Zero)
187+
{
188+
throw new ArgumentException("ReconnectBackoff must not be negative.", nameof(ReconnectBackoff));
189+
}
190+
191+
if (MaxReconnectAttempts < 0)
192+
{
193+
throw new ArgumentException("MaxReconnectAttempts must not be negative.", nameof(MaxReconnectAttempts));
194+
}
195+
}
196+
}
197+
```
198+
199+
- [ ] **Step 4: Run tests to verify they pass**
200+
201+
Run: `dotnet run --project src/TurboHTTP.Tests/TurboHTTP.Tests.csproj -- -class "TurboHTTP.Tests.Multiplexed.MultiplexedOptionsSpec"`
202+
Expected: All 15 tests PASS.
203+
204+
- [ ] **Step 5: Commit**
205+
206+
```bash
207+
git add src/TurboHTTP/Protocol/Multiplexed/Options/MultiplexedOptions.cs src/TurboHTTP.Tests/Multiplexed/MultiplexedOptionsSpec.cs
208+
git commit -m "feat(multiplexed): add MultiplexedOptions record with validation"
209+
```
210+
211+
---
212+
213+
### Task 2: Run full build and verify phase gate
214+
215+
- [ ] **Step 1: Build the solution**
216+
217+
Run: `dotnet build --configuration Release src/TurboHTTP.slnx`
218+
Expected: Build succeeds with zero errors.
219+
220+
- [ ] **Step 2: Run all Multiplexed tests**
221+
222+
Run: `dotnet run --project src/TurboHTTP.Tests/TurboHTTP.Tests.csproj -- -namespace "TurboHTTP.Tests.Multiplexed"`
223+
Expected: All specs PASS — StreamState, StreamTracker, CorrelationMap, ReconnectClassifier, ReconnectBuffer, MultiplexedOptions.
224+
225+
- [ ] **Step 3: Run full test suite**
226+
227+
Run: `dotnet test src/TurboHTTP.Tests/TurboHTTP.Tests.csproj`
228+
Expected: All tests PASS (existing + new).
229+
230+
- [ ] **Step 4: Commit phase completion**
231+
232+
```bash
233+
git commit --allow-empty -m "milestone: Phase 3 complete — Multiplexed layer fully implemented (Core, Encoding, Correlation, Reconnect, Options)"
234+
```
235+
236+
This concludes **Spec 1: Protocol/Multiplexed/**. The shared layer is ready for consumption by HTTP/2 (Phase 4–6) and HTTP/3 (Phase 7–9).

0 commit comments

Comments
 (0)