Skip to content

Commit ed3b144

Browse files
committed
Merge TASK-190: Correct HTTP/2 conformance scheduling oracle
2 parents 92f0918 + 7f72f00 commit ed3b144

6 files changed

Lines changed: 305 additions & 21 deletions

File tree

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# TASK-190 implementation evidence
2+
3+
Worktree `/home/etr/progs/libhttpserver/.worktrees/TASK-190`, branch `task/TASK-190`, base `v3` at `92f09185e28a8e19edeb7ed2435c21d8d318cc55`. Manual Groundwork implementation; caller owns validation, finalization, merge and cleanup. Task card/index remain In Progress. No configured executor memory store was found; reusable setup observations are retained in the invocation journal.
4+
5+
## Cause and repair boundary
6+
7+
Journal `.debug/h2-route-effect-ordering-s9j48boo/journal.md`; evidence root `build/task190`. The immutable four TASK-185 normal/ASan TLS-off/on logs and pinned H2 receipt were read and hashed in `retained-identities.json`. They contain five route-effect failures plus the aggregate 47!=52 assertion: six checks, one oracle cause. The fresh unchanged task-local baseline exactly reproduces those six checks (`baseline-red.log`, four tests, 944 checks, zero skips). Bootstrap/configure/library/baseline command logs are retained.
8+
9+
PRD-V3N-REQ-023 makes decisions possible at complete request headers; it does not require a lazy handler to execute before the parser's next frame. REQ-025 requires cancellation. Architecture §§3.3–3.4, DR-V3-001/003/006, `docs/task-140-http2-evidence.md` and `docs/task-142-http2-evidence.md` retain connection-owned HPACK/flow control and serialized, executor-affine guarded route resumes. The existing `http2_reset/peer_reset_invalidates_queued_and_parked_handlers_and_returns_reservations` explicitly requires zero calls if reset precedes executor start and one call if it already started.
10+
11+
`http2_request_state.hpp::admit` posts the route task. `reset/discard` disconnects it; subsequent owner reaping invalidates its queued frame witness. The original five `feed` entries parse both their valid opening and later error before running any pending handler, then incorrectly expect route 1. Running stale work or invoking handlers inline would violate the documented lifecycle. No production defect is demonstrated.
12+
13+
The minimal repair gives those same five entries the explicit `dispatch` stage. Executor turns occur only at completed protocol frames. The opening handler records its actual head and suspends on an application signal, leaving the request active until its later stream error cancels it. Output retirement remains deferred until the transcript is parsed. Every original route payload, RFC verdict and wire file is unchanged; the corpus still contains exactly 52 entries. Unknown stage values now fail explicitly.
14+
15+
A completed response is a distinct state: `prepare_response` marks the send side ended at the semantic response, and the next owner reap removes an END_STREAM request even before output retirement. The initial dispatch-only probe documents that `closed-data` and `half-closed-head` then correctly produce connection STREAM_CLOSED (`schedule-green-initial.log`). The final dispatch stage suspends those handlers rather than pretending a completed stream is still half-closed. Separate completed-response regressions retain and verify that connection verdict for both inputs across every fragmentation split. No wire-error oracle is weakened or made nondeterministic.
16+
17+
## Causal RED and discriminating GREEN
18+
19+
Before correcting the harness/metadata, new paired regressions passed all eight deferred controls but failed all eight started controls at `route effects`, yielding 14 failures together with the six original checks (`schedule-red.log`). This is the expected missing executor boundary, not a timeout or build error. Final GREEN logs retain the source identity and exact protocol observations.
20+
21+
A bounded task-local discriminator also compiles a copy of the final harness
22+
with only dispatch-stage executor turns removed. It reintroduces all five
23+
original route failures and all eight started-control failures, plus the two
24+
aggregate completion-count failures (exit 15). `discriminators/receipt.json`
25+
records the exact compile command and source SHA256
26+
`9875ffd708a273c06f97c7de401e3b1553e594979aecdcf4ce18fd966a58e2fc`;
27+
`discriminators/red.log` retains the assertions. The real harness and production
28+
tree are untouched by this probe. Exact baseline source/metadata copies are
29+
retained alongside it. This independently confirms the causal scheduling seam
30+
after adding final diagnostic/count assertions.
31+
32+
For each named later error, paired schedules append an independently authored dynamic-table insertion on reset stream 1, then a sibling on stream 3 references index 62. They require its actual `x-route: after` field, `GET /sibling`, exact route order and reset-before-sibling-response ordering. All nonterminal suffix bytes are consumed. A skipped rejected block would instead lose dynamic state and fail the sibling decode/head assertion. Invalid-opening controls remain in the complete original corpus and still never route.
33+
34+
| Transcript | Required stream 1 error | Deferred route order | Started route order | Full suffix bytes |
35+
|---|---|---|---|---:|
36+
| content-length-data | PROTOCOL_ERROR (1) | 3 | 1, 3 | 144 |
37+
| malformed-trailer | PROTOCOL_ERROR (1) | 3 | 1, 3 | 126 |
38+
| closed-data | STREAM_CLOSED (5) | 3 | 1, 3 | 126 |
39+
| stream-window-overflow | FLOW_CONTROL_ERROR (3) | 3 | 1, 3 | 129 |
40+
| half-closed-head | STREAM_CLOSED (5) | 3 | 1, 3 | 153 |
41+
42+
Existing scalar diagnostics and test-local lifetime guards provide actual event order, without production instrumentation. Deferred: `admitted:1,cancelled:1,admitted:3,started:3`; admitted-but-cancelled is distinguished from never admitted. Started application wait: `admitted:1,started:1,cancelled:1,token-completed:1,destroyed:1,admitted:3,started:3`. Each started handler has exactly one destruction and one typed token completion (`cancelled`); its exchange disconnect reason is `connection_closed`. A late application signal executes no cancelled code. Three incomplete-body controls separately allow exactly one failed body-read completion (`connection_closed`), then one destruction and token cancellation. This is failure delivery before the next owner reap, not stale signal reentry; the body does not respond after failure.
43+
44+
Every variant requires exact RST_STREAM scope/code/count, no GOAWAY, complete suffix consumption and zero usage of all 13 resource kinds after engine/executor/route teardown. Connection-error controls require one GOAWAY, final output ordering, sticky failure and no duplicate output. Compression-error precedence remains in the unchanged `peer-reset-invalid-hpack`/`closed-invalid-hpack` entries. No production source, limits, client predicates, deadlines, exclusions or suppression inventory change.
45+
46+
The complete corpus executes **52 entries / 4352 variants / 4352 route checks** in each lane. The paired regressions execute **5 transcripts / 16 schedules / 2202 variants / 2202 route checks**; every schedule includes coalesced input, bytewise input, every split and three-byte output retirement. The two fully completed response controls add 193 replay variants. Within each fixed schedule, output bytes and consumed bytes must match exactly. Per-case and aggregate counts and actual event traces are printed in the test log; the littletest count does not include every throwing corpus predicate.
47+
48+
## Required local lanes
49+
50+
Fresh C++20 normal and ASan/nonrecovering UBSan lanes, TLS-off/on, use `/usr/bin/clang-18` and `/usr/bin/clang++-18` (18.1.3), `--disable-examples`, `-std=c++20 -pthread -O1 -g -fno-omit-frame-pointer`. TLS-on uses the existing `/home/etr/progs/libhttpserver/build/tooling/provider` include/lib and `-lssl -lcrypto`; it is not rebuilt. Sanitizers add `-fsanitize=address,undefined -fno-sanitize-recover=undefined -fsanitize-address-use-after-scope`; all runs retain `ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1:halt_on_error=1` and `UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1`, outside ptrace. No diagnostics are disabled. No production concurrency changes, so a new TSan lane is unnecessary; TASK-200 remains separate.
51+
52+
`build/task190/run_lanes.py` records configure/library/scoped-test/gate commands, environments, exits and durations in lane `receipt.json` files. The library builds first; TESTS/check_PROGRAMS/noinst_PROGRAMS are scoped together. All 18 existing H2 engine programs, HPACK corpus/connection, affected semantic owners, relevant public header consumers and v3_header_hygiene run in all four lanes (35 programs off, 36 on). TLS-on additionally builds the original `http2_tls_fixture` and runs `http2_tls_boundary`.
53+
54+
Normal and sanitizer TLS-on lanes run unchanged `scripts/run-v3-http2-gates.py` with `/tmp/task185-clients/bin/python`, Node `/tmp/task185-tools/node-v24.15.0-linux-x64/bin/node`, and `/tmp/task185-tools/h2spec`. Actual tool probes verify hyper-h2 4.4.1/hpack 4.2.0/hyperframe 6.1.0, Node 24.15.0 and h2spec 2.6.0 commit `70ac2294010887f48b18e2d64f5cccd48421fad1`. Each complete gate must retain both clients' multiplexing/reset/upload_flow/response_flow/websocket predicates and every nonexcluded h2spec inventory entry. The exact two RFC 9113 exclusions remain byte-identical (SHA256 `9256a4d025cb35a6056f142599ce34a29df8e70c4c857a9ab2cd472e11eeb4c3`). Gate-runner unit contracts, C++ lint, littletest result/skip contract and diff checks also run.
55+
56+
These gates exercise the existing **private negotiated-h2 engine TLS fixture**. They do not establish new public-listener H2 capability. No affected public runtime path changes, so no new installed public transport witness is claimed. TLS-off runs the complete engine/semantic selection without TLS negotiation. BSD/macOS/Windows and nonlocal backends remain unexecuted and CI/v3-PR owned under AGENTS.md.
57+
58+
`final-identities.json` hashes final test/corpus metadata, provider/tool identities and exclusions, and verifies all tracked production cpp/hpp and all 52 wire files against unchanged `v3`. The caller must archive `build/task190` and the unique `.debug` journal before cleanup. TASK-185's incomplete status and immutable artifacts, TASK-200 ownership, unrelated FEATURE worktrees and the clean primary checkout are preserved. No validation-loop, merge, cleanup, push, tag or publication is performed by this executor.
59+
60+
## Final local receipts
61+
62+
All four focused lanes pass with zero skips and no ASan/LeakSanitizer/UBSan diagnostics. Both unchanged pinned TLS-on gates pass **two independent clients, all five scenarios each, 145 of 147 h2spec inventory entries, and exactly two unchanged RFC 9113 exclusions**. Each independent client retains upload stall 65535 bytes, upload 163840 bytes, response stall 32 bytes, response 131072 bytes, cancellation count 1 and clean WebSocket close. Node reports nghttp2 1.68.0. Provider identity is OpenSSL 3.5.9 (29 Sep 2026).
63+
64+
| Lane | Programs | Littletest cases | Checks | Skips | Corpus entries / variants / route checks |
65+
|---|---:|---:|---:|---:|---|
66+
| normal-off | 35 | 333 | 31981 | 0 | 52 /4352 /4352 |
67+
| normal-on | 36 | 338 | 32199 | 0 | 52 /4352 /4352 |
68+
| asan-off | 35 | 333 | 31981 | 0 | 52 /4352 /4352 |
69+
| asan-on | 36 | 338 | 32199 | 0 | 52 /4352 /4352 |
70+
71+
Each off lane contains 29 littletest programs plus six standalone header consumers; each on lane adds the five-case TLS boundary suite. The six-case engine harness reports 943 littletest checks in every lane, in addition to its explicit 4352 corpus and 2202 paired-schedule route predicates. Every lane executes the 16 paired schedules and 193 completed-response replay controls. Final source/object freshness is verified in all lanes; a nonfatal normal-on clock-skew warning is retained, and all four conformance objects are newer than the unchanged final source. Existing unrelated unused-helper compiler warnings are retained.
72+
73+
C++ cpplint passes the changed test. All six gate-runner contract tests pass; task-local Python tools compile; littletest skip/result contracts pass (77 all-skip, 0 mixed pass+skip, failure count for fail+skip); git diff --check passes. `final-summary.json` records per-program cases/checks/skips, per-lane source/object freshness, exact full gate receipts, final runtime log/executable/library hashes and unchanged frozen receipt verification. `final-identities.json` also verifies byte identity of the original H2 fixture, independent clients and gate runner/predicates against v3. The normal-off baseline configure/library logs precede the scripted matrix and remain separately identified.
74+
75+
No required local implementation check remains incomplete. Groundwork validation, final acceptance status, merge and cleanup remain caller-owned. TASK-190 action items are checked while card/index remain In Progress.
76+
77+
Final changed C++ source SHA256: `b2261aca6e26ef4fa1985d35d4e1f1ee34bd1e72c8362acdeda9e3fca762088e`.

