- Plexer: always-active terminal multiplexing. NativePlexerController/ TmuxPlexerController share one interface, chosen by which env var is already set (TAI_PLEXER_SOCKET vs TMUX_PANE); NativePlexerBackend is the pty.fork()-based wrapper (raw mode, SIGWINCH resize forwarding, a Unix-socket control protocol, continuous session logging, a Ctrl+A prefix key distinct from tmux's own Ctrl+B). Auto-wraps new interactive shells via a .bashrc hook mirroring the existing command-not-found install pattern, gated on $TMUX/$TAI_PLEXER_ACTIVE/ a $HOME/.tai-no-plexer bypass. New plexer_send/read/type/status tools, env-gated on a detected controller. - tests/container.py + docker-compose.yml + tests/docker/Dockerfile: a disposable podman/docker image for the tests that must never touch a real machine (installing the plexer hook into a real ~/.bashrc, a live tmux session). Required suite (test_seal.py, test_tai.py, tests/plexer.py, tests/bashrc.py) gates the result; tests/rag.py runs as best-effort since its real-LLM dependency can't be satisfied inside a disposable container with no nested engine. - tests/bashrc.py: real end-to-end hook install + bash -i auto-wrap, anti-recursion, tmux-active skip, non-interactive skip, a live TmuxPlexerController test, and an install/uninstall round-trip. - tests/plexer.py: NativePlexerBackend under a real pty, plus a dedicated robustness pass (file permissions, >64KB payload framing, a malformed control request, a genuine self-deadlock fix in the write-to-child path). - tai_test.py: the shared async process-driving framework (pipe/pty transport, group-wide kill-and-reap, a StdinBridge for --interactive) both test files build on. - ChatClient last-resort backend: when there's no local opencode CLI but the sandbox box is already running, install opencode into it (official Node tarball, not apt -- apt's nodejs/npm pull in webpack/babel/jest as transitive deps) and route through it instead of giving up. Only reachable after the first retry round already failed everything else; never creates the box itself. TmuxPlexerController.status() fixed to use `list-panes` instead of `display-message -p`, which silently fell back to the default pane instead of erroring against a dead target. - Fix 4 pre-existing test_tai.py bugs surfaced by running it in a clean environment for the first time: two stale mock signatures (subprocess.run's stdout= kwarg, Chat.complete's deadline= kwarg), one $TMUX-dependent catalog assertion, one tty-dependent color assertion. - research/: a sourced research archive (Source Dossier/Citation Card/Conclusion tiers) backing the above design decisions, with its own audit.py consistency checker. - .gitignore: fix a blanket *.md rule that was about to silently drop the entire research/ archive from version control; add __pycache__/ and the one-off debug transcript this session started from. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
3.3 KiB
CERN Gaudi — SubprocessBaseTest / GaudiExeTest
Metadata
| Field | Value |
|---|---|
| Institute | Molodetz Institute — Research & Retrieval Division |
| Subject | async-pty-process-testing |
| Document type | Source Dossier |
| Tier | 1 |
| Source title | Gaudi merge request !1605 (SubprocessBaseTest / GaudiExeTest), and companion doxygen source page |
| Source URL | https://gitlab.cern.ch/gaudi/Gaudi/-/merge_requests/1605 |
| Source publisher / author | CERN (Gaudi physics-software framework project) |
| Source type | repository (merge request + generated source documentation) |
| Source date (published) | Unknown — not stated |
| Date researched | 2026-10-09 |
| Researched by | tai (agent), model: Claude Sonnet 5, on behalf of retoor |
| Research method | web search synthesis |
| Confidence | High |
| Status | Active |
Summary
CERN's Gaudi framework (used across large physics experiments) includes a SubprocessBaseTest base class and a GaudiExeTest subclass for running and verifying subprocess-based tests. Test authors configure tests by setting class attributes (command, environment, timeout, expected return code) rather than writing imperative test logic, with an overridable preprocessor hook for customizing output handling. This companion source (gaudi.web.cern.ch/doxygen/.../_base_test_8py_source.html) is the generated source documentation for the same base class.
Key findings
SubprocessBaseTestis explicitly documented as providing "the setup and utility methods for ensuring tests run in a consistent environment" — the test author's job is reduced to declaring configuration, not reimplementing process management per test.- Configuration happens through class attributes: command, environment, timeout, and expected return code are all named, documented fields on the test class.
- Output handling is customizable via an overridable
preprocessorattribute onGaudiExeTest, letting a subclass change how reference-output comparison works without touching the runner itself.
Pros / strengths
- A mature, battle-tested framework used in large, real scientific-computing codebases — strong evidence this "declarative base class + override hooks" shape scales to many tests written by many different authors over time, which is exactly the goal stated for
tai_test.py("a structure that scales and will be allowed [to be] followed").
Cons / limitations / caveats
- Domain-specific to physics-software testing (comparing program output against reference files); not every detail (e.g. reference-file resolution relative to the test's own location) was adopted, since
tai_test.py's tests assert on vault state and search output rather than diffing against golden files. - Reviewed via its merge request description and generated source documentation, not a full read of the implementation's actual Python source.
Relevance to tai
The direct architectural model for tai_test.py.TestCase: class-level attributes (name, timeout, mode) instead of constructor boilerplate, and override hooks (setup/run/verify/teardown) instead of one monolithic test method — mirroring Gaudi's class-attribute configuration plus its overridable preprocessor hook.
Changelog
| Date | Change |
|---|---|
| 2026-10-09 | Initial dossier created. |