Skip to content

Phase 2: seam foundations — documentation and phase record - #46

Merged
Wahbeh-Mohammad merged 17 commits into
mainfrom
9-phase-2-seam-foundations-docs
Sep 15, 2026
Merged

Wahbeh-Mohammad merged 17 commits into
mainfrom
9-phase-2-seam-foundations-docs

Conversation

@Wahbeh-Mohammad

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

Copy link
Copy Markdown
Contributor

Closes #9. Third of three phase 2 PRs — the documentation and the phase record for #44 and #45. Targets #45's branch.

What lands

  • The checklist, docs/work/mvp/phase2/2026-09-07-phase2-seam-foundations-checklist.md, written from the build: 30 rows (SEAM-1–SEAM-30) — 23 ✅ (two with a named gap, SEAM-15 and SEAM-25; SEAM-29 satisfied in phase 1), 3 ⏳ (SEAM-12, SEAM-24, SEAM-28 → phase 5c Task 4), 3 🚫 (SEAM-3, SEAM-4 — the byte-stream provider seam retired per design §10.1, its behavioural contract phase 3's — and SEAM-22's reflective mechanism, replaced by the witness protocol of §10.14), 1 N/A (SEAM-10, vacuous in Ruby, the version-skew guard built in its place). Plus what was built, the twelve guards run red with their messages, the audit groups run, thirty deviations from the plan's text, the findings routed, and the postponed work re-checked (close_quietly's two routes and Hooks.notify's dropped failures → phase 4b Task 2; the deadline: keyword → 5a Task 8; SEAM-25's lifecycle event → 8b and 9).
  • The phase 2 design's ledger gains P2-8 (the sixth cop) through P2-15 (Dexpace::Hooks), with Registry recorded as public API (P2-10) and the bridges' Dexpace::Bridge namespace (P2-13).
  • docs/sdk-documentation/seams.md — new as-built page for the seam layer, every example verified against the built code; architecture.md, quality-gates.md (seven cops now), the core README, README.md, docs/README.md, docs/first-release.md updated.
  • CLAUDE.md count sentences re-derived from the tree (42 phase-1/2 files beside version.rb, 43 sig mirrors, seven cops, a 318-line manifest); the roadmap gains the phase 2 status note.

Verification

  • bundle exec rake on Ruby 4.0.6 at this tip: exit 0, 191 s, all seventeen gates green (529 / 3,376 tests, 99.93%, YARD 0 undocumented); test:gems on 3.2.11: 529 runs, 100.00%.
  • ruby .claude/skills/housekeeping/probe.rb: no drift; housekeeping suite 108 runs and knowledge suite 92 runs green; verify_knowledge_structure.rb OK.
  • The reviewer re-derived every count the status note states against the checklist and the tree.

Known follow-up (not blocking)

Three commit subjects on the repair commits run to 80–86 characters against the repository's 74; squash-merge titles are the PRs'.

Dexpace::SeamError, ClosedError and CancelledError (SEAM-5, SEAM-15,
SEAM-18); Dexpace::Closeable with the latched, ownership-aware close and
Dexpace.close_quietly (SEAM-14, SEAM-25); Dexpace::Cancellation and
Cancellation::Source, the cooperative token both transport seams take as
their third argument (SEAM-13), composing by monotonic stamp rather than
by subscription and returning a detachable Subscription from #on_cancel;
and Dexpace::Hooks, the one notification loop every write side runs so a
raising handler drops no other (design P2-14, P2-15).

Every mutex here is held across a flag flip or a snapshot swap and
nothing else. sig/dexpace/hooks.rbs exists because the strict core Steep
target checks every lib/ file; the constant stays private.
Dexpace::Async::Settlement, Completer and Future (SEAM-16, SEAM-17,
SEAM-30): the state lives on the Completer, the Future is a facade, the
queue is a wake-up signal and never the value channel, #fulfil on a lost
race closes the orphan it was handed, #value blocks on a Thread::Queue
so a registered Fiber.scheduler sees the wait, and #then is the one
combinator (design P2-11). The outcome is published, and the abort hooks
stolen, in one snapshot swap, so a raising hook can never leave a waiter
blocked.

