Design Gate — tinyclaw hermetic env scrubber (issue #3161)
Date: 2026-08-06 · Wave 0 of Hermes parity arc (epic #3160)
Problem
Integration tests in crates/terraphim_tinyclaw/tests/ silently pick up
the developer's real env vars. Two failure modes:
- Credential leak: a hermetic test hits a live API using the
developer's
OPENAI_API_KEY/SLACK_BOT_TOKEN/ etc. - False-positive pass: a test that should fail (missing env var) actually passes because the dev's real var makes the happy-path branch fire.
Hermes Agent solves this with a pytest autouse fixture
(_hermetic_environment in tests/conftest.py:340). Rust has no autouse
test fixtures — we need an explicit, convention-enforced discipline.
Decision (code touchpoints)
1. New helper module: crates/terraphim_tinyclaw/tests/common/mod.rs
pub fn scrub_env():
- Strips 19 credential / API-key env vars (
SCRUB_VARSconst): LLM keys (OPENAI_API_KEY,ANTHROPIC_API_KEY,KIMI_API_KEY,EXA_API_KEY,ZAI_API_KEY,MINIMAX_API_KEY,OPENCODE_API_KEY), service tokens (GITHUB_TOKEN,GITEA_TOKEN), voice (WHISPER_MODEL_PATH), local-LLM (OLLAMA_BASE_URL,OLLAMA_MODEL), channel credentials (SLACK_BOT_TOKEN,SLACK_APP_TOKEN,SLACK_SIGNING_SECRET,TELEGRAM_BOT_TOKEN,DISCORD_BOT_TOKEN,MATRIX_HOMESERVER_URL,MATRIX_ACCESS_TOKEN). - Pins
TZ=UTC,LANG=C.UTF-8,LC_ALL=C.UTF-8. - Redirects
HOME,XDG_CONFIG_HOME,XDG_DATA_HOME,XDG_CACHE_HOMEto a per-process temp dir at/tmp/terraphim-tinyclaw-hermetic-<pid>.
pub fn hermetic_home() -> PathBuf — returns the temp dir so tests can
stage tinyclaw.toml fixtures under it.
The unsafe blocks are required because std::env::set_var /
remove_var are unsafe in Rust 2024 (process-global state). Safe
because the test harness is single-threaded at setup time.
2. New doc: crates/terraphim_tinyclaw/TESTING.md
Documents the discipline: file-scope mod common; + per-#[test]
first-line call. Explains why per-function (Rust syntax: statements
not allowed at module scope). Documents the #[ignore] +
TERRAPHIM_TEST_LIVE=1 opt-in for live tests.
3. Retrofit 5 integration test files
| File | Test fns scrubbed |
|---|---|
| config_wiring.rs | 3 |
| gateway_dispatch.rs | 4 |
| skills_benchmarks.rs | 3 |
| skills_integration.rs | 12 (1 #[ignore]) |
| slack_integration.rs | 2 (both #[ignore], gated behind --features slack) |
Each retrofit: mod common; after doc comments + common::scrub_env();
as the first line inside each #[test] / #[tokio::test] fn body.
4. CI grep gate (deferred)
Heuristic grep is brittle; precise version needs Python AST or
syn-based analysis. Documented in TESTING.md but not implemented
in this PR — separate issue to track.
Ground truth to verify (never assume)
Verified during implementation:
- ✅
cargo build -p terraphim_tinyclaw --testsexit 0 (35s cold) - ✅
cargo clippy -p terraphim_tinyclaw --all-targets -- -D warningsexit 0 - ✅
cargo test -p terraphim_tinyclaw --no-fail-fast— 196 tests pass, 0 fail, 1 ignored (live) - ✅
cargo fmt -p terraphim_tinyclaw --checkclean - ✅
tests/common/mod.rsis auto-discovered by cargo as a submodule of every integration test binary in the sametests/directory (Rust 2018+ convention) - ✅
common::scrub_env()as first line of#[test]fn compiles - ✅
common::scrub_env();at module top level is a Rust syntax error (statements not allowed at module scope — only items). Verified via/tmp/test_modrustc test.
Acceptance criteria
- [x]
tests/common/mod.rsexists withpub fn scrub_env()andpub fn hermetic_home(). ✅ - [x]
TESTING.mddocuments the discipline. ✅ - [x] All 5 integration test files retrofitted. ✅
- [x]
cargo build,cargo clippy -D warnings,cargo test,cargo fmtall green. ✅ - [ ] CI grep gate (deferred — separate issue)
- [ ] Hermes
_hermetic_environmentfixture parity (Rust cannot match autouse semantics; convention + gate is the closest equivalent).
Non-goals
- Not adding
ctorcrate dependency. Per-#[test]call is idiomatic and avoids a new dep. - Not auto-generating a fixture. Rust has no autouse mechanism; convention is the closest practical equivalent.
- Not implementing CI grep gate in this PR. Out of scope; tracked separately.
- Not modifying unit tests in
src/.src/code never reads env vars at module scope (verified during the adf design phase); only integration tests are affected.
Test plan
- Unit verification —
cargo test -p terraphim_tinyclaw --libconfirms no regressions in the 174 unit tests. - Integration verification —
cargo test -p terraphim_tinyclaw --testsruns all 5 integration binaries with the new hermetic env. Confirmed: 196 passed, 0 failed, 1 ignored (live test, correctly#[ignore]d). - Hermetic isolation — tests pass even when the developer has
SLACK_BOT_TOKEN,OPENAI_API_KEY, etc. set in their real env. (Implicit: the scrubberremove_vars them. To verify explicitly:SLACK_BOT_TOKEN=xoxb-test cargo test -p terraphim_tinyclawand confirm the slack tests stay ignored and don't accidentally try to hit Slack.) - fmt/clippy/build gates — all green per ground-truth section.
Gates
| Gate | Status |
|---|---|
| cargo build -p terraphim_tinyclaw --tests | ✅ exit 0 |
| cargo clippy -p terraphim_tinyclaw --all-targets -- -D warnings | ✅ exit 0 |
| cargo test -p terraphim_tinyclaw --no-fail-fast | ✅ 196/196 |
| cargo fmt -p terraphim_tinyclaw --check | ✅ clean |
| Structural PR review | ⏳ next step |
| adf/build Gitea status | ⏳ post-merge |
| Merge to main | ⏳ |
Notes for review
common::scrub_env()usesunsafe { std::env::set_var(...) }blocks. This is required in Rust 2024 (process-global state). Safety is upheld because the test harness callsscrub_env()from a single-threaded test setup before any concurrent test execution.- The
SCRUB_VARSlist is intentionally broad — false positives (clearing an unused var) are harmless; false negatives (a real key leaks in) are not. TERRAPHIM_TEST_LIVEis intentionally not inSCRUB_VARS— it is the explicit opt-in marker for live tests (see TESTING.md).