Skip to content

Phase 7a: serialization — documentation and phase record - #89

Merged
Wahbeh-Mohammad merged 10 commits into
mainfrom
26-phase-7a-serialization-docs
Sep 20, 2026
Merged

Wahbeh-Mohammad merged 10 commits into
mainfrom
26-phase-7a-serialization-docs

Conversation

@Wahbeh-Mohammad

Copy link
Copy Markdown
Contributor

Closes #26. Third PR of phase 7a's stack — the phase record and the documentation — on top of #88.

What lands

12 files on top of 7b's and 7c's main (the 7a record plus the reconcile pass's dated paragraphs). Phase 7 is complete with this PR — umbrella #25 closes by hand.

  • docs/work/mvp/phase7/phase7a/2026-09-10-phase7a-serialization-checklist.md — 30 own rows, all ✅ (SERDE-1–SERDE-30; nine carry a stated clause rather than a bare tick), plus 20 cross-reference rows; the roadmap's legend verbatim; sections: requirement rows, what was built, guards run red (38), audit groups run, deviations from the plan, findings routed, postponed work (phase 9's lift of serde_seam_assertions.rb).
  • The design's As-built addendum, ledger rows P7-61–P7-72 beside the design's own P7-1–P7-9, which stand; the design's "_Codec never written" finding withdrawn with the evidence (c881f92). The consolidation into design §10 is a human's — docs/sdk-design-ruby/ is frozen — and the addendum says so.
  • docs/sdk-documentation/serde.md (new; every example executed on 4.0.6 and 3.2.11 — the one block that needs require "stringio" is R2-1 below), architecture.md, gems/dexpace-serde-json/README.md (no longer a skeleton's), README.md, docs/README.md (whose "what the tree holds" sentence had omitted 6b's redirect layer on main — corrected while adding 7a's).
  • docs/knowledge/notes/serde.md — two new reference entries (a decoded JSON String can be UTF-8-tagged and invalid; JSON::Coder's option handling drifted between 2.19.9 and 3.0), keys cited in support, none overriding; ruby scripts/verify_knowledge_structure.rb OK.
  • .claude/skills/knowledge-lookup/SKILL.md — the serde audit row's query widened to rules,constraints,conclusions (--section rules alone misses SERDE-17, 24, 25 and 30).
  • docs/first-release.md — SERDE-27's no-materialization entry: the first owed half closed by serde.md on 2026-09-20; the phase-8 pull-parser check stays open; dexpace-serde-oj's post-v1 entry verified, not re-filed.
  • CLAUDE.md — the built-phases sentence reads … 6c, 7b, 7c and 7a are built — the whole of phase 6 and the whole of phase 7; the opening paragraph carries 7b's, 7c's then 7a's layer sentences; dexpace-serde-json leaves the "phase-0 skeleton" sentence; every count re-derived from the combined tree by the reconcile pass — 219 lib files beside version.rb, 219 sig/ mirrors, the same nineteen private_constant test-mirror exceptions (7a adds none), seventeen checklists, seventeen pages, eighteen gates; 7b's four, 7c's four then 7a's five "Constraints that will bite" lines.
  • The roadmap's phase-7a status note (append-only), one new phase-10 inbound bullet (gates:clean_bundle's BUNDLE_PATH, dated 2026-09-20) and the dated bracketed correction to the false _Codec bullet.

docs/product-spec/, docs/sdk-design-ruby/, docs/knowledge/harvested/, docs/deviations.md and every other phase's documents are untouched.

Reconciliation onto 7b's and 7c's main

This stack was reviewed on c53638b and rebased onto 5e2cb21 by a reconcile pass that resolved five prose collisions (CLAUDE.md, README.md, docs/README.md, architecture.md, the roadmap) by keeping every lane's content in merge order and re-deriving every count from the tree; two omissions in 7a's own docs were corrected from the tree while there (README.md's status sentence had not gained 7a; architecture.md's page list stopped at fourteen without serde.md); the roadmap gains a dated "Phase 7a reconciled onto main after 7b and 7c" paragraph and 7a's checklist a dated "Reconciled 2026-09-20" note (its own numbers describe its old base). One omission is recorded, not fixed: gems/dexpace-core/README.md names the SSE and pagination layers and not the serialization layer (7a's branch never touched that file) — a one-paragraph addition owed to the next docs pass. 8a (#30) is not on main yet: it converts the same two seam_surface_test.rb pins (one copy survives its reconcile), edits its own Steepfile targets beside this lane's :serde_json block, and its guarded generator-slice test un-guards once it is rebased onto this.

Verification

Docs tip (rebased): bundle exec rake (eighteen gates) green on 4.0.6; ruby .claude/skills/housekeeping/probe.rb exit 0 (all eight checks); ruby scripts/verify_knowledge_structure.rb OK; the housekeeping and knowledge tooling suites green.

Known follow-ups

R2-1, R2-2, R2-3 (nits, all docs) — see the code PR's list.

Phase 7a, Tasks 2-16 and 19's wiring. In dexpace-core, under
lib/dexpace/serde/: DecodeContext -- the `ctx` every witness receives, a
frozen Data carrying the RFC 6901 path and the decode's target name,
with the eight `!` checks and the ONE raise site that makes
SERDE-13/21/22 properties of one method; witness.rb reopening
Dexpace::Serde for WITNESS_METHOD, DUMP_METHOD, .witness? and .witness!
(a respond_to? on .dexpace_load, never on #call); Native and OMIT, the
encode walk that drops an Absent key, writes nil for one in an Array or
at the top level, and raises SerializationError naming the class where
::JSON.generate would stringify (P7-9); the private scalar table and the
named BOOLEAN witness; Tristate with ABSENT, NULL, Present (validating
in #initialize, .new and .[] private, Model#with keeping the fourth
state closed on 3.2) and the private Combinator behind .of with its two
entry points; List, Map and Nullable, frozen Datas by value from a
concrete element witness, failing at construction (SERDE-8); Instant,
the ISO-8601 witness with P7-8's microsecond domain; DecodingHandler
(SERDE-27: the body's own #source, one ensure-close, BufferedSource#eof?
for an empty body) and StatusAwareHandler (SERDE-28: 2xx delegates,
4xx/5xx raises the factory's error over Recovery.buffer_error_body's
copy with no second close, anything else closes and raises leading with
the code and the raw ETag/Location). Body.serialized is the ninth
factory (SERDE-2). Phase 2's interface _Codec is edited in place --
#media_type a MediaType or a String, #load over the new _Witness,
#dump_to an Integer -- never written twice.

In dexpace-serde-json: the gemspec's json >= 2.19.9 line, the only place
that floor is stated and the first NFR-2 third-party half spent; the
entry file asserting MINIMUM_JSON_VERSION at require time (P7-7),
registering under :json with REQUIRED_CORE, and the .default / .build
factories; and Codec, the six seam methods over one private
::JSON::Coder per instance (P7-4) built with keywords only, strict: true
and allow_duplicate_key: false fixed, encoders: never forwarded, options
as one positional Hash over a five-key allowlist, #dump_into's explicit
fit check raising IndexError cause: nil, #load draining through
#read_utf8 under the materialisation ceiling (P7-1), validating UTF-8
(P7-6) and rescuing ::JSON::JSONError around the parse alone (SERDE-12
structurally).

Every new file has a sig/ mirror; the Steepfile's :serde_json target
alone downgrades Ruby::UnknownConstant to :information for the one
JSON::Coder rbs 4.2.0 does not declare (json 3.0.2 ships no sig/). The
surface manifests are regenerated once, 86 rows. Five core pins the
require-time registration invalidates change here:
seam_surface_test.rb's two seam pins and serde_test.rb's "starts empty"
pin assert in a child process that requires dexpace alone, and its two
swap pins assert the override is gone. The adapter's smoke test
snapshots after `require "dexpace"` and pins the gem's four constants.
Review round 0, R0-1. The feat commit rewrote three lines of the
file's header comment to correct its stale "json arrives with the
codec in phase 7" sentence. The file is a shared one this phase's brief
lists out of bounds -- phase 8a adds the first row to it -- and no gate
reads the comment, so the correction rode a scope breach for nothing.
Restore the file to main's content; the stale sentence is routed by
date and content to phase 10's inbound list on the docs branch, for
whichever lane adds the first row to close. The Steepfile's :serde_json
relaxation, which is what settles the JSON::Coder reference, is
unchanged.
Review round 1, R1-4. DecodeContext.root gives an anonymous class no
target (P7-70), so that `#error!` keeps its plain form and no
`#<Class:0x...>` reaches a message -- but DecodingHandler#missing_body
and StatusAwareHandler#unhandled_message interpolated that nil
directly, and a 204 through a `Class.new` witness read "no body to
decode into : the response carried none". Each handler now derives the
name through one private #target_name that falls back to the literal
"an anonymous witness", declared in the two sig/ mirrors. A named
witness's messages are byte-for-byte what they were; P7-70's rule on
the context is unchanged.
Phase 7a, Tasks 1-18's tests. Thirteen core suites under
test/dexpace/serde/ -- one mirror per lib file plus
tristate_decode_test.rb and the SEAM-2 scan no_concrete_codec_test.rb
(Ripper-tokenised, comments dropped) -- and http/body_serialized_test.rb
beside 3b's body suite: SERDE-21's nine coercions as nine fixtures,
SERDE-22's two permissions, SERDE-13's target naming at the root frame,
SERDE-14 closed on construction, .[] and #with, SERDE-15/16/17/20's
three states both ways, SERDE-24's domain and P7-8's truncation,
SERDE-27's five-case matrix over 3b's FakeResponseBody (raw close
counts) and a real Response, and SERDE-28's three branches with the
non-canonical 599 in the second.

In dexpace-serde-json: codec_test.rb (the four encode profiles,
SERDE-4's four-part buffer matrix with cause: nil asserted from inside a
rescue, P7-5, the failure model, SERDE-25/26/29, the option allowlist
against json 2.19.9 and 3.0), codec_load_test.rb (SERDE-3's zero close
count under every option, SERDE-5/12/13/20/21/22/23, P7-6, the
over-ceiling StreamError), defaults_test.rb (SERDE-1/19/24 through the
real codec, two seeded property samples), seam_conformance_test.rb over
the phase-9 lift target test/support/serde_seam_assertions.rb, and
composition_test.rb walking Operation -> Pipeline.standard ->
TypedResponse over a recording lambda transport. Two new close-counting
doubles. Thirty-four mutations run red on 4.0.6 and 3.2.11 (two
equivalent, one floor-only), recorded in the checklist.
Review round 0, R0-2. Dropping the explicit `allow_duplicate_key:
false` default from Codec#initialize survived the whole suite on the
bundle's json 3.0.2, where a duplicate key is a ParserError by default,
and turned into a warning-plus-last-wins only at the 2.19.9 floor, which
no gate row runs. The option is now pinned where it lives rather than
through the engine's behaviour: a fourth nested class in codec_test.rb
runs a child process (context_store_config_test.rb's shape -- the
recorder is a permanent prepend on a library class's singleton, so it
never enters the suite's own process) that captures every keyword
::JSON::Coder.new receives across four constructions and asserts
`allow_duplicate_key: false` and `strict: true` on the default, the
caller's opt-in on the second, `max_nesting` forwarded and `encoders:`
withheld on the third, and the two booleans on the fourth. The child is
handed the core the parent loaded explicitly, so the case reads the same
tree under `bundle exec` and under a bare `ruby -I` run at the floor.

Measured: the mutant is red on 4.0.6 with json 3.0.2 and on 3.4.10 with
json 2.19.9 pinned unbundled; guard 19 (`strict: true` dropped), an
equivalent mutant until now, is red at the keyword level too; forwarding
`encoders:` is red on both.
Review round 1, R1-1, R1-2 and R1-4's proof.

R1-1: the empty-body case in decoding_handler_test.rb asserted only
that the message names PetWitness, which the witness's OWN shape
failure over the drained "" also does, so reducing `raise missing_body
if source.eof?` to a bare probe left every suite green. It now asserts
/no body/ beside the target, and composition_test.rb drives an empty
200 through Pipeline.standard and the real JSON codec, asserting the
handler's target-naming error and never the parser's "malformed JSON:
unexpected end of input". Both are red under that mutant.

R1-2: P7-7's require-time floor assertion was exercised by no test --
every gate row runs under the bundle's json, above the floor. The new
json/floor_test.rb runs a child process with RUBYOPT, RUBYLIB and the
BUNDLE_*/BUNDLER_* keys cleared (GEM_HOME and GEM_PATH kept, so the
bundle's json stays findable), pins a json by exact version with `gem`
and requires the entry file: the interpreter's default json (2.6.3,
2.7.2, 2.9.1, 2.18.0 across the matrix) is refused with the SeamError
naming the floor and the active version, and the json this process
runs loads and registers under :json. Deleting the block turns the
first case red on every row -- json 2.18.0 loads and registers, the
silently-unpatched case the deviation exists for.

R1-4: one case per handler drives a `Class.new` witness and asserts the
message reads "an anonymous witness", never an empty name and never
`#<Class`.
Phase 7a, Task 19's records. The checklist, written from the build:
thirty SERDE rows, all implemented, nine stating a clause and nine
touched by a deviation; twenty cross-reference rows; the matrix
facts re-run on all four interpreters against json 3.0.2 and 2.19.9;
thirty-four guards and four gate mutations; thirty-one departures from
the plan; findings routed. The design gains an As-built addendum,
P7-61-P7-72, withdrawing the "_Codec never written" finding with the
evidence. docs/sdk-documentation/serde.md is the fifteenth page, every
example run on 4.0.6 and 3.2.11; architecture.md, both READMEs, the gem
README and docs/README.md point at it. CLAUDE.md gains the built-phase
sentence, the layer paragraph, 184 -> 195 lib files, fifteen checklists,
the adapter's lib/ sentence and five constraints.
docs/first-release.md's 7a P7-1 entry records its first owed half
closed. docs/knowledge/notes/serde.md is new with two entries. The
roadmap gains the 2026-09-20 status note, one phase-10 inbound bullet
(gates:clean_bundle's install location) and a bracketed correction on
the _Codec residue bullet; the knowledge-lookup skill's thirteenth audit
row gains constraints,conclusions.
Review round 0 returned changes_requested with two should-fix findings
and three nits. The records catch up with the two fixes below this
commit and take the three nits:

- R0-1: rbs_collection.yaml is as main has it again; the checklist's
  What was built and deviation 8 no longer say its comment was
  corrected, and the stale "json arrives with the codec in phase 7"
  sentence is one new phase-10 inbound bullet, by date and content, for
  whichever lane adds the first row to close (Findings routed).
- R0-2: the keyword pin on the codec's JSON::Coder construction is guard
  34 (red on 4.0.6 with json 3.0.2 and on 3.4.10 with json 2.19.9
  pinned unbundled), with 34.5 for encoders: forwarded; guard 19, an
  equivalent mutant behaviourally, is red at the keyword level through
  the same pin. The battery is thirty-five, thirty-three caught on both
  rows, guard 3 on 3.2.11 alone, guard 21 the one equivalent mutant.
  The SERDE-9 row cites the pin; CLAUDE.md's constraint line says why
  the option is pinned at the keyword level and not through the
  engine's behaviour.
- R0-3: serde.md's last example defines the five responses it reads,
  over Body.buffer from one helper; every ruby block of the page runs as
  written on 4.0.6 and 3.2.11 with no fixture pre-defined (12 blocks,
  69 checks).
- R0-4: CLAUDE.md now says which serde pin runs in a child process (the
  "starts empty" one) and that the two swap pins assert the override is
  gone.
- R0-5: the roadmap's status note names the Steep relaxation as the
  second route of the manager's decision (1), as P7-62 and the checklist
  do.

The design's As-built addendum carries a round-1 paragraph; no ledger
row is added, because the round found no behaviour the document states
that the code fails to honour. The roadmap gains a round-1 status
paragraph. Probe clean.
Review round 1 returned changes_requested with two should-fix findings
and two nits. The checklist's guard table gains the round's three rows
(23.5: the eof? raise reduced to a bare probe; 35: the P7-7 floor block
deleted; 36: the anonymous-witness fallback dropped), corrects guard
23's row -- its third failure was the eof?-probe case, not the
empty-body case, which the round found green under that guard -- and
restates the battery at thirty-eight; the SERDE-27 and SERDE-28 rows
cite the new assertions and the floor test joins What was built. The
design's As-built addendum carries a round-2 paragraph and P7-70's row
is amended in place for the two handler messages. The roadmap's status
line carries the docs tip's current run count (R1-3) and a round-2
paragraph; CLAUDE.md's two serde constraint lines say why the
empty-body message is pinned on both halves and where P7-7's raise is
observable; serde.md names the stock 4.0 json and the anonymous
witness. No frozen tree, no earlier phase's document and no other
lane's file is touched.
Phase 7a was built off c53638b concurrently with 7b and 7c, whose stacks
landed first. The three 7a branches were rebased onto 7c's reconciled
docs tip with every 7a commit preserved; the files both sides changed
were reconciled inside the rebased 7a commits, with every count
re-derived from the combined tree. This commit carries the two records
no 7a commit could: the roadmap's dated reconciliation paragraph, and a
dated "Reconciled" note at the head of 7a's checklist saying which of
its count sentences describe its own base and what the combined tree's
figures are. Phase 7 is complete with this stack.
@Wahbeh-Mohammad Wahbeh-Mohammad added type:feature New capability or enhancement area:core Core HTTP, IO, body, context, encoding: HTTP-* IO-* BODY-* CTX-* UTF-* labels Sep 20, 2026
@Wahbeh-Mohammad

Copy link
Copy Markdown
Contributor Author

Review record for the phase 7a stack (#87 → #88 → #89)

3 independent reviews, each by a fresh agent with no memory of the previous one, each re-running every gate itself on every tip (4.0.6 every gate individually at the code tip and the full rake at the tests and docs tips; the matrix set on 3.2.11, 3.3.12 and 3.4.10) and applying its own mutations on both interpreters, with a fix round by a fresh agent between each. The stack was cut from main at c53638b.

Round Verdict Blocking Should-fix Nits Mutations (caught) Disposition
0 changes required 0 2 3 49 (45) all 5 addressed in fix round 1
1 changes required 0 2 2 59 (55) all 4 addressed in fix round 2
2 (final) approve 0 0 3 51 (49) carried into the PR bodies

Nothing was skipped.

Round 0 → fixed in round 1

  • R0-1 should-fix — rbs_collection.yaml:1: rbs_collection.yaml edited on the code branch — an out-of-bounds shared file, rewritten rather than appended. Fixed on code (d66e545): rbs_collection.yaml restored to main's bytes (git diff main -- rbs_collection.yaml is empty) in a fix: commit; nothing else in the code layer changed. Docs half on bcda522: the checklist's What was built and deviation 8 no longer claim the comment was correcte
  • R0-2 should-fix — gems/dexpace-serde-json/test/dexpace/serde/json/codec_test.rb:262: The explicit allow_duplicate_key: false default is unpinned: dropping it survives the whole suite on the bundle's json 3.0.2 (mutation X7). Fixed on tests (390d778): gems/dexpace-serde-json/test/dexpace/serde/json/codec_test.rb gains a fourth nested class CoderKeywordsTest: a child process (IO.popen([RbConfig.ruby, '-w', '-I', CORE_LIB, '-Ilib', '-e', PROBE], err: %i[child out], chdir: GEM_ROOT) -- context_store_config_tes
  • R0-3 nit — docs/sdk-documentation/serde.md:371: The response-handler example block uses five fixtures the page never defines. Fixed on docs (bcda522): docs/sdk-documentation/serde.md's handler block now opens with a response_over(status, text, headers:) helper over Dexpace::Response.build / Body.buffer and defines ok_200, not_found_404, not_modified_304 (etag "v1"), empty_200 and empty_list_200, with one lea
  • R0-4 nit — CLAUDE.md:898: CLAUDE.md says "the three serde pins are asserted in a CHILD process"; only one is. Fixed on docs (bcda522): CLAUDE.md's registration constraint line now reads: the two seam-iterating pins and serde_test.rb's "starts empty" pin are asserted in a CHILD process ... and serde_test.rb's two swap pins assert the override is GONE.
  • R0-5 nit — docs/work/mvp/2026-09-05-ruby-sdk-v1-roadmap-design.md:3970: The roadmap status note calls the Steepfile relaxation "the manager's route (1)"; it is route two of decision (1). Fixed on docs (bcda522): The roadmap's 2026-09-20 status note now says "so the second route of the manager's decision (1)".

Round 1 → fixed in round 2

  • R1-1 should-fix — gems/dexpace-core/test/dexpace/serde/decoding_handler_test.rb:81: The empty-body screen (P7-67, SERDE-27's zero-byte case) is unpinned: the test passes whether the handler raises or the witness does. Fixed on tests (04aad28): decoding_handler_test.rb's empty-body case adds assert_match(/no body/, error.message) (its comment says why /PetWitness/ alone passed with the screen gone); composition_test.rb ReadTest gains 'an empty 200 raises the handler's target-naming error, never the p
  • R1-2 should-fix — gems/dexpace-serde-json/test/dexpace/serde/json_test.rb:71: P7-7's require-time floor assertion is exercised by no test: deleting it leaves every suite green on every gate row. Fixed on tests (04aad28): New gems/dexpace-serde-json/test/dexpace/serde/json/floor_test.rb (DexpaceSerdeJSONFloorTest, 2 cases): IO.popen(UNBUNDLED, [RbConfig.ruby, '-w', '-I', CORE_LIB, '-Ilib', '-e', PROBE, version], err: %i[child out], chdir: GEM_ROOT) where UNBUNDLED clears RUBYOP
  • R1-3 nit — docs/work/mvp/2026-09-05-ruby-sdk-v1-roadmap-design.md:4028: The 2026-09-20 status note's gate line still says 2 938 runs / 68 933 assertions at the docs tip; the tree is 2 939 / 68 935 since the round-1 test. Fixed on docs (408698e): Roadmap :4028 now reads 'test:gems 2 944 runs / 68 966 assertions / 0 skips at 99.96% line coverage (the counts after review round 2's five added cases)', matching the full rake at the docs tip on 4.0.6 (and 3.3.12/3.4.10; 3.2.11 reports 68 968 assertions as
  • R1-4 nit — gems/dexpace-core/lib/dexpace/serde/decoding_handler.rb:119: For an anonymous witness class the two handler messages interpolate a nil target as an empty name. Fixed on code (ae15acb): gems/dexpace-core/lib/dexpace/serde/decoding_handler.rb and status_aware_handler.rb each gain a private #target_name = DecodeContext.root(target: witness).target || "an anonymous witness", used by #missing_body and #unhandled_message; declared in both .rbs mir

Round 2 (final) — approve

  • R2-1 nit — docs/sdk-documentation/serde.md:329: Block 10 uses StringIO.new without a require "stringio" anywhere on the page, so it raises NameError in a fresh process. The page's intro defines the preamble as S, T, CODEC, ctx and src only. bundle exec ruby -Igems/dexpace-core/lib -Igems/dexpace-serde-json/lib -e 'require "dexpace"; require "dexpace/serde/json"; p defined?(StringIO)' prints nil on 4.0.6 (json 3.0.2), and my page runner over all twelve ```ruby blocks aborts in block 10 with NameError: uninitialized constant StringIO on 4.0.6 and 3.2.11; with `
  • R2-2 nit — CLAUDE.md:885: "a rescue StandardError anywhere on that path is a guard the suite runs red" overstates what the suite pins: the parse-rescue widening (guard 21) stays green. My mutation 21 (rescue ::JSON::JSONError -> rescue ::StandardError around @coder.load(text) alone) leaves codec_load_test (19 runs) and seam_conformance_test (3) green on 4.0.6, exactly as the checklist's own guard-21 row records ('STAYED GREEN on both rows, and is equivalent'); only a rescue around the drain (21.5) goes red (4 + 2 failures). The CLAUDE.md sentence has read this way since ro
  • R2-3 nit — docs/work/mvp/2026-09-05-ruby-sdk-v1-roadmap-design.md:3987: The 2026-09-20 status note says "four new suites beside it" while listing five, and the tree now has six (floor_test.rb). Roadmap :3987-3991 names codec_test.rb, codec_load_test.rb, defaults_test.rb, seam_conformance_test.rb and composition_test.rb after the words 'four new suites'; find gems/dexpace-serde-json/test -name '*_test.rb' at the tests tip lists those five plus json/floor_test.rb beside json_test.rb. The checklist's What was built already says 'six new suites beside it … since review round 1, json/floor_

What the final reviewer verified by experiment, both interpreters

  • Preconditions and set-up — main c53638b unmoved with redirect/step.rb and auth/step.rb present; tips ae15acb / 04aad28 / 408698e as the fix report says; no 7a branch checked out in any of the 79 worktrees at the start; bundle install --local res
  • H. Prior findings R1-1..R1-4 re-verified individually by mutation and by hand — All four fixed (see prior_findings): 23c red in two suites on both rows, N1 red on both rows, 36 red on both rows, the roadmap count matches my docs-tip run.
  • P7-7 unbundled: each interpreter's stock json pinned with gem "json", "= v" before the require; and unpinned — Each pinned stock json -> Dexpace::SeamError: dexpace-serde-json requires json >= 2.19.9; json <v> is active. Add gem "json", ">= 2.19.9" …; unpinned every row loads the installed 3.0.2 with registered_keys [:json] (3.
  • gates:clean_bundle on 3.3.12 and 3.4.10 (inside the matrix subset) — PASS on both with no network fetch this run: gem list json before and after reads json (3.0.2, default: 2.7.2) / (3.0.2, default: 2.9.1).
  • Duplicate-key input {"a":1,"a":2} through Codec#load — DeserializationError 'malformed JSON: duplicate key "a" at line 1 column 1' on all three with no warning; allow_duplicate_key: true opts in to last-wins ({"a"=>2}). Shipped codec_test (30 runs / 143 assertions) and floor
  • SERDE-14 fourth state on every construction path, and a plain Data for contrast — T.present(1).with(value: nil) -> InvalidArgumentError 'value is required'; Present[value: nil] -> NoMethodError (private); send(:new / :[], value: nil) -> InvalidArgumentError on all three; a plain validating Data's #wit
  • The five registry pins in ONE process with the adapter's smoke test loaded (suite_runner's -r shape), converted versus main's originals swapped in — Converted: 30 runs, 151 assertions, 0 failures. Main's serde_test.rb and seam_surface_test.rb swapped in: exactly five failures — serde_test.rb:42, :66, :77 and seam_surface_test.rb:17, :22 — the reshape is load-bearing
  • Standalone suites: json_test, floor_test (bundle exec and bare), composition_test, codec_test; floor_test x10 and codec_test x5 under timeout 120 — 3.2.11: json_test 9/36, floor_test 2/11 (bundle exec and bare), composition 7/39 green; 4.0.6: floor_test 2/11 ten of ten, codec_test 30/138 five of five — no flake.
  • steep and rbs:validate on both rows; the relaxation's scope — 4.0.6: both clean at all three tips. 3.2.11: rbs:validate clean; steep 12 problems / 10 files, all the rbs-4.1 Hash#to_h block-typing class, 11 in files 7a never touched plus map.rb:51 of the identical class; zero Unknow
  • Composition through Pipeline.standard over a lambda transport — 7 runs, 39 assertions, 0 failures: 200 -> Pet, 404 -> ProtocolError whose body_string reads twice, a/b -> a%2Fb, PATCH body drains to {"name":"Ré"} (ABSENT omitted), unfollowed 304 -> 304-led DeserializationError, empty
  • Time#iso8601(6) truncation and the microsecond round trip — Time.new(2026,9,10,12,0,0.123456,"+02:00").iso8601(6) -> 2026-09-10T12:00:00.123455+02:00 on all three; Time.at(1_757_505_600, 123_456, :usec).utc round-trips through Instant (2025-09-10T12:00:00.123456Z). Time.iso8601's
  • surface:regenerate at the tests tip; manifest diff versus main — Regenerate rewrote the six manifests (mtimes 16:24:55-57) byte-identically (git status clean); versus main +73 rows in dexpace-core.txt (1137 -> 1210) and +13 in dexpace-serde-json.txt (2 -> 15), 0 removed lines, every
  • Housekeeping probe, registers/citations, knowledge structure and the note's key markers at the docs tip — probe exit 0 'no drift found.'; --only registers,citations 'no drift found.'; verify_knowledge_structure OK (2166 harvested, 58 notes); serde/b5e5efc8, d3bef411, b36d4403 and d15ade64 print [cited by notes/serde.md], not
  • Every ```ruby example of docs/sdk-documentation/serde.md and of the gem README, run by my own page runner (preamble: S, T, CODEC, ctx, src; # => compared by inspect, `# => Klass: message` as a raise — serde.md: 12 blocks; WITHOUT a stringio require the program aborts in block 10 at `StringIO.new` (NameError: uninitialized constant StringIO) on both interpreters — `defined?(StringIO)` is nil after requiring the two gem
  • lib/dexpace.rb require order: three swaps and the whole block removed — decode_context<->witness, scalars<->tristate and status_aware_handler moved to the top each still load (every serde file require_relatives what it names — the internal order is defensive); removing the whole '# Phase 7a:
  • Counts re-derived at the docs tip — 195 lib files beside version.rb under lib/dexpace/ (184 + 11), 196 sig files under sig/dexpace/, 15 checklists under docs/work/, 16 files under docs/sdk-documentation (15 pages beside architecture.md), 23 notes .md (22 +
  • Mechanical global-constraint scan of the lib and test diffs (added lines) — 35 new .rb files all open with the two headers; plain requires under lib: core time (allowlisted), adapter json/date/time/dexpace (json declared by the gemspec); no sleep/delay/Thread.new/Timeout/Thread#raise in lib; n
  • Anonymous-witness message probe at the code tip (R1-4) — 204 -> 'no body to decode into an anonymous witness: the response carried none (SERDE-27)'; 304 -> '304 Not Modified: not decoded into an anonymous witness, only a 2xx body is (SERDE-28); etag: "v1"'; named witness uncha
  • Final state — Worktree detached at 408698e, git status clean, no 7a branch checked out in any worktree, nothing stashed, main and the three tips unchanged (c53638b / ae15acb / 04aad28 / 408698e); Gemfile.lock (gitignored) left resol

Tips reviewed at the final round: code ae15acb, tests 04aad28, docs 408698e on main c53638b.

Reconciliation after review

The three reviews above ran on the stack as built off c53638b. Phases 7b (#81–#83) and 7c (#84–#86) merged first, so the stack was rebased onto 7c's reconciled tip by a reconcile pass (wf_d87ccc11-e60) and re-parented onto main 5e2cb21 — whose tree is byte-identical to that tip — by the manager: one code conflict (lib/dexpace.rb's require blocks — 7b, 7c, then 7a), the core manifest auto-merged to exactly the regenerated 1,330 rows, five prose collisions resolved by keeping every lane's content in merge order with every count re-derived from the tree, two omissions in 7a's own docs corrected from the tree (README.md's status sentence, architecture.md's page list), and the feat commit's message restored verbatim after rebase --continue's comment cleanup had dropped three body lines. Re-proven on the rebased tips by the pass and again by the manager: all eighteen gates on 4.0.6 at every tip, honest RuboCop clean (597 files), probe clean, the matrix set on 3.2.11 / 3.3.12 / 3.4.10 at the tests tip (3,380 runs / 71,339 assertions / 0 skips / 99.96 %) and on 3.2.11 at the code tip (97.82 %). Rebased tips: code d4f7315, tests 695ffef, docs 5b0c019. Phase 7 is complete with this stack.

@Wahbeh-Mohammad
Wahbeh-Mohammad changed the base branch from 26-phase-7a-serialization-tests to main September 20, 2026 15:44
@Wahbeh-Mohammad
Wahbeh-Mohammad merged commit 734e6b3 into main Sep 20, 2026
5 checks passed
@Wahbeh-Mohammad
Wahbeh-Mohammad deleted the 26-phase-7a-serialization-docs branch September 20, 2026 15:45
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:feature New capability or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 7a: Serialization

1 participant