Dexpace/QualifiedCoreConstant, the sixth custom cop (design §9 addendum
A1): a bare Thread, Queue, Mutex, SizedQueue, ConditionVariable or JSON
inside Dexpace::Async or Dexpace::Serde rebinds to the adapter gem's
constant once that gem is required, and core's own suite never requires
it. Scoped to lib/dexpace/async/, lib/dexpace/serde/ and the seam module
lib/dexpace/serde.rb itself.
Dexpace::Registry implements SEAM-5..SEAM-9's five branches once -- one
frozen snapshot swapped under a mutex, unsynchronised reads, a
single-flight claim that builds outside the lock and records its owning
fiber so a re-entrant resolve raises instead of parking, a claim released
in an ensure so a LoadError leaves the registry re-evaluable, and the
version-skew guard phase 0 postponed here as the required core: keyword,
compared by hand because Gem is undefined under --disable-gems (P2-7).

Dexpace::Transport, Dexpace::AsyncTransport (P2-1) and Dexpace::Serde:
each a duck type, a .conforms? predicate, an RBS interface and five
delegations to its own registry; the zero-candidate error names no gem
(SEAM-2). Dexpace::Bridge::AsyncOver and SyncOver, SEAM-18's two
bridges (P2-13), both Closeable and owning nothing; AsyncOver checks
the token before and after the send and refuses an async transport at
the first send. Dexpace::Serde::Error, a class root, with the encode and
decode subtypes (SEAM-23, P2-2). Dexpace::Operation with its
construction-time placeholder checks and the hand-built SEAM-27
composition, which is a concatenation and not RFC 3986 reference
resolution (P2-3). lib/dexpace.rb requires the twenty files in
dependency order.
The runtime surface manifest of dexpace-core grows from 212 to 318
lines, exactly the public constants and methods the twenty files
define; no private_constant appears. The four adapter manifests each
lose the one namespace line -- Dexpace::Transport, Dexpace::Serde or
Dexpace::Async -- that core now defines, and the gate test that had
pinned the phase-0 shape (an adapter's manifest beginning at the shared
namespace) now asserts the property its own comment states: the
manifest begins inside the shared namespace and holds none of core's
lines. The core smoke suite's constant list gains the seam layer.
Registry.accepts_positionals? counted :req, :opt and :rest and ignored
:keyreq, so ->(request, options, cancellation, must:) {} passed
Transport.conforms? and AsyncTransport.conforms?, was accepted by
install and register, and raised Ruby's ArgumentError (missing keyword)
from inside the seam at the first send -- failure at use where the
predicate exists to fail at registration, and a stdlib error where the
seam promises InvalidArgumentError. The design's stated predicate
("required count <= 3 and (a rest parameter is present or required +
optional >= 3)") omitted keywords and the implementation reproduced the
gap; one clause closes it. An optional keyword, a keyword rest and a
block parameter leave the three-positional call intact and stay
admitted. Verified on 3.2.11 and 4.0.6.

Operation#build_request now treats a nil header input as absent, as it
already treats a nil query input, rather than sending the header with
an empty value: a generated client passing an unset optional header as
nil must not emit `X-Trace:`. An empty String is a value and goes out.

registry.rbs spells ::Method::param_types with the leading ::, since
Dexpace::Method exists in the same namespace and the relative name
resolved to Ruby's Method only because the phase-1 class declares no
such alias -- the RBS twin of the hazard Dexpace/QualifiedCoreConstant
guards in lib/.
…base

