Skip to content

Phase 9: cross-cutting invariants and conformance — the four suites, the aggregate report and three invariant gates - #99

Merged
Wahbeh-Mohammad merged 4 commits into
mainfrom
9-phase-9-cross-cutting-invariants-and-conformance
Sep 24, 2026
Merged

Wahbeh-Mohammad merged 4 commits into
mainfrom
9-phase-9-cross-cutting-invariants-and-conformance

Conversation

@Wahbeh-Mohammad

Copy link
Copy Markdown
Contributor

Part of #33. First PR of phase 9's three-PR stack — the code — built off main at 582e33a (phases 0 through 8, all nine build phases). The roadmap's first audit phase: its deliverable is an answer, not an artifact, and it repairs nothing it audits.

What lands

dexpace-conformance gains the four suites appendix B owes this phase, the aggregate report, and three repository gates — 41 own requirement rows (XCUT-1–24, NFR-1–17: 36 ✅, 2 ✅-in-part, 2 N/A, 1 ⏳ owned elsewhere) plus 11 cross-reference rows.

  • Check, Runner and the four suites. Runner decides the five statuses once for all four (TransportSuite is deliberately left on 8a's own loop — P9-10: phase 8 owns that file, and a phase that refactors another phase's shipped code while claiming only to report is the boundary failing quietly). InvariantSuite (appendix B.8, XCUT-1–24) and ExecutorSuite (B.7's lifecycle half, and SEAM-25's harness) are split into per-file group modules marked private_constant, in 8a's TransportSuite shape; PackagingSuite is B.9's eight portable NFRs; CodecSuite is B.3's seam half.
  • Aggregate — one Report over every suite, a PREAMBLE printing what a green run does not prove, and by_requirement_id reading each suite's .assertions rather than a Report, so a suite nobody ran cannot silently shorten the coverage map.
  • Levels and the MUST-level vacuity blocker (NFR-17) — 645 requirement levels generated from appendix C by tools/requirement_levels.rb, Report#blocking_vacuities, and accepted_vacuous:, without which an unbuilt MUST reads as a green run. Acceptance is all? over an assertion's MUST-level IDs and every acceptance names every one of them.
  • SharedInstance — XCUT-11's structural predicate on R8's disjunction: a frozen object conforms on frozen? alone, an unfrozen one only when every ivar is declared by the driver (P9-9), never read off the audited object.
  • Three AST gates over tools/ast_scan.rb — gates:cause_walk, gates:bounded_map, gates:seam_names — plus gates:serde_boundary's PENDING-empty assertion. DEFAULT_GATES goes from eighteen to twenty-one, in the root Rakefile where the array is defined and frozen, so all three are in task default: and therefore blocking, with .github/workflows/ci.yml updated because phase 0's ci_workflow_test.rb asserts every entry appears in some job.
  • APPENDIX_B.md — the 61-row coverage map, B.1/B.2/B.5 dispositioned by reference with the limit of that claim stated in the map itself (P9-1, P9-7).

The boundary, and what it cost

R6/P9-6: phase 9 reports, phase 10 repairs. git diff --name-status main..docs -- gems/dexpace-core is one path, status A, under test/ — the first-party InvariantSuite driver — and the diff over every other gem's lib/ and sig/, over docs/deviations.md and over the three frozen trees is empty. dexpace-serde-json's two suite files are byte-identical to main: the plan's Task 10 said to lift two assertions out of 7a's suite, and lifting them would have deleted SEAM-21's only evidence, SERDE-10's, and the encode half of SERDE-9's, so CodecSuite's assertions are re-derived from the requirement text instead and 7a's gap is routed to phase 10 rather than repaired here (P9-22).

Decisions taken in the open, against the plan's text

Ledger rows P9-21–P9-33 in the design's As-built addendum and the checklist's deviations. Beyond the two above: Report keeps 8a's waived: rendering and gains a driver-declared would-fail marker, so R5's waived (would fail): ASYNC-3 prints without making the report lie about TRANSPORT-14/27 and without reddening 8a's two pinned assertions; accepted_vacuous: goes on TransportSuite.run and each new suite's .run and on neither driver, because neither shipped driver builds a Report at all and the keyword there would be dead surface NFR-4 then locks (P9-23); the three new gates' DEXPACE_GATE_ROOT route was broken on arrival — they opened every file relative to the process CWD and could never have run against a fixture — and is fixed with a fixture workspace and four tests (P9-27); tools/requirement_levels.rb --check gains an optional path so both exits are proven, the old test having passed a --check that always exited 0 (P9-31); tools/serde_boundary.rb's .pending gains a list: keyword, because PENDING is a frozen constant no gate root can change (P9-30).

Two assertions were found non-discriminating by the phase's own mutation pass and repaired (P9-28, P9-29): XCUT-3's now asserts the waiter was parked when the cancel was issued — without it a Clock#sleep ignoring the token passed — and XCUT-12's asserts every racer was parked before the coalesced fetch is released, without which a stamper fetching per caller passed.

Layering

Each tip is green under every gate on its own tree — all twenty-one. This branch is green on the SimpleCov floor too: 94.65% on 4.0.6, so the one tolerated red was not needed; the 3.2.11 matrix row is green at 90.95%. The tests PR takes the same tree to 99.82% — 4,206 runs / 74,618 assertions / 9 skips, the base's seven plus the core driver's accepted XCUT-18 vacuity and the pool driver's ASYNC-3 waiver, each named by ID.

Verification

