feat(tot+totlib): mono detection ABI + Layer-C smoke (#201 Phase 2b)#207
Merged
Conversation
Brainstorm-output spec for the next #201 PR. Captures the user-confirmed design choices (2026-05-18): - Mono detection: new tot_is_mono() C ABI export in tot_api.f90 (Option A from spec §3). - Two-file flag pattern: tot_mono_flag.f90 (default = .FALSE.) + tot_mono_flag_mono.f90 (mono = .TRUE.) — Makefile picks the right one per build. - Pipeline conditional rule registration: instance-level _active_rules in TotPipeline.__init__, overlaying _MONO_ONLY_RULES (("eq","tr") verify rule) when _detect_mono() is True. - Layer-C scope: direct ctypes calls on the mono .so handle (test_mono_flag, test_bpsd_round_trip, test_distinguishes_per_module). Full TotPipeline mono routing via Eqlib/Trlib/Fplib/Tilib/Wrxlib is OUT-OF-SCOPE and deferred to a future Phase 2c spec. Codex retrospective findings 2026-05-15 incorporated: - Spec amendment placement: original L-7b-ii spec §0 already corrected in PR #206; Phase 2b leaves it as-is. - 2-PR split: Phase 2a (build-infra, shipped in PR #206) vs Phase 2b (this spec's deliverable, behavioral activation). - Stale BPSD state isolation: --forked enforced via mono-build CI step. - Conditional rule registration: instance-level _active_rules pattern guarantees rule does NOT activate on default per-module .so. Acceptance criteria (§9): 10 explicit items including 1e-10 equivalence preserved, ABI present in both builds, smoke tests pass, pipeline gating correct, CLAUDE.md compliance verified. Open questions resolved by user (§11): #1 keep Phase 2b scope tight (wrapper plumbing deferred to Phase 2c), #3 module name tot_mono_flag. Open question #2 (eq-push C ABI sequence) flagged for verification during implementation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2a (PR #206) made libtotapi_mono.so dlopen-loadable and 1e-10 equivalent to libtotapi.so. Phase 2b adds: 1. **Mono detection ABI** (`tot_is_mono` C function) — distinguishes the two builds at runtime. 2. **Layer-C smoke test** — proves the mono image actually carries shared BPSD broker state by driving eq_run(1) -> eq_bpsd_put on the mono .so and pulling via tr_check_bpsd_pull on the SAME image (ok=1). The default per-module image returns ok=0 (verified by test_distinguishes_per_module). 3. **TotPipeline rule-overlay infrastructure** — `_detect_mono` / `_active_rules` / `_MONO_ONLY_RULES`. **The infrastructure is in place but `_MONO_ONLY_RULES` is intentionally EMPTY in Phase 2b.** Why _MONO_ONLY_RULES is empty (Codex pre-push review caught the trap): populating ("eq","tr") here would cause `run_pipeline` to fire the verify rule against the TotPipeline's Trlib instance, which still loads its OWN per-module libtrapi.so (NOT the mono image). Result: eq pushes to libeqapi.so's PRIVATE bpsd, tr reads from libtrapi.so's PRIVATE bpsd, verify deterministically fails → user regression. Phase 2c will plumb the wrappers to route through the mono .so, at which point ("eq","tr") activation becomes meaningful. Phase 2b ships the SCAFFOLDING; Phase 2c will populate the rule. Six changes: 1. tot/tot_is_mono.f90 (NEW, default returns 0) + tot/tot_is_mono_mono.f90 (NEW, mono returns 1). Standalone BIND(C) functions (NOT inside MODULE) so swapping at link time does NOT force tot_api.o recompile. The Makefile picks one per build target. 2. tot/tot_api.h declares `int tot_is_mono(void)` with usage docs. tot/tot_api.f90 carries a doc-only pointer to the standalone files. 3. tot/Makefile OBJS_PIC adds tot_is_mono.o (default build), OBJS_PIC_MONO substitutes tot_is_mono_mono.o (mono build). 4. python/totlib/_ffi.py attaches tot_is_mono prototype with try/except AttributeError for legacy build compatibility. 5. python/totlib/pipeline.py adds `_MONO_ONLY_RULES` (empty), `_detect_mono()` (with warnings.warn on failure paths for debuggability — Codex retrospective 2026-05-18 LOW finding), `_build_active_rules()`, `_get_active_rules()`. `run_pipeline` uses `_get_active_rules()` instead of `COUPLING_RULES` directly. 6. python/totlib/tests/test_mono_bpsd_smoke.py (NEW, Layer-C): - C-1 (test_mono_flag): mono reports tot_is_mono() == 1. - C-2 (test_bpsd_round_trip): drives eq_init + eq_set_param + eq_run(1) on the mono image; eq_run(1) hits eq_load -> eq_bpsd_put; then tr_check_bpsd_pull on the SAME image returns ok=1. Uses PR #199's committed eqdata-HT6M fixture in a chdir'd tmpdir. - C-3 (test_distinguishes_per_module): default libtotapi.so reports tot_is_mono() == 0. CI mono-build job extended with: - "Build default libtotapi.so" step (needed for C-3) - "Layer-C BPSD broker smoke (mono image vs default)" step (--forked + MONO_LIB_PATH + TOTLIB_PATH) Local verification (macOS arm64, gfortran 15.x): - Default tot_is_mono() = 0 ✓ - Mono tot_is_mono() = 1 ✓ - 1e-10 equivalence preserved against BOTH builds ✓ - Layer-C 3/3 pass ✓ - Existing pipeline tests (test_pipeline*.py): 50/50 pass ✓ - libtotapi_mono_inspect: both HARD GATES (8-bpsd + no-.so-deps) ✓ - TotPipeline default: active_rules = {('fp','tr')} - TotPipeline mono: active_rules = {('fp','tr')} (empty _MONO_ONLY_RULES per Codex pre-push review) Phase 2c follow-up scope (separate issue/PR): - Route Eqlib/Trlib/Fplib/Tilib/Wrxlib through a single mono .so (e.g. MONO_LIB_PATH env var honored by each _ffi.py). - Populate _MONO_ONLY_RULES with the ("eq","tr") verify rule. Spec: docs/superpowers/specs/2026-05-18-l7b-ii-phase-2b-design.md (committed earlier on this branch + amended in this commit to reflect the shipped implementation and the empty-rule-dict decision) Reviewer drivers: Codex retrospective 2026-05-15 items 1, 3, 5, 6 + Codex pre-push review 2026-05-18 (caught the wrapper-plumbing trap and forced the empty _MONO_ONLY_RULES decision). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Owner
Author
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 58d4448. Configure here.
k-yoshimi
added a commit
that referenced
this pull request
May 25, 2026
Design for the next step in the Phase 2 chain. PR-A introduces a single authoritative mono-routing rule (MONO_LIB_PATH env var + shared python/_runtime_mode.py helper) so every wrapper (eqlib/trlib/fplib/tilib/wrxlib/totlib) inside or outside TotPipeline loads the same libtotapi_mono.so when activated. Codex's "single authoritative loader semantics" constraint (2026-05-18 retrospective) is the load-bearing requirement. Spec structure: 12 sections covering context, scope (PR-A only — NO rule activation, that's PR-B), 7 design decisions D-1..D-7 each with chosen path + rejected alternatives + rationale, helper API contract (lru_cache + strict verify + OSError wrap), per-wrapper patch shape, 9 test cases with explicit coverage matrix, CI integration leaning on the existing PR #207 default-build step, risks with mitigations, and acceptance criteria. Reviewed three rounds by Codex (codex:codex-rescue): - Pass 1: 3 MED + 3 LOW — all addressed - Pass 2: 2 LOW (test fixture mismatch + acceptance count stale) — both addressed (test split into hasattr-guard path and OSError-wrap path; §11 count updated to 9) - Pass 3: HIGH/MED/LOW = 0 — SHIP IT Plus an architecture sub-review for the helper module placement — Codex independently confirmed Option A (python/_runtime_mode.py) over totlib-owned (layering inversion) and subpackage (YAGNI). Successor: PR-A implementation (this branch on dev next session). Out of scope: PR-B rule activation + TotPipeline integration test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Phase 2a (PR #206 merged 2026-05-18) made
libtotapi_mono.sodlopen-loadable and 1e-10 equivalent. Phase 2b adds the behavioral-activation scaffolding:tot_is_mono()C ABI — runtime distinguishing between the two builds (0 = default, 1 = mono).eq_run(1)→eq_bpsd_putand pulling viatr_check_bpsd_pullon the SAME image (ok=1). Default per-module image returns ok=0._detect_mono/_active_rules/_MONO_ONLY_RULES.Critical scope decision:
_MONO_ONLY_RULESis EMPTY in Phase 2bCodex pre-push review caught that populating
("eq","tr")in_MONO_ONLY_RULEShere would causerun_pipeline([("eq",...),("tr",...)])to fire the verify rule against the TotPipeline'sTrlibinstance, which still loads its OWN per-modulelibtrapi.so(NOT the mono image). Result: eq pushes tolibeqapi.so's PRIVATE bpsd, tr reads fromlibtrapi.so's PRIVATE bpsd, verify deterministically fails → user-facing regression.Phase 2b ships the SCAFFOLDING: detection mechanism + infrastructure + Layer-C symbol-chain proof. Phase 2c will populate the rule once the wrappers (Eqlib/Trlib/etc.) are plumbed to route through the mono
.sovia a shared path.Files touched
tot/tot_is_mono.f90tot/tot_is_mono_mono.f90tot/tot_api.f90tot/tot_api.hint tot_is_mono(void)declarationtot/MakefileOBJS_PICaddstot_is_mono.o;OBJS_PIC_MONOsubstitutestot_is_mono_mono.opython/totlib/_ffi.pytot_is_monoprototype + try/except for legacy buildspython/totlib/pipeline.py_detect_mono(warns on failure),_active_rules, empty_MONO_ONLY_RULES,run_pipelineuses instance lookuppython/totlib/tests/test_mono_bpsd_smoke.py.github/workflows/python-tests.ymldocs/superpowers/specs/2026-05-18-l7b-ii-phase-2b-design.mdWhy standalone-function flag instead of MODULE+PARAMETER
An earlier draft proposed
MODULE tot_mono_flagexportingLOGICAL, PARAMETER :: tot_is_monowithtot_api.f90USEing it. That pattern forcestot_api.orecompile per build target becausePARAMETERis inlined at the call site. Standalone-function (the shipped pattern) lets a singletot_api.oparticipate in both builds — symbol resolved at link time.Reviewer trail
fcd5e975→ 3 MED + 3 LOW. All addressed in amend58d44484. Delta re-review: approve.fcd5e975→ HOLD (critical: premature rule activation would regress run_pipeline on mono users). Amend empties_MONO_ONLY_RULES. Delta re-review: "amendment is clean".Test plan
tot_is_mono()= 0; mono = 1libtotapi_mono_inspectboth HARD GATES still passlibtotapi.sopathOut of scope (Phase 2c)
Eqlib/Trlib/Fplib/Tilib/Wrxlibthrough the mono.so(likely viaMONO_LIB_PATHenv var honored by each_ffi.py)._MONO_ONLY_RULESwith the("eq","tr")verify rule (cannot fire usefully until wrapper plumbing exists).🤖 Generated with Claude Code
Note
Medium Risk
Medium risk because it changes the C ABI surface and build/link inputs for both
libtotapi.soandlibtotapi_mono.so, and adds new runtime detection paths inTotPipelinethat depend on dynamic library loading and warnings behavior.Overview
Introduces a new C ABI entrypoint
tot_is_mono()(0=default per-module, 1=monolithic) by linking a per-target standalone Fortran implementation (tot_is_mono.f90vstot_is_mono_mono.f90) and wiring it intotot/Makefile,tot_api.h, andpython/totlib/_ffi.py(best-effort prototype for legacy builds).Adds
TotPipelineinstance-level coupling-rule overlay: it lazily loadslibtotapito detect mono mode and uses_active_rulesto optionally merge_MONO_ONLY_RULES(kept empty in this phase), switchingrun_pipelineto consult the per-instance rule table.Adds a new Layer-C pytest
test_mono_bpsd_smoke.pythat exercises mono broker sharing viaeq_run(1)+tr_check_bpsd_pulland verifies the default build reportstot_is_mono()==0, and updates themono-buildGitHub Actions job to also build defaultlibtotapi.soand run this smoke test after the existing mono equivalence test.Reviewed by Cursor Bugbot for commit 58d4448. Bugbot is set up for automated code reviews on this repo. Configure here.