Operation validated a template's brace balance and its placeholder /
projection agreement and never looked at the literal text between the
placeholders, so `Operation.build(method: :get, template: "/x?y")` --
a generator putting a literal query in the template -- and "/a b",
"/pets/ü", "/100%", "/a<b", "/x#f", "/[x]" all constructed and then
raised URI::InvalidComponentError from URI::Generic#path= inside
Composition.compose at the first #build_request: a stdlib error, outside
`rescue Dexpace::Error`, where the plan promises InvalidArgumentError
for a malformed template and SEAM-27 a context-bearing error for a
composition resolving to a malformed URL. Path VALUES were never at
risk (encode_component yields pchars, and the property test proves it);
only the literal was unguarded.

Validation.literal! now checks the template with its placeholders
removed against RFC 3986 `path` -- every character a pchar or "/",
every "%" opening a two-hex-digit escape, the grammar #path= enforces
at assembly -- at construction, naming the template. The brace check
moves into the same step so an unbalanced brace is still reported as
such. An already-encoded literal such as "/a%20b" is a path and passes;
"pets" and "" pass as before. The set is spelled out rather than
borrowed from URI: phase 1's URL is the one place core reaches URI.

The probe that found the literal found its sibling: a base with no
hierarchical part ("mailto:x@y", "urn:isbn:123") parses as absolute,
passes URL.parse! and the fragment check, and leaks URI::InvalidURIError
"path conflicts with opaque" from the same #path=. validated_base now
refuses it beside the fragment, naming the base. Verified identical on
3.2.11 and 4.0.6 before and after.
…onto it

Phase 1's URL.parse! reaches URI::Generic#query=, whose percent check is
/(%\H\H)/ -- a "%" followed by two NON-hex characters -- so a base query
ending in a bare "%" or in "%z" ("https://host/c?sig=100%",
"https://host/c?;~%", "https://host/c?a=%z") is accepted at parse time.
Composition.compose then appends "&limit=1", the same writer sees "%&l"
and a stdlib URI::InvalidURIError escapes #build_request: the leak class
the template-literal and opaque-base fix closed on the path side,
surviving on the query side, where SEAM-27 requires a composition
resolving to a malformed URL to be rejected with a context-bearing error.