Five independent reviews by five fresh agents with a fix round between each — round 0 0/9/3, round 1 0/3/2, round 2 0/3/0, round 3 0/2/1, round 4 (targeted) 0/2/0 — each re-running every gate itself at every tip on 4.0.6 and 3.2.11 and applying its own mutations. Sixty-plus mutations across the rounds; review 4 alone applied 42 with 35 caught and every survivor accounted for. The review that mattered most swept all 46 assertion bodies one at a time and found four that could be neutralised whole with the suite green — the repair of the one no document named is on the tests branch.

Phase 9's instrument. Four suites beside phase 8a's transport one, over
one shared Runner so the five statuses are decided in one place:
InvariantSuite (28 assertions over all twenty-four XCUT ids, ten
private_constant groups), PackagingSuite (8 over NFR-1, 2, 3, 10, 11,
13, 14 and 15, read from published gem metadata rather than a source
gemspec), CodecSuite (2 portable seam properties, re-derived from the
requirement text rather than lifted out of dexpace-serde-json, whose
files are untouched) and ExecutorSuite (7, SEAM-25's harness half among
them, with ASYNC-3 written so it genuinely fails).

Report gains .merge, #to_h and the MUST-level vacuity blocker: Levels
carries every requirement id's normative level, generated from appendix
C, and a vacuity on a MUST fails the run until an acceptance names that
id with a citation. SharedInstance is XCUT-11's structural predicate,
R8's disjunction, with the declaration supplied by the driver and never
read off the subject. Aggregate gives a whole run one verdict and a
preamble that prints what green does not prove; APPENDIX_B.md is the
61-row coverage map.

Three repository-wide invariant scans become blocking gates over one
RubyVM::AbstractSyntaxTree walker -- gates:cause_walk, gates:bounded_map
and gates:seam_names -- taking the set to twenty-one, all in
DEFAULT_GATES and all in ci.yml's gates job. gates:serde_boundary now
asserts its PENDING list empty, which is the clause SSE-37 handed
forward.
Review round 0, R0-1 and R0-2.

R0-1. Three waits in the phase-9 suites carried no bound, so a subject
that never answers parked the whole run instead of failing an assertion.
ExecutorSuite::Shutdown#check_release opened with an unbounded
`transport.entered.pop`: an executor that REFUSES the post -- ASYNC-2's
saturated queue, or a closed pool -- pushes nothing there and the poster
rescues the refusal, so the run hung and the ensure that frees the gate
and closes the pool was never reached. The entry pop now carries the
assertion's own bound and an expired one is :vacuous with its reason:
with no unit started there is no "blocking task on a worker thread",
which is the antecedent ASYNC-3's sentence opens with, and a MUST-level
vacuity blocks the report anyway.

The two `.each(&:join)` sites take the same treatment against a subject
whose submission or whose shared call never returns -- ExecutorSuite's
concurrent_post and InvariantSuite's drive_threads -- each as a Failure
naming the clause, with the whole thread set sharing one budget so
sixteen stuck threads cost one bound and not sixteen. That is 8a's rule
for this gem, which await_closed_connection's `timeout:` fixed: an
adapter that never releases fails the assertion instead of hanging.

R0-2. PackagingSuite's NFR-14 assertion documented "with no source
supplied it is :vacuous with that reason, never a pass, because 'nobody
told us' is not evidence of a single source" and did not implement it --
with no `versions:` every want was nil, every unit was skipped and the
check passed having proved nothing. It now raises the vacuity the
comment names, and the comment says what a PARTIAL source means too.
Found by writing review round 0's R0-4 test, which drives the shipped
DEFAULT_RESOLVE for the first time: every case in packaging_suite_test.rb
supplies its own `resolve:`, so the lambda a real driver gets had never
run.

RubyGems raises Gem::MissingSpecError for a name it cannot resolve, and
that descends from Gem::LoadError < LoadError < ScriptError -- it is not
a StandardError. So `rescue ::StandardError` let it past, and it escaped
Runner's own bare rescue too, aborting the whole run where the suite's
contract is one :vacuous result naming the absent unit. Measured on
4.0.6: a Suite.run naming an uninstalled gem errored out of the first
assertion instead of reporting eight vacuities.

The rescue now names Gem::LoadError beside StandardError, and the
comment says why the explicit class is load-bearing.
…oute

InvariantSuite::Models#only_closes_what_it_created makes two Checks
against one caller-supplied resource: it was not closed, and it still
works. `Borrowed#usable?` was `!@closed`, the exact negation of the
`closed?` the first Check already reads, so no subject the suite can
build separated the two and replacing the second Check's condition with
a literal `true` left the whole gem's suite green. The code comment and
the phase checklist both claimed the opposite -- that a component
tearing the resource down another way would pass the first alone.

`Borrowed` now carries that other way. `#finish` is the adapter-side
teardown reached without going through `#close` -- `Net::HTTP` spells it
exactly that, a pooled client spells it "retire" -- and `usable?` reads
both flags, so a holder that shuts a borrowed resource down without
calling `#close` passes the first Check and fails the second. The
comment is rewritten to say what the double now does and how the gap
was measured.
@Wahbeh-Mohammad Wahbeh-Mohammad added type:test Tests only spec:conformance Conformance-suite behavior, or a gap in the suite itself labels Sep 24, 2026
@Wahbeh-Mohammad
Wahbeh-Mohammad merged commit 35583e5 into main Sep 24, 2026
2 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

spec:conformance Conformance-suite behavior, or a gap in the suite itself type:test Tests only

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant