|
| 1 | +# JavaScript Dependencies and Fetch Fixture Stability 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:** Update the root JavaScript tooling and eliminate intermittent Windows fetch-test socket resets without changing production behavior. |
| 6 | + |
| 7 | +**Architecture:** Keep the dependency refresh isolated to `package.json` and `pnpm-lock.yaml`. Correct the test-only HTTP fixture by reading a complete header block before responding, with a deterministic test that proves the helper does not return after only a request line. |
| 8 | + |
| 9 | +**Tech Stack:** Rust 2024, standard-library TCP sockets and channels, Cargo tests, pnpm, fallow, oxfmt, oxlint, GitHub Actions, CodSpeed. |
| 10 | + |
| 11 | +## Global Constraints |
| 12 | + |
| 13 | +- Keep production fetch behavior unchanged. |
| 14 | +- Add no HTTP mock-server dependency. |
| 15 | +- Do not increase timeouts to hide the socket reset. |
| 16 | +- Preserve `memchr` 2.8.2 because 2.8.3 failed the performance gate. |
| 17 | +- Use signed conventional commits. |
| 18 | + |
| 19 | +## File Structure |
| 20 | + |
| 21 | +- Modify `crates/cli/tests/cli.rs`: deterministic fixture regression test and complete-header request reader. |
| 22 | +- Modify `package.json`: exact root tooling versions. |
| 23 | +- Modify `pnpm-lock.yaml`: regenerated dependency graph. |
| 24 | +- Create `docs/superpowers/plans/2026-07-14-js-dependencies-fetch-fixture.md`: execution record. |
| 25 | + |
| 26 | +--- |
| 27 | + |
| 28 | +### Task 1: Consume Complete HTTP Fixture Requests |
| 29 | + |
| 30 | +**Files:** |
| 31 | +- Modify and test: `crates/cli/tests/cli.rs:950-1015` |
| 32 | + |
| 33 | +**Interfaces:** |
| 34 | +- Consumes: `TcpListener`, `TcpStream`, `mpsc`, `Duration`, and the existing `read_request(&mut TcpStream) -> String` helper. |
| 35 | +- Produces: the same `read_request(&mut TcpStream) -> String` interface, now returning only after `\r\n\r\n` has been consumed. |
| 36 | + |
| 37 | +- [ ] **Step 1: Add the deterministic failing test after `read_request`** |
| 38 | + |
| 39 | +```rust |
| 40 | +#[test] |
| 41 | +fn read_request_waits_for_complete_headers() { |
| 42 | + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); |
| 43 | + let address = listener.local_addr().unwrap(); |
| 44 | + let (accepted_sender, accepted_receiver) = mpsc::channel(); |
| 45 | + let (path_sender, path_receiver) = mpsc::channel(); |
| 46 | + let server = thread::spawn(move || { |
| 47 | + let (mut stream, _) = listener.accept().unwrap(); |
| 48 | + accepted_sender.send(()).unwrap(); |
| 49 | + path_sender.send(read_request(&mut stream)).unwrap(); |
| 50 | + }); |
| 51 | + |
| 52 | + let mut client = TcpStream::connect(address).unwrap(); |
| 53 | + accepted_receiver.recv_timeout(Duration::from_secs(2)).unwrap(); |
| 54 | + client.write_all(b"GET /complete HTTP/1.1\r\n").unwrap(); |
| 55 | + client.flush().unwrap(); |
| 56 | + |
| 57 | + assert!( |
| 58 | + path_receiver.recv_timeout(Duration::from_millis(100)).is_err(), |
| 59 | + "request completed before the header terminator" |
| 60 | + ); |
| 61 | + |
| 62 | + client.write_all(b"Host: localhost\r\nConnection: close\r\n\r\n").unwrap(); |
| 63 | + assert_eq!(path_receiver.recv_timeout(Duration::from_secs(2)).unwrap(), "/complete"); |
| 64 | + server.join().unwrap(); |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +- [ ] **Step 2: Run the focused test and verify red** |
| 69 | + |
| 70 | +Run: |
| 71 | + |
| 72 | +```bash |
| 73 | +cargo test -p srcmap-cli --test cli read_request_waits_for_complete_headers -- --exact |
| 74 | +``` |
| 75 | + |
| 76 | +Expected: FAIL with `request completed before the header terminator`. |
| 77 | + |
| 78 | +- [ ] **Step 3: Replace request-line-only reading with bounded complete-header reading** |
| 79 | + |
| 80 | +```rust |
| 81 | +const MAX_REQUEST_HEADERS_SIZE: usize = 32 * 1024; |
| 82 | + |
| 83 | +fn read_request(stream: &mut TcpStream) -> String { |
| 84 | + stream.set_read_timeout(Some(Duration::from_secs(2))).unwrap(); |
| 85 | + let mut request = Vec::with_capacity(512); |
| 86 | + while !request.ends_with(b"\r\n\r\n") { |
| 87 | + let mut byte = [0_u8; 1]; |
| 88 | + stream.read_exact(&mut byte).unwrap(); |
| 89 | + request.push(byte[0]); |
| 90 | + assert!( |
| 91 | + request.len() <= MAX_REQUEST_HEADERS_SIZE, |
| 92 | + "HTTP request headers are unexpectedly large" |
| 93 | + ); |
| 94 | + } |
| 95 | + |
| 96 | + let request_line = request.split(|byte| *byte == b'\n').next().unwrap_or_default(); |
| 97 | + let request_line = String::from_utf8_lossy(request_line); |
| 98 | + request_line.split_whitespace().nth(1).unwrap_or_default().to_string() |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +- [ ] **Step 4: Run the focused test and fetch integration tests** |
| 103 | + |
| 104 | +Run: |
| 105 | + |
| 106 | +```bash |
| 107 | +cargo test -p srcmap-cli --test cli read_request_waits_for_complete_headers -- --exact |
| 108 | +cargo test -p srcmap-cli --test cli fetch_ |
| 109 | +``` |
| 110 | + |
| 111 | +Expected: PASS. |
| 112 | + |
| 113 | +- [ ] **Step 5: Run a local stress loop** |
| 114 | + |
| 115 | +Run: |
| 116 | + |
| 117 | +```bash |
| 118 | +for run in {1..25}; do cargo test -q -p srcmap-cli --test cli fetch_ || exit 1; done |
| 119 | +``` |
| 120 | + |
| 121 | +Expected: every iteration exits successfully. |
| 122 | + |
| 123 | +- [ ] **Step 6: Commit the fixture fix** |
| 124 | + |
| 125 | +```bash |
| 126 | +git add crates/cli/tests/cli.rs |
| 127 | +git commit -S -m "test: consume complete fixture requests" |
| 128 | +``` |
| 129 | + |
| 130 | +### Task 2: Update Root JavaScript Tooling |
| 131 | + |
| 132 | +**Files:** |
| 133 | +- Modify: `package.json:40-44` |
| 134 | +- Modify: `pnpm-lock.yaml` |
| 135 | + |
| 136 | +**Interfaces:** |
| 137 | +- Consumes: existing root scripts for fallow, oxfmt, and oxlint. |
| 138 | +- Produces: exact versions `fallow` 3.5.0, `oxfmt` 0.59.0, and `oxlint` 1.74.0 with a frozen-install-compatible lockfile. |
| 139 | + |
| 140 | +- [ ] **Step 1: Update exact manifest versions** |
| 141 | + |
| 142 | +```json |
| 143 | +"devDependencies": { |
| 144 | + "fallow": "3.5.0", |
| 145 | + "oxfmt": "0.59.0", |
| 146 | + "oxlint": "1.74.0" |
| 147 | +} |
| 148 | +``` |
| 149 | + |
| 150 | +- [ ] **Step 2: Regenerate the lockfile without running package scripts** |
| 151 | + |
| 152 | +Run: |
| 153 | + |
| 154 | +```bash |
| 155 | +pnpm install --lockfile-only --ignore-scripts |
| 156 | +``` |
| 157 | + |
| 158 | +Expected: `package.json` and `pnpm-lock.yaml` resolve the three requested versions. |
| 159 | + |
| 160 | +- [ ] **Step 3: Verify the updated tools** |
| 161 | + |
| 162 | +Run: |
| 163 | + |
| 164 | +```bash |
| 165 | +pnpm run fmt:js:check |
| 166 | +pnpm run lint:js |
| 167 | +pnpm outdated --format list |
| 168 | +``` |
| 169 | + |
| 170 | +Expected: formatting and linting pass, with no newer direct JavaScript dependency reported. |
| 171 | + |
| 172 | +- [ ] **Step 4: Review dependency scope and preserve the Rust lock selection** |
| 173 | + |
| 174 | +Run: |
| 175 | + |
| 176 | +```bash |
| 177 | +git diff -- package.json pnpm-lock.yaml |
| 178 | +rg -n 'name = "memchr"|version = "2\.8\.2"' Cargo.lock |
| 179 | +``` |
| 180 | + |
| 181 | +Expected: only the requested JavaScript tooling graph changes and `memchr` remains at 2.8.2. |
| 182 | + |
| 183 | +- [ ] **Step 5: Commit the dependency update** |
| 184 | + |
| 185 | +```bash |
| 186 | +git add package.json pnpm-lock.yaml |
| 187 | +git commit -S -m "chore: update JavaScript tooling" |
| 188 | +``` |
| 189 | + |
| 190 | +### Task 3: Full Verification and Publication |
| 191 | + |
| 192 | +**Files:** |
| 193 | +- Verify all files changed since `origin/main`. |
| 194 | + |
| 195 | +**Interfaces:** |
| 196 | +- Consumes: completed Tasks 1 and 2. |
| 197 | +- Produces: a clean pull request with passing local and remote validation, merged to `main`. |
| 198 | + |
| 199 | +- [ ] **Step 1: Run full local verification with bounded logs** |
| 200 | + |
| 201 | +```bash |
| 202 | +pnpm check > /tmp/srcmap-js-fixture-check.log 2>&1 |
| 203 | +pnpm test > /tmp/srcmap-js-fixture-test.log 2>&1 |
| 204 | +``` |
| 205 | + |
| 206 | +Expected: both commands exit successfully. |
| 207 | + |
| 208 | +- [ ] **Step 2: Review the complete diff and repository state** |
| 209 | + |
| 210 | +```bash |
| 211 | +git diff --check origin/main...HEAD |
| 212 | +git diff --stat origin/main...HEAD |
| 213 | +git status --short --branch |
| 214 | +git log --show-signature --format=fuller origin/main..HEAD |
| 215 | +``` |
| 216 | + |
| 217 | +Expected: only the design, plan, fixture test helper, dependency manifest, and lockfile are changed; commits have valid signatures. |
| 218 | + |
| 219 | +- [ ] **Step 3: Push and create a ready pull request** |
| 220 | + |
| 221 | +```bash |
| 222 | +git push -u origin codex/update-js-stabilize-fetch-tests |
| 223 | +gh pr create --base main --head codex/update-js-stabilize-fetch-tests --title "test: stabilize fetch fixtures and update JS tooling" --body-file /tmp/srcmap-js-fixture-pr.md |
| 224 | +``` |
| 225 | + |
| 226 | +Expected: a ready pull request is created. |
| 227 | + |
| 228 | +- [ ] **Step 4: Require all PR checks** |
| 229 | + |
| 230 | +Run `gh pr checks --watch` and inspect any failure before retrying or fixing it. |
| 231 | + |
| 232 | +Expected: Windows CI, security, coverage, and CodSpeed pass. |
| 233 | + |
| 234 | +- [ ] **Step 5: Squash merge and verify the exact merge commit** |
| 235 | + |
| 236 | +```bash |
| 237 | +gh pr merge --squash --delete-branch --subject "test: stabilize fetch fixtures and update JS tooling" |
| 238 | +``` |
| 239 | + |
| 240 | +Expected: the pull request is merged, local `main` matches `origin/main`, and every workflow for the merge commit completes successfully. |
0 commit comments