Operation gains QUERY_LITERAL, RFC 3986 `query` (pchar / "/" / "?",
every "%" a two-hex escape), and validated_base checks the parsed base's
query against it before anything is appended, raising
Dexpace::InvalidArgumentError naming the base -- whether or not the
operation query is empty, so a base malformed on its own is refused
rather than composed into a malformed URL silently. RFC 3986 `query` is
strictly tighter than #query='s check and the operation query is
Query#encode's, so no append can complete a "%\H\H"; the path side's
grammar was re-derived the same way (the parser's `segment` set equals
#path='s ABS_PATH), and compose's comment now states why neither writer
is rescued. Measured on 3.2.11 and 4.0.6 with a 40,000-base fuzz over
nine base shapes and four operations: 29,738 bases accepted by
URL.parse!, 5,031 stdlib leaks before, 0 after, identical on both.

Registry#register and #install build their conflict messages after the
lock is released rather than inside it: both interpolate #inspect of two
user objects, and a factory or provider whose #inspect reached back into
the registry met ThreadError: recursive locking instead of the argument
error. #install's swap moves into a private swap_in that reports the
conflicting incumbent and the handed-out flag out of the block, so the
synchronize body is the snapshot swap and nothing else, as the file's
own rule states; the sig/ mirror follows.
Twenty-four suites under gems/dexpace-core/test/ -- one mirror per lib/
file but the private_constant hooks.rb, plus the scheduler-transparency
and constant-shadowing proofs, the version-skew grid against
Gem::Requirement over six running versions, the SEAM-27 composition
suite with its property test, and the seam-surface suite -- each
header naming the IDs it exercises. Eight test-support files: the three
in-memory fakes the roadmap's constraint 4 asks for, their companions
one class per file, the probe Fiber.scheduler and the block-scoped
WarningCapture (design P2-12). The cop's nine rejected and eight
accepted cases, in a nested class of their own.

Every concurrency guard the plan's verification table lists was run red
by reverting its fix, on 4.0.6, and restored; the checklist records what
each said.
…bsent

Registry.callable? -- and through it Transport.conforms? and
AsyncTransport.conforms? -- admitted a callable with a required keyword
beside its three positionals: registration succeeded and the first send
raised Ruby's ArgumentError (missing keyword) from inside the seam, the
outcome the predicate exists to prevent. The refutes land in
registry_test.rb (whose callable? cases move into a nested Callable
class, under the 100-line cap), transport_test.rb and
async_transport_test.rb; an optional keyword, a keyword rest and a block
parameter stay admitted. Red before the fix:
"callable? refuses a required keyword and admits the optional keyword
shapes: Expected true to not be truthy."

Operation#build_request rendered a nil header input as an empty header
(X-Trace: "") where a nil query input contributes nothing; the new case
in operation_build_request_test.rb takes the query side's reading and
says why. Red before the fix: "Expected #<data Dexpace::Headers
values={"x-trace" => [""]} ...> to not include "X-Trace"."

future_shadowing_test.rb's header now names SEAM-16 and SEAM-17, the
IDs it exercises under the adapter namespace.
…are refused

operation_test.rb gains the negative case beside "an unbalanced brace
and an empty placeholder are refused": thirteen literals -- "/x?y",
"/x#f", "/a b", "/pets/ü", "/100%", "/%2", "/%G1", "/a<b", "/[x]", a
quote, a tab, a newline and "/{id}?q" -- each refused at construction
with Dexpace::InvalidArgumentError (asserted to be a Dexpace::Error)
naming the template, and the positive case: every pchar, a slash, an
already-encoded octet, a rootless "pets", "" and a bare "{id}" all
construct. Red before the fix: "/x?y". Dexpace::InvalidArgumentError
expected but nothing was raised.

operation_build_request_test.rb gains the opaque base -- "mailto:x@y",
"urn:isbn:123" -- refused with a context-bearing error naming the base.
Red before the fix: [Dexpace::InvalidArgumentError] exception expected,
not Class: <URI::InvalidURIError> Message: <"path conflicts with
opaque">. And, in a third nested class under the 100-line cap, the
literal's twin of the path-value property: over 200 sampled templates
drawn from pchars, "/", "?", "#", "%", "{", "}", a space and "ü",
either .build refuses the template as the SDK's error or #build_request
composes a URL that re-parses to itself; only InvalidArgumentError is
rescued, so a stdlib URI error from the composition errors the test.
Red before the fix: URI::InvalidComponentError: bad component(expected
absolute path component): /c/F/ #ü. 36 of the 200 compose.
operation_build_request_test.rb gains the negative case beside the
opaque base: "https://host/c?sig=100%", "https://host/c?;~%",
"https://host/c?a=%z" and "https://host/c?a=[1]" -- each accepted by
URL.parse!, whose URI::Generic#query= check is /(%\H\H)/ -- refused by
build_request with Dexpace::InvalidArgumentError (asserted to be a
Dexpace::Error) naming the base and the query, with and without an
operation query appended; "https://host/c?sig=100%25" composes.

A fourth class, Bases, holds the base-varying twin of the Templates
property: 200 random queries over an alphabet of "a", "/", "?", "#",
"%", "2", "F", "&", "=", ";", "~", "[", "]", " " and "ü", each either
refused as the SDK's argument error or composed into a URL that
re-parses to itself, with a non-empty operation query so the append
that reaches URI::Generic#query= runs on every sample.

Red before the fix: [Dexpace::InvalidArgumentError] exception expected,
not Class: <URI::InvalidURIError> Message: <"invalid percent escape:
%&l"> from operation.rb:318 Composition#compose, and the property errors
with the same URI::InvalidURIError.
… outside the lock

registry_test.rb's Reentrancy class gains two cases: a registration
conflict and an install conflict whose rejected object's #inspect
registers a second key on the same registry -- taking its write lock --
each raising Dexpace::InvalidArgumentError with the message naming the
object, and the second registration landing.

Red with the fix reverted, on both: [Dexpace::InvalidArgumentError]
exception expected, not Class: <ThreadError> Message: <"deadlock;
recursive locking"> from registry.rb:120 Registry#register (the message
interpolated under @Write) and from registry.rb:144 via conflict!
inside Registry#install's synchronize block.
The thirty-row checklist, written from what was built, with the
twenty-one guard runs recorded and twenty-five departures from the
plan's text itemised; three as-built notes beneath the design's
Deviation Ledger (P2-9, P2-12, P2-15); the roadmap's dated status
note; CLAUDE.md, the READMEs, docs/first-release.md and
docs/sdk-documentation/quality-gates.md brought to the built tree; and
docs/sdk-documentation/seams.md, the as-built page for the seam layer.
docs/sdk-documentation/seams.md said a resolution that completed inside
a #swap block survives the block; it does not. Only the in-flight claim
and the factories are taken from the live state; the resolved provider
is restored, so a build that completes inside the block is discarded
and closed and the next resolve builds again. The page now says so,
states the transport predicate's required-keyword rule, records that
Serde.conforms? is presence-only beside the transport seams' admitted
gap, and states the three nil-input readings of Operation#build_request
together.

