Repository navigation
Phase 9: cross-cutting invariants and conformance — the four suites, the aggregate report and three invariant gates - #99
Merged
Wahbeh-Mohammad merged 4 commits intoSep 24, 2026
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #33. First PR of phase 9's three-PR stack — the code — built off
mainat582e33a(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-conformancegains 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,Runnerand the four suites.Runnerdecides the five statuses once for all four (TransportSuiteis 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(appendixB.8,XCUT-1–24) andExecutorSuite(B.7's lifecycle half, andSEAM-25's harness) are split into per-file group modules markedprivate_constant, in 8a'sTransportSuiteshape;PackagingSuiteisB.9's eight portableNFRs;CodecSuiteisB.3's seam half.Aggregate— oneReportover every suite, aPREAMBLEprinting what a green run does not prove, andby_requirement_idreading each suite's.assertionsrather than aReport, so a suite nobody ran cannot silently shorten the coverage map.Levelsand the MUST-level vacuity blocker (NFR-17) — 645 requirement levels generated from appendix C bytools/requirement_levels.rb,Report#blocking_vacuities, andaccepted_vacuous:, without which an unbuilt MUST reads as a green run. Acceptance isall?over an assertion's MUST-level IDs and every acceptance names every one of them.SharedInstance—XCUT-11's structural predicate onR8's disjunction: a frozen object conforms onfrozen?alone, an unfrozen one only when every ivar is declared by the driver (P9-9), never read off the audited object.tools/ast_scan.rb—gates:cause_walk,gates:bounded_map,gates:seam_names— plusgates:serde_boundary's PENDING-empty assertion.DEFAULT_GATESgoes from eighteen to twenty-one, in the rootRakefilewhere the array is defined and frozen, so all three are intask default:and therefore blocking, with.github/workflows/ci.ymlupdated because phase 0'sci_workflow_test.rbasserts every entry appears in some job.APPENDIX_B.md— the 61-row coverage map,B.1/B.2/B.5dispositioned 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-coreis one path, statusA, undertest/— the first-partyInvariantSuitedriver — and the diff over every other gem'slib/andsig/, overdocs/deviations.mdand over the three frozen trees is empty.dexpace-serde-json's two suite files are byte-identical tomain: the plan's Task 10 said to lift two assertions out of 7a's suite, and lifting them would have deletedSEAM-21's only evidence,SERDE-10's, and the encode half ofSERDE-9's, soCodecSuite'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:
Reportkeeps 8a'swaived:rendering and gains a driver-declared would-fail marker, soR5'swaived (would fail): ASYNC-3prints without making the report lie aboutTRANSPORT-14/27and without reddening 8a's two pinned assertions;accepted_vacuous:goes onTransportSuite.runand each new suite's.runand on neither driver, because neither shipped driver builds aReportat all and the keyword there would be dead surfaceNFR-4then locks (P9-23); the three new gates'DEXPACE_GATE_ROOTroute 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 --checkgains an optional path so both exits are proven, the old test having passed a--checkthat always exited 0 (P9-31);tools/serde_boundary.rb's.pendinggains alist:keyword, becausePENDINGis 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 aClock#sleepignoring the token passed — andXCUT-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-18vacuity and the pool driver'sASYNC-3waiver, 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.