Skip to content

Phase 1: core HTTP domain model — documentation and phase record - #42

Merged
Wahbeh-Mohammad merged 4 commits into
mainfrom
8-phase-1-core-http-domain-model-docs
Sep 15, 2026
Merged

Wahbeh-Mohammad merged 4 commits into
mainfrom
8-phase-1-core-http-domain-model-docs

Conversation

@Wahbeh-Mohammad

@Wahbeh-Mohammad Wahbeh-Mohammad commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Closes #8. Third of three phase 1 PRs — the documentation and the phase record for #40 and #41. Targets #41's branch.

What lands

  • The checklist, docs/work/mvp/phase1/2026-09-05-phase1-core-http-domain-model-checklist.md, written from the build: 42 rows — HTTP-1–HTTP-35, HTTP-46–HTTP-50, HTTP-53, SEAM-29 — 38 ✅ (two by construction, HTTP-1/HTTP-2; two split with a deferred half, HTTP-3/HTTP-46) and 4 ⏳ (HTTP-22, HTTP-48, HTTP-49, HTTP-50: no v1 phase constructs a conditional request; owned by the docs/first-release.md decision line). Plus what was built, the audit groups run, 22 deviations from the plan's text, the findings routed, and the postponed work re-checked (the suppressed trail → phase 4b Task 1; wire-boundary re-validation → phase 8a Task 16 / 8c Task 9 / phase 9 Task 7; the body member's type → phase 3b, P3-15).
  • The phase 1 design's ledger gains P1-11 (HeaderName fold as a derived attribute), P1-12 (Query equality by encoding), P1-13 (URL.parse! re-parses and owns its Strings), P1-14 (RequestOptions tag values are String only), and retires P1-9 as built.
  • docs/knowledge/notes/data-modeling.md — the planning-time note that narrowed design §4's Ractor-shareability claim is replaced by an as-built confirmation: the whole wire model is Ractor.shareable?, Request/Response included, because the uri gem already freezes URI::RFC3986_PARSER at definition and URL.parse! owns its component Strings.
  • docs/sdk-documentation/http.md — new as-built page for the wire model, every commented result verified against the built code; architecture.md, quality-gates.md, the core README, README.md, docs/README.md, docs/first-release.md updated.
  • CLAUDE.md count sentences rewritten for a core that now holds domain code; the roadmap gains the phase 1 status note and a 34th phase-10 inbound bullet (URL.parse!'s host-less / non-HTTP scheme handling, routed rather than changed).
  • One comment-only correction in lib/dexpace/http/url.rb (the YARD block described the retired P1-9).

Verification

  • bundle exec rake on Ruby 4.0.6 at this tip: exit 0, 181 s, all seventeen gates green (273 / 1,956 tests, 100% coverage, YARD 0 undocumented); test:gems green on 3.2.11.
  • ruby .claude/skills/housekeeping/probe.rb: no drift; housekeeping suite 108 runs and knowledge suite 92 runs green; verify_knowledge_structure.rb OK.

Known follow-up from the final review (not blocking)

The roadmap's 2026-09-15 status note says "twenty-one departures … itemised in the checklist" and "five are gate or tool corrections, each pinned by a fixture"; the checklist itemises 22, and three of the five (deviations 6, 9, 10) carry a fixture — 7 (AllowRBSInlineAnnotation) and 8 (rbs:validate -r) do not.

@Wahbeh-Mohammad Wahbeh-Mohammad added this to the v1/mvp milestone Sep 15, 2026
@Wahbeh-Mohammad Wahbeh-Mohammad added type:docs Documentation only area:core Core HTTP, IO, body, context, encoding: HTTP-* IO-* BODY-* CTX-* UTF-* labels Sep 15, 2026
@Wahbeh-Mohammad
Wahbeh-Mohammad added this pull request to stack #43 September 15, 2026 09:06
@Wahbeh-Mohammad

Copy link
Copy Markdown
Contributor Author

Review record for the phase 1 stack (#40 → #41 → #42)

Four independent reviews, each by a fresh agent with no memory of the previous one, each re-running the gates itself rather than reading the implementer's log; three fix rounds between them. Every finding got a stable id, a severity, a file:line and the command output that was its evidence. A round's verdict was approve only with zero blocking and zero should-fix findings; the run stopped at the agreed cap of four reviews.

Round Verdict Blocking Should-fix Nits Disposition
0 changes required 0 3 3 all 6 fixed
1 changes required 0 2 3 all 5 fixed (one routed, see R1-5)
2 changes required 0 3 3 all 6 fixed (one routed, see R2-6)
3 (final) changes required 0 2 3 carried into the PR bodies

No finding was ever blocking; nothing was skipped.

Round 0 → fixed in round 1

  • R0-1 Response aliased the caller's mutable reason String — a post-construction mutation changed the model (XCUT-15/HTTP-1). Fixed: reason type-checked and owned through Model.frozen_string.
  • R0-2 Headers.build / #with with a non-Hash values/casing leaked NoMethodError past rescue Dexpace::Error. Fixed: container checks before validate_names!, raising InvalidArgumentError.
  • R0-3 The corpus still marked harvested rule data-modeling/996c0b12 (design §4's Ractor claim) as overridden by the planning-time note after the build had disproved the narrowing. Fixed: the two entries folded into one as-built confirmation.
  • R0-4 MediaType#matches? on a non-MediaType → SDK error. R0-5 ten parser regexps and codec tables made private_constant (they were in the NFR-4 manifest). R0-6 Model#with typed -> self instead of -> untyped.

Round 1 → fixed in round 2

  • R1-1 A Request accepted a Headers validated by the inbound grammar, so obs-text/non-ASCII values could ride out through .build and Request::Builder#headers= (HTTP-18, XCUT-18). Fixed: Request#initialize requires direction == :outbound.
  • R1-2 URL.own's comment still described the retired P1-9. Fixed.
  • R1-3 Model.own ran in .build before the container type check in initialize, so a non-copyable object escaped as a stdlib TypeError — moved into each model's initialize after every shape check. R1-4 Query.parse on a non-String → SDK error. R1-5 tag values restricted to String where HTTP-34 says "opaque" — recorded as a deliberate narrowing, ledger row P1-14.

Round 2 → fixed in round 3

  • R2-1 A String under a dummy (stateful) encoding whose bytes are pure ASCII passed every byte validator and then crashed the fold or the scanner with Encoding::CompatibilityError from inside Ruby. Fixed: HeaderSyntax.ascii_compatible normalises proven-ASCII bytes under an ASCII-compatible tag before any fold/upcase/scan.
  • R2-2 The checklist's roll-up said 36 ✅ where the table had 38 (36 + 4 ≠ 42). Recounted from the table; roadmap note corrected.
  • R2-3 HTTP-19's third clause — inbound header names stay strictly validated — had no test anywhere. Added in headers/builder_test.rb and headers_test.rb.
  • R2-4 Complex timeout → NoMethodError, Float::INFINITY accepted — now Numeric && real? && finite? && positive?. R2-5 Model#with(5) → SDK error. R2-6 URL.parse! accepts a host-less http: and any absolute non-HTTP scheme — behaviour kept (the rejection belongs to whoever knows what is dispatchable) and routed as phase 10 inbound bullet 36.

Round 3 (final) — open, carried into the PR bodies

  • R3-1 should-fix — Model.own on a Hash carrying a default_proc (Hash.new { |h, k| h[k] = [] }) passes the shape checks and then Ractor.make_shareable(copy: true) raises TypeError: allocator undefined for Proc, escaping rescue Dexpace::Error from Headers.build, RequestOptions.build and the options builder. Identical on 3.2.11 and 4.0.6. Listed on Phase 1: core HTTP domain model — the wire model #40.
  • R3-2 should-fix — the roadmap status note says 21 deviations and "five gate/tool corrections each pinned by a fixture"; the checklist itemises 22 and three of the five carry a fixture. Listed on Phase 1: core HTTP domain model — documentation and phase record #42.
  • R3-3 Query.parse trims with strip on bytes, dropping a raw trailing NUL where the header path trims SP/HTAB only. R3-4 the public HeaderSyntax predicates and Headers::Builder.new(values:) raise NoMethodError on a non-String. R3-5 long commit subjects on the repair commits.

What the final reviewer verified, in its own runs

  1. Gates — Phase 1: core HTTP domain model — the wire model #40 tip: all seventeen run individually on 4.0.6, sixteen green, test:gems red on the coverage floor alone (60.58%, 20 runs, 0 failures). Phase 1: core HTTP domain model — tests #41 and Phase 1: core HTTP domain model — documentation and phase record #42 tips: full bundle exec rake green on 4.0.6 (273 runs / 1,956 assertions, 789/789 lines), test:gems green on 3.2.11, the four matrix gates green on 3.2.11, probe clean.
  2. Construction pattern by experiment on 3.2.11 and 4.0.6 with identical output: every model Data + Model, new private, .build validating; every required-field message exactly <name> is required; #with re-validates on the floor — and removing Model#with makes Status.of(200).with(code: nil) succeed on 3.2.11, so it is proven load-bearing; collections frozen once with the same reference from every accessor; new_builder isolation for all five builders; XCUT-15 live-Hash and live-URI isolation; Ractor.shareable? true for every model including Request/Response; the send(:new) gap reaches the constructor and is documented, not hidden.
  3. HTTP rules by experiment — HTTP-7 rejected for GET/HEAD/TRACE/CONNECT via builder, .build and #with; HTTP-8 defaulting; HTTP-13 fold locale-free (I→i; İ/ı/ß rejected as non-ASCII names; cross-casing equality and hash); HTTP-17–20 over CR, LF, NUL, DEL, SP, HTAB, obs-text and invalid UTF-8 — every rejection an SDK error, none from Ruby, the value never echoed, NUL surviving trim; Status total over 100–599; MediaType parse/render/matches and every HTTP-53 rejection; PercentEncoding byte round-trip including %FF; URL.parse! rejections with cause; RequestOptions HTTP-34/35. Grep over core lib: no URI.parse/join/split, no DEFAULT_PARSER, no Time.parse, no Timeout.timeout/Thread#raise/Thread#kill, no locale argument to a case fold.
  4. Layering — every file in each of the three diffs classified; history linear, 14 commits, 0 merges.
  5. Global constraints — SPDX + frozen_string_literal on every .rb; no # typed:; dexpace-core.gemspec untouched with zero add_dependency; plain requires exactly uri and strscan; frozen trees show an empty diff; nothing resolved-per-interpreter committed; sig/ mirrors lib/ 23-for-23 and test/ mirrors the 22 phase-1 files; every test class inherits DexpaceTestCase; every test header names its IDs; the manifest diff adds exactly this phase's constants (212 lines, nothing removed).
  6. Plan and spec coverage — all 17 tasks' files and expected outputs; each issue Scope bullet mapped to code; all 42 IDs have a row (script-verified); every ✅ row's cited test read in full and confirmed to assert the property; the 22 recorded deviations checked against the code; ledger rows P1-11–14 and the P1-9 retirement match the built behaviour; the docs/sdk-documentation/http.md example run against the code.
  7. Phase record — checklist roll-up matches its table (42/38/4); CLAUDE.md counts match the tree; postponed work routed to phase 10's inbound list and docs/first-release.md, no register introduced.
  8. Commit hygiene — no attribution or co-author lines; clean tree at every tip.

Run: workflow wf_c0b684c5-187, 8 agents (1 implementer, 4 reviewers, 3 fixers), 2.4M tokens.

@Wahbeh-Mohammad
Wahbeh-Mohammad force-pushed the 8-phase-1-core-http-domain-model-docs branch from c88970d to e37b406 Compare September 15, 2026 09:08
Base automatically changed from 8-phase-1-core-http-domain-model-tests to main September 15, 2026 09:08
…main-model page

The forty-two-row checklist, written from what was built; three ledger
rows added at implementation (P1-11, P1-12, P1-13) and P1-9 retired as
built, with the corpus note that carried it corrected; the roadmap's
dated status note and a thirty-fourth phase-10 inbound bullet; CLAUDE.md,
the READMEs and docs/first-release.md brought to the built tree; and
docs/sdk-documentation/http.md, the first as-built page for
dexpace-core.
…ues as a reading (P1-14), and correct URL.own's comment
…hecklist roll-up, cite HTTP-19's name tests and route URL.parse!'s host-less shapes
@Wahbeh-Mohammad
Wahbeh-Mohammad force-pushed the 8-phase-1-core-http-domain-model-docs branch from e37b406 to cb45703 Compare September 15, 2026 09:08
@Wahbeh-Mohammad
Wahbeh-Mohammad merged commit 025fa74 into main Sep 15, 2026
@Wahbeh-Mohammad
Wahbeh-Mohammad deleted the 8-phase-1-core-http-domain-model-docs branch September 15, 2026 09:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core Core HTTP, IO, body, context, encoding: HTTP-* IO-* BODY-* CTX-* UTF-* type:docs Documentation only

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 1: Core HTTP Domain Model

1 participant