Dexpace/QualifiedCoreConstant is the seventh custom cop in the tree,
not the sixth: phase 1 added Dexpace/NoKeywordSplat after the phase-2
design was written. CLAUDE.md and the roadmap's status note now count
seven, the checklist says once why the design's addendum says six, and
the design's as-built notes record it beside the predicate refinement.

CLAUDE.md's "forty-two files under lib/dexpace/" is literally
forty-three; the sentence now names phase 0's version.rb beside the
forty-two phase-1 and phase-2 files it was counting.

The checklist gains the review round's two red-then-green guards,
deviations 26 and 27, the Callable class in registry_test.rb, and the
run counts of the rebuilt tree.
…ase rule

The checklist's status roll-up said "22 ✅ … 22 + 3 + 3 + 1 = 30, recounted
from the table"; the table has 23 ✅ rows (SEAM-1, 2, 5, 6, 7, 8, 9, 11,
13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 25, 26, 27, 29, 30 -- the three
qualified ones included, none subtracted) and the stated sum was 29. Both
the sentence and the roadmap status note's "thirty rows, 22 ✅" now say
23. The note's "forty-two files" takes CLAUDE.md's wording -- forty-two
phase-1 and phase-2 files beside phase 0's version.rb -- since the
directory holds 43.

The round-2 fix is recorded where the phase's as-built record lives: the
SEAM-27 row names the two places a stdlib URI error could escape the
composition and how each is closed; deviation 28 carries the argument;
the guards-run-red section carries the three red messages; the design's
"as built" section gains the composition rule beside the predicate; the
seams page states the template-literal and opaque-base refusals with the
composition's other rules. Gate counts follow the tests branch: test:gems
525 runs / 3265 assertions, 99.93% (1512/1513) on 4.0.6, 525 runs on
3.2.11.
…ount the roll-up