‎specs/tasks/M14-v3-release-repairs/TASK-190.md‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
**Workflow:** Follow `../v3-release-repair-workflow.md`; helper-resolve TASK-190, require `base_branch=v3`, systematic cause analysis and RED/GREEN, then Groundwork validation/finalization.
1010

1111
**Action Items:**
12-
- [ ] Reproduce `http2_engine_conformance/rfc9113_engine_transcripts` failures for `content-length-data`, `malformed-trailer`, `closed-data`, `stream-window-overflow`, `half-closed-head` (47/52 transcripts currently complete). Trace protocol events versus scheduled header-time handler execution, resets and cancellation across executor batching and all fragmentation splits.
13-
- [ ] Establish whether corpus route expectations or engine behavior violate the documented header-time decision contract. Add focused failing regression for each distinct cause, asserting route observations, terminal scope/code/count, compression state and resource release. A handler may validly observe headers before a later malformed body; an expectation change requires specification-backed evidence and a regression, never blanket acceptance of fewer route checks.
14-
- [ ] Repair minimal engine scheduling/lifetime/error handling or demonstrably invalid oracle; preserve strict RFC 9113 error scope and suffix/HPACK handling after stream resets.
15-
- [ ] Run all corpus fragmentation variants, stream/body/response/WebSocket/drain suites and existing pinned h2spec/independent-client lane. Keep the exact h2spec exclusion inventory and named corpus requirements unchanged.
12+
- [x] Reproduce `http2_engine_conformance/rfc9113_engine_transcripts` failures for `content-length-data`, `malformed-trailer`, `closed-data`, `stream-window-overflow`, `half-closed-head` (47/52 transcripts currently complete). Trace protocol events versus scheduled header-time handler execution, resets and cancellation across executor batching and all fragmentation splits.
13+
- [x] Establish whether corpus route expectations or engine behavior violate the documented header-time decision contract. Add focused failing regression for each distinct cause, asserting route observations, terminal scope/code/count, compression state and resource release. A handler may validly observe headers before a later malformed body; an expectation change requires specification-backed evidence and a regression, never blanket acceptance of fewer route checks.
14+
- [x] Repair minimal engine scheduling/lifetime/error handling or demonstrably invalid oracle; preserve strict RFC 9113 error scope and suffix/HPACK handling after stream resets.
15+
- [x] Run all corpus fragmentation variants, stream/body/response/WebSocket/drain suites and existing pinned h2spec/independent-client lane. Keep the exact h2spec exclusion inventory and named corpus requirements unchanged.
1616

