feat(python): MONO_LIB_PATH wrapper routing (#208 Phase 2c PR-A)#212
Merged
Conversation
Adds python/_runtime_mode.py with mono_lib_path() — the single authoritative reader for the MONO_LIB_PATH env var. Strict + verify per spec D-4: missing file -> FileNotFoundError; failed dlopen -> RuntimeError; missing tot_is_mono symbol -> RuntimeError; tot_is_mono() != 1 -> RuntimeError. lru_cache'd; cache_clear() exposed for runtime env mutation (tests). First test case from spec §8.1: test_helper_cache_clear pins the cache + cache_clear contract so future refactors cannot silently regress R-1. Wrappers in subsequent commits will consume this helper as priority-0 in their _default_lib_path() chains. Spec: docs/superpowers/specs/2026-05-23-l7b-ii-phase-2c-pra-loader-contract-design.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
eqlib._ffi._default_lib_path() now consults python._runtime_mode.mono_lib_path() before its existing 4-step resolution. If MONO_LIB_PATH is set + valid, the wrapper loads the monolithic image instead of libeqapi.so. Explicit lib_path=... to the Eqlib(...) constructor still wins (early branch in load_library runs first). Adds 3 tests from spec §8.1 going through the eqlib path: test_unset_falls_back_to_per_module, test_set_routes_eqlib, test_missing_file_raises. Other 5 wrappers wired in next commit; test_set_routes_all_wrappers (5 of 6) lands after the rollout. eqlib's own test suite (eqlib/tests/) stays green. Spec: docs/superpowers/specs/2026-05-23-l7b-ii-phase-2c-pra-loader-contract-design.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Roll out the priority-0 mono routing hook from eqlib (prior commit) to trlib, fplib, tilib, wrxlib, and totlib. Pattern is identical across wrappers — only the per-module <MOD>LIB_PATH name differs. totlib's hook (D-7): MONO_LIB_PATH overrides TOTLIB_PATH on the orchestrator wrapper, so a TotPipeline whose Tot has mono routing gets a consistent broker view across all 6 wrappers it instantiates. Tests expanded to a parameterized TestAllWrappersRouting that exercises all 6 wrappers in a single subTest loop for each of: unset_falls_back_to_per_module, set_routes_all_wrappers, missing_file_raises. Per-module test suites (eqlib/tests, trlib/tests, ...) all stay green — priority-0 insertion is additive when MONO_LIB_PATH is unset, which is the case for those existing tests. Spec: docs/superpowers/specs/2026-05-23-l7b-ii-phase-2c-pra-loader-contract-design.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the remaining 5 spec §8.1 cases on top of the 4 already in place (1 helper + 3 wrapper-flow): - test_per_module_env_var_still_honored: pins D-5 "non-mono chain unchanged" — uses tempfile-staged fake .so so the assertion is on the env-var step, not a side-effect of the fallback file existing. (Bugfix: capture mono_so before popping MONO_LIB_PATH from env so _mono_path() returns "" rather than "."). - test_non_mono_so_raises: default libtotapi.so (tot_is_mono==0) surfaces "not a mono image" RuntimeError. - test_wrong_so_without_tot_is_mono_raises: libobjc-trampolines.dylib (macOS) / libc.so.6 (Linux) surfaces "no tot_is_mono symbol" RuntimeError. Uses _UNRELATED_SO_CANDIDATES with real-on-disk requirement (macOS dyld cache paths fail Path.exists() so we skip them in favour of /usr/lib/libobjc-trampolines.dylib which is always a real file on macOS 12+). - test_unreadable_so_raises_runtime_error: empty/text file surfaces "cannot be dlopen'd" — exercises the §6 try/except OSError wrap. - test_explicit_constructor_path_still_wins: Tot(lib_path=...) at the user-facing constructor level beats MONO_LIB_PATH. 9 cases total; full local run green (9 passed, 0 skipped). Spec: docs/superpowers/specs/2026-05-23-l7b-ii-phase-2c-pra-loader-contract-design.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fix 1: TestExplicitConstructorPathWins.test_explicit_constructor_path_still_wins - Move tot.close() into finally block to prevent ctypes handle leak on assertion failure - Initialize tot=None before try block so finally can safely check before closing Fix 2: _UNRELATED_SO_CANDIDATES macOS comment - Add future-proofing note about libobjc-trampolines.dylib fixture - Flag that test will SKIP (not error) if Apple removes the file - Reference #208 PR-A Codex 2026-05-25 review MED-2 All 9 tests pass (pytest --forked --timeout=120 --timeout-method=signal). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- tot/tot_api.h: 1 prose paragraph in the existing header docstring. No ABI / symbol changes. - python/totlib/README.md: short "Mono routing" section with usage, failure-mode contract, and pointer to PR-B for the behavioral follow-up. Spec: docs/superpowers/specs/2026-05-23-l7b-ii-phase-2c-pra-loader-contract-design.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends the mono-build job to run python/totlib/tests/ test_mono_routing.py with MONO_LIB_PATH + TOTLIB_PATH both set. The default libtotapi.so artifact for test_non_mono_so_raises is already built by the "Build default libtotapi.so" step (PR #207), so no new build step is required. This is a sanity test only — no behavioral cross-module rule is exercised. PR-B will add the TotPipeline.run_pipeline([eq, tr]) integration test that uses this same routing. Spec: docs/superpowers/specs/2026-05-23-l7b-ii-phase-2c-pra-loader-contract-design.md 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 951c56c. Configure here.
k-yoshimi
added a commit
that referenced
this pull request
May 26, 2026
Terminal step of Phase 2c. Activates the ("eq","tr") verify rule
in TotPipeline._MONO_ONLY_RULES (now possible after PR-A's
MONO_LIB_PATH plumbing landed at #212) and adds a Layer-D
integration test driving TotPipeline.run_pipeline through eq->tr
on the mono image. PR-B closes #208 and #201.
Spec structure: 12 sections covering context, scope (minimum +
1 negative test), 4 design decisions D-1..D-4 each with chosen/
rejected/why, rule definition (named module-level callable so
error-message repr is meaningful), 2 test cases with try/finally
hygiene and assert-outside-with discipline, CI step in mono-build
job, lifecycle invariants + the empirical resolution of the
tr_init-after-eq.run ordering question, and acceptance criteria.
Codex 2026-05-26 reviewed five rounds:
- Round 1 (pre-spec proposal): 3 findings (MED-7 timing,
MED-6 negative-test, LOW-5 assert-outside-with) all addressed.
- Round 2: SHIP IT with 2 LOW clarifications — D-4 scope
narrowed; negative test skip-gate from TOTLIB_PATH env to
per-module .so existence.
- Round 3: LOW — vestigial TOTLIB_PATH branch in helper, removed.
- Round 4: 3 LOW — stale TOTLIB_PATH-centric language at §7.3
docstring, §8 step comment, §8 narrative, and §11 AC#3,
all cleaned.
- Round 5: SHIP IT (clean).
Successor: PR-B implementation (writing-plans skill next).
Out of scope: other module-pair rules, Fortran changes.
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
Closes #208 PR-A milestone (PR-B follow-up in a separate PR).
Adds a single-authoritative routing rule: setting
MONO_LIB_PATH=/path/to/libtotapi_mono.somakes every wrapper(eqlib/trlib/fplib/tilib/wrxlib/totlib) inside or outside
TotPipelineload the same mono image. Plumbing prerequisite forPR-B's
_MONO_ONLY_RULES = {("eq","tr"): [...]}behavioralactivation.
What's in this PR (9 commits)
feat(python): MONO_LIB_PATH helper + cache_clear test— newpython/_runtime_mode.pywithmono_lib_path()(lru_cache + strict verify).test(totlib): cleanup + precise pytestmark comment— minor follow-up on the helper test.feat(eqlib): consume MONO_LIB_PATH helper as priority 0— wire eqlib + 3 wrapper-flow tests.feat(wrappers): wire 5 remaining wrappers— roll out to tr/fp/ti/wrx/totlib; tests parametrized over all 6.test(totlib): restore FileNotFoundError message assertion— Codex code-quality LOW fix.test(totlib): complete 9-case mono routing contract— 5 more spec §8.1 cases (per-module env, non-mono, libc lacks-symbol, empty-file dlopen, explicit lib_path).test(totlib): close handle in finally + libobjc removal note— Codex code-quality MED fixes.docs(tot+totlib): document MONO_LIB_PATH routing—tot/tot_api.h+python/totlib/README.md.ci(python-tests): mono routing sanity step—mono build (Linux libtotapi_mono.so)job extended.Acceptance (spec §11)
python/_runtime_mode.pymatches §6 contract._ffi.pyimplement priority-0 hook per §7 (top-level import + mono_lib_path() before existing 4-step resolution).python/totlib/tests/test_mono_routing.pyhas 9 cases per §8.1 (1 helper + 3 parametrized + 5 standalone).MONO_LIB_PATHandTOTLIB_PATH._MONO_ONLY_RULESuntouched — no behavioral activation (PR-B scope).totlib/tests/test_equivalence.pynot modified, all equivalence tests still pass).Tests
python/totlib/tests/test_mono_routing.py: 9 tests, all pass locally + viapytest --forked. Portable Linux + macOS.ExceptionGroupcompat issue intest_close_raises_exception_group_when_multiple_modules_fail, same as PR fix(tot): cascade eq_api_init in tot_init (#209) #211 baseline — unrelated to PR-A).Pre-existing concern (separate triage item, NOT introduced by this PR):
fplib/tests/test_equivalence.pyandwrxlib/tests/test_equivalence.pyeach have 2 failures on develop baseline. Verified by checkout + run onorigin/develop@da91ee45. Should be filed as a follow-up issue.Out of scope
_MONO_ONLY_RULESpopulated with("eq","tr")rule — PR-B.TotPipeline.run_pipeline([("eq",...),("tr",...)])integration test — PR-B.("eq","tr").feedback_fortran_refactor_needs_lead_signoff.md).<MOD>LIB_PATHenv vars.Spec + Plan
docs/superpowers/specs/2026-05-23-l7b-ii-phase-2c-pra-loader-contract-design.md(3 Codex passes, SHIP IT).docs/superpowers/plans/2026-05-25-l7b-ii-phase-2c-pra-implementation.md(4 Codex passes, SHIP IT).Process notes
Plan executed via subagent-driven-development skill: 6 implementer subagents (Tasks 1-6) + 2-stage review per task (spec + code quality). Task 5 implementer subagent accidentally committed CI yaml to local
developinstead of the worktree branch — cherry-picked to the right branch + reset localdeveloptoorigin/developbefore push. This mirrors thefeedback_codex_worktree_sharing.mdincident from a prior session.Test plan
python-tests.yml(3.11 + 3.13 + mono build with new routing step)🤖 Generated with Claude Code
Note
Medium Risk
Changes default native library resolution for every Python wrapper when MONO_LIB_PATH is set; misconfiguration could load the wrong .so, though validation and forked tests reduce that risk. No behavioral coupling rules are enabled yet.
Overview
Adds
MONO_LIB_PATHas a single routing switch so eqlib, trlib, fplib, tilib, wrxlib, and totlib can all loadlibtotapi_mono.so(one BPSD broker) instead of separate per-module.sofiles.A new
python/_runtime_mode.pyexposesmono_lib_path(): it validates the path, dlopens the library, and requirestot_is_mono() == 1, with clear errors for missing files, bad dlopen, or non-mono images. Each wrapper’s_default_lib_path()consults that helper first; explicitload_library(path=...)/ constructorlib_pathstill overrides.test_mono_routing.py(forked pytest) locks the contract across all six modules; the mono-build CI job runs it. Docs note that eq→tr behavioral coupling stays for PR-B—this PR is loader plumbing only.Reviewed by Cursor Bugbot for commit 951c56c. Bugbot is set up for automated code reviews on this repo. Configure here.