The checklist gains deviations 29 (a base URL whose query is not RFC 3986 is refused before composition, with the argument and the 40,000-base measurement behind the 'cannot raise' claim deviation 28 stated without either) and 30 (the registry's two conflict messages are built outside the lock), the round-3 red-run table, the SEAM-27 row's third closed leak and the thread-safety audit row's corrected list. The design's as-built notes gain the same two entries; docs/sdk-documentation/seams.md states the query rule beside the fragment and opaque ones and adds #inspect to what never runs under the registry lock; the roadmap's status note counts thirty departures, the number the checklist's list has carried since round 2 added its twenty-eighth.
test:gems is 529 runs / 3376 assertions at 99.93% line coverage (1521/1522 on 4.0.6, 1515/1516 on 3.2.11) with the four guards review round 3 added; deviation 25 and the 3.2.11 sentence follow.
@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

Copy link
Copy Markdown
Contributor Author

Review record for the phase 2 stack (#44 → #45 → #46)

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

Round Verdict Blocking Should-fix Nits Disposition
0 changes required 0 3 6 all 9 fixed
1 changes required 0 2 1 all 3 fixed
2 changes required 0 1 2 all 3 fixed
3 (final) changes required 0 2 2 carried into the PR bodies

No finding was ever blocking; nothing was skipped.

Round 0 → fixed in round 1

  • R0-1 seams.md claimed a resolution completing inside a #swap block survives the block; only the live resolving claim is spliced, the resolved provider is discarded. Doc corrected to what the ensure actually does.
  • R0-2 CLAUDE.md called QualifiedCoreConstant "the sixth custom cop"; seven exist. Corrected in CLAUDE.md and the roadmap note (the checklist keeps one "sixth" with the reason: the design's §9 addendum counts it sixth, the tree seventh).
  • R0-3 Transport.conforms? accepted a callable with a required keyword, which then fails with ArgumentError at the first #call. Registry.accepts_positionals? now returns false on :keyreq; tests first.
  • R0-4 a nil header input rendered X-Trace: "" while a nil query input contributed nothing — now absent. R0-5 Serde.conforms? is presence-only (a codec whose #load takes no witness passes) — recorded as an admitted gap beside the transport seams' rather than changed, since the design fixes it as presence-based. R0-6 the shadowing proof's header named no ID (SEAM-16/17 added). R0-7 "forty-two files" was 43 (phase 0's version.rb) — reworded. R0-8 Method::param_types in registry.rbs resolved to ::Method only by luck — qualified. R0-9 four commit subjects over 75 chars — stack rebuilt by cherry-pick + amend.

Round 1 → fixed in round 2

  • R1-1 A template literal that is not a legal URI path (space, quote, ?, #, %, <, non-ASCII) was accepted by Operation.build and leaked URI::InvalidComponentError from #build_request. Fixed by construction-time validation: a private_constant PATH_LITERAL regexp (per-pattern timeout, RFC 3986 path spelled out), plus an opaque base refused.
  • R1-2 Checklist roll-up said 22 ✅ where the table had 23, and the stated sum was 29. Recounted mechanically; roadmap note aligned.
  • R1-3 the roadmap note's "forty-two files" — same off-by-one as R0-7, same fix.

Round 2 → fixed in round 3

  • R2-1 A base URL whose query ends in a bare % passed URL.parse! and leaked URI::InvalidURIError from Composition.compose's #query= when an operation query was appended — the R1-1 leak class on the query side. Fixed by a QUERY_LITERAL check in validated_base; a base property test added.
  • R2-2 the roadmap note's deviation count (27 vs 28, then 30) — re-derived. R2-3 Registry#register/#install ran #inspect on user objects inside the @write synchronize block on their conflict paths — the conflicting incumbent is now returned out of the block and the error raised after it.

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

  • R3-1 should-fix — a mailto:// base with an authority (mailto://host/?x=1) passes validated_base (#hierarchical? is true) but carries an @opaque that URI::MailTo synthesises from the query, so compose's #path= raises URI::InvalidURIError: path conflicts with opaque out of #build_request. The R1-1/R2-1 leak class surviving a third time, on a scheme no transport can dispatch. Listed on Phase 2: seam foundations — the seam layer #44.
  • R3-2 should-fix — phase 1's URL.parse! leaks URI::InvalidComponentError for mailto://host (a sibling of InvalidURIError, not rescued) and a FrozenError from URI::FTP#to_s on ftp://…;type=x (the URI is frozen and #to_s mutates it). Inherited by #build_request and Request::Builder#url=. Listed on Phase 2: seam foundations — the seam layer #44; URL.parse!'s scheme handling is already phase 10 inbound bullet 36.
  • R3-3 Source#off_cancel's comment gives the wrong reason (Array#delete compares Procs with ==, not identity; the conclusion holds because each registration closes over its own frame). R3-4 three repair-commit subjects at 80–86 chars.

What the final reviewer verified, in its own runs

  1. Gates — Phase 2: seam foundations — the seam layer #44 tip: all seventeen individually on 4.0.6, sixteen green, test:gems red on the coverage floor alone (73.12%, 274 runs, 0 failures). Phase 2: seam foundations — tests and fakes #45 and Phase 2: seam foundations — documentation and phase record #46 tips: full bundle exec rake green on 4.0.6 (529 runs / 3,376 assertions, 99.93%), test:gems green on 3.2.11 (100.00%), probe clean; the gem suite three more times on different seeds under timeout 300 — 512 runs, 0 failures, no hang on any seed.
  2. Concurrency discipline — every synchronize block in registry.rb, closeable.rb, cancellation.rb, cancellation/source.rb and async/completer.rb read: each a flag flip, a snapshot swap or a list push/steal, nothing else; warn, #inspect, #release, #close, factories, .conforms?, gate.close and every hook run outside. The pivot's queue carries no value. No Timeout.timeout, Thread#raise/#kill, Thread.current[] carrier, or resource acquisition inside an Enumerator.
  3. All twelve planning-table guards reverted in a scratch copy and run red under timeout, each with the predicted message; the re-entrant-resolve trio hangs and alone dies on Ruby's own deadlock detector (fatal: No live threads left).
  4. The seams by experiment, 91 checks, 0 failures, identical on 4.0.6 and 3.2.11: the five registry branches with their classes and messages; the skew guard raising on a mismatched Dexpace::VERSION and working under ruby --disable-gems; .conforms? rejecting near-misses; both bridges honouring SEAM-18's interruption clause with two waiters; cancellation ordering, once-only publish, Completer#await detaching; Closeable idempotence and ClosedError; the SEAM-27 conformance example differing from URI#merge; the FakeTransport double end to end. A fuzz of 9,645 bases / 31,206 compositions, every one re-parsing to itself. The ::Thread shadowing proof: a bare Thread inside Dexpace::Async resolves to Ruby's before and to Dexpace::Async::Thread after the skeleton is required; the cop fires on the bare name.
  5. Layering — every file in each diff classified; linear, 17 commits, 0 merges.
  6. Global constraints — SPDX + frozen_string_literal on all 57 changed .rb; no # typed:; zero add_dependency; no plain require added (only require_relative); no Gem reference in core; frozen trees untouched; nothing resolved-per-interpreter committed; sig/ mirrors lib/ 20-for-20, test/ 19 mirrors + 5 extra suites; every test class inherits DexpaceTestCase; the manifest diff is exactly this phase's constants and no private_constant leaks.
  7. Plan and spec coverage — the 16 tasks' files and outputs; each Scope bullet mapped; all 30 SEAM IDs have a row (23 ✅ / 3 ⏳ / 3 🚫 / 1 N/A) and the cited test read for SEAM-2/5/6/7/8/9/11/13/14/15/16/18/19/20/21/23/25/26/27/29/30; the thirty recorded deviations checked against the code; the two named postponements (Hooks.notify, close_quietly's routes → phase 4b) recorded with owner and pick-up condition.
  8. Phase record — CLAUDE.md counts re-derived (six gems, 42 phase-1/2 files beside version.rb, 43 sig, seven cops, 318-line manifest); roadmap note numbers match the checklist; probe clean; no register introduced.
  9. Commit hygiene — no attribution or co-author lines; clean tree at every tip; branches left where the fixer put them.

Run: workflow wf_67746022-13d, 8 agents (1 implementer, 4 reviewers, 3 fixers), 3.1M tokens, 5.4 h.

@Wahbeh-Mohammad
Wahbeh-Mohammad changed the base branch from 9-phase-2-seam-foundations-tests to main September 15, 2026 15:36
@Wahbeh-Mohammad
Wahbeh-Mohammad merged commit 6149cde into main Sep 15, 2026
5 checks passed
@Wahbeh-Mohammad
Wahbeh-Mohammad deleted the 9-phase-2-seam-foundations-docs branch September 15, 2026 15:37
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 2: Seam Foundations

1 participant