Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
904297a
feat: the four conformance suites, the runner and three invariant gates
Wahbeh-Mohammad Sep 23, 2026
1a6ad15
fix: bound every wait in the new suites and raise NFR-14's vacuity
Wahbeh-Mohammad Sep 23, 2026
7721e1b
fix: PackagingCase's default resolve never caught RubyGems' own error
Wahbeh-Mohammad Sep 23, 2026
dd03c35
fix: make XCUT-22's second Check falsifiable with a second teardown r…
Wahbeh-Mohammad Sep 24, 2026
94cd017
test: the suites' own suites, the three drivers and the gate fixtures
Wahbeh-Mohammad Sep 23, 2026
aa79d6c
test: drive the bounded waits, the default resolve and the prefix list
Wahbeh-Mohammad Sep 23, 2026
e95381b
test: drive the shutdown-event match and XCUT-22's second clause
Wahbeh-Mohammad Sep 24, 2026
7132416
test: a defective core for the seven assertions that had none
Wahbeh-Mohammad Sep 24, 2026
59da489
test: defective header-syntax cores for XCUT-18's model assertion
Wahbeh-Mohammad Sep 24, 2026
577cf68
docs: the phase-9 checklist, the as-built rows and the audit's verdict
Wahbeh-Mohammad Sep 23, 2026
2c76530
docs: correct the phase-9 record against review round 1's measurements
Wahbeh-Mohammad Sep 24, 2026
4d834c7
docs: name the overturned predictions and record round 2's two guards
Wahbeh-Mohammad Sep 24, 2026
78b73dd
docs: re-measure the phase-9 proofs at review round 2's tips
Wahbeh-Mohammad Sep 24, 2026
f6fedbe
docs: name R6's one bend and the guard claims that were untrue
Wahbeh-Mohammad Sep 24, 2026
5ec0909
docs: name the fourth survivor and correct two counts in the record
Wahbeh-Mohammad Sep 24, 2026
5e24008
docs: correct NFR-14's level and the stale bounded-map inbound bullet
Wahbeh-Mohammad Sep 24, 2026
ab9a03d
docs: discharge the bounded-map premise in its two other homes
OmarAlJarrah Sep 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/skills/knowledge-lookup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ gives no hint. Never take the entry count as the prefix's ID count.
| Resilience: retry, redirect and authentication | `--topic retry-and-resilience,redirect-handling,authentication,cancellation-and-timeouts --section rules --brief` and `--prefix RETRY,REDIR,AUTH --section rules --brief` | `--prefix-info RETRY`, `--gaps RETRY,REDIR,AUTH,RECOV` | live |
| Serialization, SSE and pagination | `--topic serde,sse-streaming,pagination --section rules,constraints,conclusions --brief` and `--prefix SERDE,SSE,PAGE --section rules,constraints,conclusions --brief` (a `--section rules` reading alone misses `SERDE-17`, `SERDE-24`, `SERDE-25` and `SERDE-30`, filed under Constraints and Conclusions; phase 7a's finding) | `--prefix-info SERDE`, `--gaps SERDE,SSE,PAGE` | live |
| Transport and async-runtime adapters | `--topic transport-adapter,cancellation-and-timeouts,concurrency-and-async --section rules --brief` and `--prefix TRANSPORT,ASYNC --section rules --brief` | `--prefix-info TRANSPORT`, `--gaps TRANSPORT,ASYNC` | live |
| Cross-cutting invariants and the quality bar | `--topic cross-cutting-invariants,tooling-and-quality-gates,resource-management --section rules --brief` and `--prefix XCUT,NFR --section rules --brief` | `--prefix-info XCUT`, `--prefix-info NFR`, `--gaps XCUT,NFR` | live |
| Styleguide-vs-design conflicts | `--section conflicts --brief` | resolve via a note under notes/ | live |

A regex with an alternation cannot live in a Markdown cell without an escape that would be copied
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ jobs:
- run: bundle exec rake steep
- run: bundle exec rake test:gates
- run: bundle exec rake gates:serde_boundary
- run: bundle exec rake gates:cause_walk
- run: bundle exec rake gates:bounded_map
- run: bundle exec rake gates:seam_names
- run: bundle exec rake gates:rbs_surface
- run: bundle exec rake gates:sig_diff
- run: bundle exec rake gates:surface_snapshot
Expand Down
43 changes: 27 additions & 16 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ three-state PATCH — solved exactly once, and it deliberately does not compete
Work here is **spec-driven, not feature-driven**. `docs/product-spec/` is normative: 645 numbered requirements
across 19 prefixes. Before implementing anything, find the requirement IDs it must satisfy.

**Phases 0, 1, 2, 3a, 3b, 4a, 4b, 4c, 5a, 5b, 5c, 6a, 6b, 6c, 7b, 7c, 7a, 8a, 8b and 8c are built — the whole of
**Phases 0, 1, 2, 3a, 3b, 4a, 4b, 4c, 5a, 5b, 5c, 6a, 6b, 6c, 7b, 7c, 7a, 8a, 8b, 8c and 9 are built — the whole of
phase 6, the whole of phase 7, whose three sub-phases were built concurrently off one base and landed in that
order, 7a last (umbrella #25 closes by hand), and the whole of phase 8, whose three sub-phases were built
8a first, off the same base as phase 7's and reconciled onto the tree that holds all three, then 8b and 8c
concurrently off the tree that holds 8a, 8c landing second; the domain model, the seam layer, the
concurrently off the tree that holds 8a, 8c landing second, and phase 9, the audit phase, which ships no
`lib/` code outside `dexpace-conformance` and repairs nothing it finds; the domain model, the seam layer, the
byte-streaming layer, the body layer, the execution context, the recovery layer, the stage pipeline, the
configuration layer, the tracing and metrics layer, the logging facade with its redaction, the retry layer,
the authentication layer, the redirect layer, the server-sent-events layer, the pagination layer, the
Expand Down Expand Up @@ -254,9 +255,11 @@ the synchronous transport is the first thing here that talks to a socket, and th
on the async path — the two meet in `dexpace-async-thread`'s composed suite over a real socket. Both
transports talk to a socket; the async one needs a running reactor on the calling thread and creates none. The workspace root
carries the `Gemfile`, `Rakefile`, `Steepfile`, `rbs_collection.yaml`, `.rubocop.yml`, `.yardopts`, `VERSIONS` and
the eighteen blocking gates — phase 0's seventeen
(`docs/work/mvp/phase0/2026-09-05-phase0-scaffold-and-quality-gates-checklist.md`) and phase 7b's
`gates:serde_boundary`.
the twenty-one blocking gates — phase 0's seventeen
(`docs/work/mvp/phase0/2026-09-05-phase0-scaffold-and-quality-gates-checklist.md`), phase 7b's
`gates:serde_boundary` and phase 9's three repository-wide invariant scans, `gates:cause_walk`,
`gates:bounded_map` and `gates:seam_names`, over one `RubyVM::AbstractSyntaxTree` walker in
`tools/ast_scan.rb`.
Beyond that, what exists is the specification, the port design, the process tooling and the register,
`docs/deviations.md`. Ruby **>= 3.2** is the floor (`required_ruby_version` in every gemspec but one, asserted by
`gates:versions`; `dexpace-transport-async_http` alone declares **>= 3.3**, read from `VERSIONS`' per-gem
Expand Down Expand Up @@ -299,8 +302,8 @@ makes an older Bundler try to install Bundler 4.

```bash
bundle install
bundle exec rake # the default task: all eighteen gates, in order (NFR-17)
bundle exec rake gates:list # the eighteen names, in the order CI and `rake` both use
bundle exec rake # the default task: all twenty-one gates, in order (NFR-17)
bundle exec rake gates:list # the twenty-one names, in the order CI and `rake` both use
bundle exec rake rubocop # NFR-7, findings fatal, no autocorrection
bundle exec rake rubocop:fix # safe autocorrections only, never the gate
bundle exec rake cops:test # the custom cops' own suite (.rubocop/test/)
Expand All @@ -310,6 +313,9 @@ bundle exec rake test:gates # the repository's gate suites
bundle exec rake gates:gemspec_audit # SEAM-1, NFR-1, NFR-2
bundle exec rake gates:require_allowlist # SEAM-1, SEAM-2: the allowlist and the denylist
bundle exec rake gates:serde_boundary # SSE-37, spec-forced boundary 5: no serde under lib/dexpace/{sse,page}/**
bundle exec rake gates:cause_walk # XCUT-9: Dexpace.each_cause is the only walk of a cause chain
bundle exec rake gates:bounded_map # XCUT-14: only Dexpace::BoundedMap holds a caller-keyed map
bundle exec rake gates:seam_names # SEAM-2: core never names a concrete seam implementation
bundle exec rake gates:clean_bundle # the scratch-Gemfile isolation run, all six gems
bundle exec rake gates:rbs_surface # NFR-11
bundle exec rake gates:sig_diff # NFR-4, RBS half (vacuous until the first v* tag)
Expand Down Expand Up @@ -1358,10 +1364,12 @@ probe compares each against the live tree, and a count written anywhere else in
`pool.rb` and `timer.rb`, the last a `private_constant`, every one mirrored in `sig/` and the two public
ones in `test/` (`timer.rb` is proven through `pool_delay_test.rb` and `pool_test.rb`'s source scans) —
and its gemspec declares `dexpace-core` alone, by design: the gem spends none of its `NFR-2` budget;
`dexpace-conformance`'s holds the phase-8a conformance suite with phase 8c's two groups — twenty-five
files beside phase 0's `version.rb`, nine of them `private_constant`s, every one mirrored in `sig/` and
every one but `transport_suite/checks.rb`, `wire_server/recorded_request.rb` and
`wire_server/request_reader.rb` mirrored in `test/` — and its gemspec declares `dexpace-core` alone, its
`dexpace-conformance`'s holds the phase-8a conformance suite with phase 8c's two groups and phase 9's
four further suites — **fifty-four** files beside phase 0's `version.rb`, twenty-five of them
`private_constant`s, every one mirrored in `sig/` and every public one but `check.rb`, `levels.rb`,
the four `*_case.rb` files and `wire_server/recorded_request.rb` mirrored in `test/` (each of those six
is driven through the suite it serves, and `levels.rb` through `test/gates/requirement_levels_test.rb`)
— and its gemspec declares `dexpace-core` alone, its
`socket` and `tempfile` requires carried by the allowlist's exceptions. Every gem under `gems/` is real:
no phase-0 skeleton remains, and the third-party half of each `NFR-2` budget arrived with the phase that
wrote the code needing it — 7a's, 8a's and 8c's (8b's spends none).
Expand All @@ -1371,7 +1379,7 @@ probe compares each against the live tree, and a count written anywhere else in
carry that phase's design, plan and checklist; `phase3/` carries its segmentation design,
`docs/work/mvp/phase3/2026-09-08-phase3-segmentation-design.md`, and two sub-phase directories —
`phase3/phase3a/` and `phase3/phase3b/`, each holding that sub-phase's design, plan and checklist —
twenty checklists written so far, each at implementation; `phase4/`
twenty-one checklists written so far, each at implementation; `phase4/`
carries its segmentation design,
`docs/work/mvp/phase4/2026-09-08-phase4-segmentation-design.md`, and three sub-phase
directories — `phase4/phase4a/`, `phase4/phase4b/` and `phase4/phase4c/`; each holds that sub-phase's
Expand Down Expand Up @@ -1410,9 +1418,11 @@ probe compares each against the live tree, and a count written anywhere else in
gemspec, version and first release phase 8 owns. Its three sub-phases are independent, so
their order is convenience; one task is phase-level because it lands in `dexpace-core`, which
none of the three ships.
`phase9/` carries **no segmentation design and no sub-phase** — it holds its design and its
plan directly, `docs/work/mvp/phase9/2026-09-12-phase9-cross-cutting-invariants-and-conformance-design.md`
and `docs/work/mvp/phase9/2026-09-12-phase9-cross-cutting-invariants-and-conformance.md`. The
`phase9/` carries **no segmentation design and no sub-phase** — it holds its design, its plan
and its checklist directly, `docs/work/mvp/phase9/2026-09-12-phase9-cross-cutting-invariants-and-conformance-design.md`,
`docs/work/mvp/phase9/2026-09-12-phase9-cross-cutting-invariants-and-conformance.md` and
`docs/work/mvp/phase9/2026-09-12-phase9-cross-cutting-invariants-and-conformance-checklist.md`,
the last written at implementation on 2026-09-23. The
roadmap's segmentation rule reaches build phases 1 through 8 and leaves phases 9 and 10 to
segment "only if their own design finds it necessary"; phase 9's design finds it does not, at
41 IDs (`XCUT-1`–`24`, `NFR-1`–`17`) against phase 1's 42 unsegmented rows, and one gem rather
Expand Down Expand Up @@ -1447,5 +1457,6 @@ probe compares each against the live tree, and a count written anywhere else in
at `0.0.0`.
Every checklist but phase 0's, phase 1's, phase 2's, phase 3a's, phase 3b's, phase 4a's, phase 4b's,
phase 4c's, phase 5a's, phase 5b's, phase 5c's, phase 6a's, phase 6b's, phase 6c's, phase 7b's,
phase 7c's, phase 7a's, phase 8a's, phase 8b's and phase 8c's is still to be written at execution time.
phase 7c's, phase 7a's, phase 8a's, phase 8b's, phase 8c's and phase 9's is still to be written at
execution time — which leaves phase 10's alone.
- There are 40 harvested topics under `docs/knowledge/harvested/`; the harvest ran here on 2026-09-05.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ response body, HTTP/2 by ALPN over TLS, and a Ruby floor of 3.3 that this gem al
(`docs/sdk-documentation/transport-async_http.md`) — and `dexpace-conformance` carries the
conformance suite: the assertion protocol, the thirty-four-assertion transport suite with its
vacuous and waived rows, the plaintext wire fixture, the Minitest and RSpec drivers and the two
observability doubles, proven by both transports as its two drivers
observability doubles, proven by both transports as its two drivers — and, since phase 9, four
further suites: twenty-eight assertions over every cross-cutting invariant, eight over the
packaging requirements, two over any wire codec and seven over an async-runtime adapter, with one
aggregate report that states what a green run does not prove
(`docs/sdk-documentation/conformance.md`). `dexpace-async-thread` carries the
async-runtime adapter — the first executor on the async path: a fixed-size thread pool over a
bounded queue whose `#post` never blocks, the bridge that makes a blocking transport asynchronous
Expand Down
3 changes: 3 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ DEFAULT_GATES = %w[
gates:gemspec_audit
gates:require_allowlist
gates:serde_boundary
gates:cause_walk
gates:bounded_map
gates:seam_names
gates:clean_bundle
gates:rbs_surface
gates:sig_diff
Expand Down
6 changes: 5 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,11 @@ transport and the conformance suite are what phase 8a built — the first code o
[`sdk-documentation/async-thread.md`](./sdk-documentation/async-thread.md), because the thread pool
that is the async path's first executor is what phase 8b built, and
[`sdk-documentation/transport-async_http.md`](./sdk-documentation/transport-async_http.md), because
the asynchronous transport, the suite's second driver, is what phase 8c built.
the asynchronous transport, the suite's second driver, is what phase 8c built. Phase 9 added no page
of its own: it ships no gem, and what it built extended two that already existed —
[`sdk-documentation/conformance.md`](./sdk-documentation/conformance.md) gains a section on its four
suites and [`sdk-documentation/quality-gates.md`](./sdk-documentation/quality-gates.md) three rows,
one per repository-wide invariant scan.

## Keeping this file true

Expand Down
22 changes: 17 additions & 5 deletions docs/first-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ the same day, on the same rule — a finding is routed to its owner when it is f
and four of its items came here: two blockers before first publish (documenting the `include Dexpace`
constant shadow, and the `AuthDescriptor` carrier decision), `CFG-20`'s cancel-with-interrupt clause
under the unsatisfied MUSTs, and the `CTX-7`/`CTX-8` drain proof under the post-release triggers. Two
more left a second home here beside their primary owner: the Minitest 6 trigger, and the red
`gates:bounded_map` blocker phase 10's repair clears. **Nothing has been published.** Since phase 0 landed
more left a second home here beside their primary owner: the Minitest 6 trigger, and the
`gates:bounded_map` blocker — which was filed red and expecting a phase-10 repair, and was closed on
2026-09-23 by phase 9's own green run against a cap `8c` had already shipped, with no repair owed. **Nothing has been published.** Since phase 0 landed
(2026-09-14) `gems/` holds the six gems, since phase 1 (2026-09-15) `dexpace-core` carries the HTTP domain
model, and since phase 2 (2026-09-15) the seam layer as well; there is still no tag, and no version beyond the
`0.0.0` every gem starts at.
Expand Down Expand Up @@ -132,8 +133,9 @@ stated in the release notes rather than discovered at `bundle install`.
operation projection carrying a descriptor**. Until that event: either the release notes state that the
tier resolver has no carrier and only the client tier is reachable end to end, or a carrier is built
before the tag
- [ ] **`gates:bounded_map` green: `dexpace-transport-async_http`'s `Clients` is an uncapped
per-origin client cache, which `XCUT-14` (MUST) forbids.** Found 2026-09-13 by phase 9's planning, and
- [x] **`gates:bounded_map` green: `dexpace-transport-async_http`'s `Clients` is an uncapped
per-origin client cache, which `XCUT-14` (MUST) forbids.** — **CLOSED 2026-09-23 by phase 9's
`gates:bounded_map` run.** Found 2026-09-13 by phase 9's planning, and
the **one true positive** of six `gates:bounded_map` reports over 222 filed Ruby fences at 184 distinct
`gems/*/lib/**/*.rb` paths, measured identically on 3.2.11, 3.3.12, 3.4.10 and 4.0.6. The map is
instance-lived and lives as long as the client, its key space is chosen by caller URLs and by a server's
Expand All @@ -152,7 +154,17 @@ stated in the release notes rather than discovered at `bundle install`.
evicted first, every evicted client's pool retired and closed — proven by the three `XCUT-14`
cases in `gems/dexpace-transport-async_http/test/dexpace/transport/async_http/clients_test.rb`;
the gate itself does not exist yet, so the line stays open for phase 9's `gates:bounded_map` run
and nothing else
and nothing else. **Closed 2026-09-23**: phase 9 built the gate and ran it. `gates:bounded_map`
is **green over all 307 files in the six gems' `lib/` trees**, with six adjudicated exceptions,
none of them this one. `clients.rb` is on the allowlist as a **verified false positive of the
scan**, not as a silenced defect — the scan sees a Hash assigned to an instance variable and
cannot see a cap, and the reason recorded beside the entry is the reading of the file: `#fetch`
inserts with `||=` and calls `#drain` inside the SAME `@mutex.synchronize` as the insert, and
`#drain` is a loop, `evicted << @by_key.delete(@by_key.keys.first) while @by_key.size >
MAX_ORIGINS`, with closed reactors evicted first, which is `XCUT-14`'s drain clause exactly.
The as-built ivar is `@by_key` and not `@by_origin`, which is why a check against the planning
note's name would have missed it. `XCUT-14`'s checklist row is ✅ and no phase-10 repair is
filed
- [ ] **Before release, `docs/sdk-documentation/` carries one worked end-to-end example** — a
generated-style client over `dexpace-core` + `dexpace-transport-net_http` + `dexpace-serde-json`:
operation descriptor, request assembly, pipeline with an AUTH step, decode, typed error, one
Expand Down
Loading
Loading