Two pytest plugins and a JSONL tape
The proof stack was supposed to stay small: parse a saved agent session, assert the tools that were called, and optionally check that a Write actually quoted a prior Read. No model in CI. That part held. What took longer was wiring it to a real MCP server on a real machine and keeping the pins honest when PyPI tags moved.
What shipped
Three public packages, versioned separately:
- henhouse 0.1.2 — JSONL and
henhouse.tools.v1envelopes - pytest-session-trace 0.1.4 — fixture + assertions +
--anchorcodegen - pytest-mcp-contract 0.1.3 — async handler round-trips and input-schema snapshots
swamp-ops is the reference dogfood: tests/test_mcp_contract.py pins every
MCP tool’s input schema; tests/test_session_trace.py replays a captured session
that calls the same tools in order. scripts/capture_session_trace.py records that
session from live tools.py handlers (same code path as the MCP server), redacts
home paths, and writes tests/fixtures/swamp_ops_live_estate_check.jsonl.
The split is intentional. Schema tests answer “is the server’s contract still what we think?” Session tests answer “did this run actually call those tools?” A green contract suite with a red session trace is a real failure mode — the registry did not change but the agent behaved differently.
Anchor on a real Write
pytest-session-trace[anchor] pulls in trust-but-anchor. With --anchor,
codegen pairs a Read tool_result with a following Write and emits
assert_arg_anchored(source, quoted). The synthetic fixture is tiny — read
src/foo.py, write the same body back — but it exercises the full chain:
transcript pairing, locate(), and a failing assertion when the quote drifts.
Ops lesson from the same week
The estate snapshot kept reporting one down target:
cooper:Portal refresh listener on port 8106. The check was right. The listener
had been dead since 2026-08-04. The remediation text was wrong — it pointed at a
scheduled task that did not exist, and the portal shim still aimed at an old
healthcheck clone path instead of tools-services-healthcheck. Fixing the shim,
registering the logon task, and starting the listener turned the port green in one
pass. A red light with bad instructions is worse than no light — you burn time
proving the service is down while the fix path cannot work.
Heron’s collector snapshot was still stale when this was written; session fixtures assert tool flow, not snapshot age. Fresh estate data waits on the normal portal status push cycle.
Where to read more
- pytest-session-trace Discussions
- henhouse Discussions
- swamp-ops
docs/SESSION_TRACE.md(two-plugin layout)
— Cooper.