1717
**Acceptance Criteria:**
1818
- All 52 engine transcripts and existing engine tests pass with every route-effect check executed, under normal and ASan/nonrecovering UBSan TLS-off/on.
@@ -24,4 +24,6 @@
2424
- Blocks: TASK-191, TASK-185
2525
**Related Requirements:** PRD-V3N-REQ-005, PRD-V3N-REQ-006, PRD-V3N-REQ-009, PRD-V3N-REQ-023, PRD-V3N-REQ-025, PRD-V3N-REQ-037.
2626
**Related Decisions:** DR-V3-001, DR-V3-003, DR-V3-006.
27-
**Status:** Not Started
27+
**Status:** Complete
28+
29+
**Finalization evidence:** Groundwork validation passed in one iteration with eight approvals, zero fixes and zero unworked findings (run `3b82067abcc68b84b7e78e82f7b4e514`). Implementation build/debug/evidence artifacts are retained beneath `build/task190-evidence/implementation-a5b0d3cb/` in the primary worktree, with a SHA256 archive manifest (4325 records).

‎specs/tasks/_index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ Nominally: **13 sequential tasks**, each S–XL. Most other tasks parallelize of
337337
| TASK-187 | [Repair response-source and multipart coroutine lifetimes](M14-v3-release-repairs/TASK-187.md) | M14 | Complete | TASK-186 |
338338
| TASK-188 | [Eliminate peer-policy mutation starvation](M14-v3-release-repairs/TASK-188.md) | M14 | Complete | TASK-187 |
339339
| TASK-189 | [Repair native HTTP/1 WebSocket handshake and flood progress](M14-v3-release-repairs/TASK-189.md) | M14 | Complete | TASK-188 |
340-
| TASK-190 | [Repair HTTP/2 conformance route-effect ordering](M14-v3-release-repairs/TASK-190.md) | M14 | Not Started | TASK-189 |
340+
| TASK-190 | [Repair HTTP/2 conformance route-effect ordering](M14-v3-release-repairs/TASK-190.md) | M14 | Complete | TASK-189 |
341341
| TASK-191 | [Repair independent HTTP/3 semantic drain completion](M14-v3-release-repairs/TASK-191.md) | M14 | Not Started | TASK-190 |
342342
| TASK-192 | [Supply current-v3 cookie parity witnesses](M14-v3-release-repairs/TASK-192.md) | M14 | Not Started | TASK-191 |
343343
| TASK-193 | [Install public credentials and serve native TCP TLS](M14-v3-release-repairs/TASK-193.md) | M14 | Not Started | TASK-192 |

0 commit comments

Comments
 (0)