Stage 8: Engineering Testing and Troubleshooting
Course: Hermes Agent Source Code Course · Stage Paper Pre-set: Stage 0~7 Overview of Matches:
engineering.mdGoal of this stage: Use minimal reproduction and evidence chains to locate the problem, avoiding mistaking “local runs” for “contracts are intact.”
1. This stage addresses what to address
Section titled “1. This stage addresses what to address”Hermes failures often lie not within a single function but between two boundaries:
- Admission has been submitted, but the worker has not claimed it;
- The tool is running, but the result is not canonical;
- Fallback is activated, but no rebuild request has been made;
- Compression successful, but session reads incorrectly;
- UI is displayed, but transcript is not closed;
- Two profiles share one process, and the backend ticker is mistakenly set to home.
The goal of engineering is not to “write more tests,” but to ensure that each type of fact has observable evidence and independent verification points.
2. Remember one judgment first
Section titled “2. Remember one judgment first”First determine whether it has already occurred/submitted, then determine why it did not appear as expected.
The same sentence “the tool didn’t respond” could be:
- Never started at all: admission still queued;
- Started but not executed: lease or approval stuck;
- Executed but not seen by the model: result not canonical;
- Written but UI not shown: event/delivery issue;
- Submitted but deduplicated by the platform: receipt/idempotent issues.
The fixes for the five types of problems are completely different.
3. Source code main storyline
Section titled “3. Source code main storyline”- First, find the process: The logs and states of classic CLI, Gateway, TUI Python backend, Desktop, and cron worker are not aligned.
- Then look for turns:
prompt.submit,SessionAuthority.submit(), admission id, generation,TurnRunner. - Find enforcement rights: lease holder,
expires_at, refresh log, liveness watchdog. - Find the context: system prompt source,
build_api_messages(), number of preflights, compression lock. - Then find the call: request id, provider, model, retry, credential rotation, fallback.
- Then find tools: call flush, executor, result commit, guardrail, timeout.
- Finally, find the finishing touch: finalizer, assistant row, transcript tail, delivery receipt, event replay.
- The test directory is organized by layer:
tests/gateway,tests/agent,tests/tools,tests/hermes_cli,tests/cron,tests/tui_gateway,tests/acp_adapter, etc. - Minimal reproduction should use temporary
HERMES_HOME, fake provider, fake adapter, and temporary SessionDB; Do not conduct destructive experiments on real state.db. - When modifying persistence/protocols, first find contracts from existing tests, then add five types of use cases: “success, boundary, failure, recovery, replay.”
4. Key evidence and contracts
Section titled “4. Key evidence and contracts”| Symptoms | First Evidence | Cannot be directly assumed |
|---|---|---|
| turn Stuck | admission status + lease holder | Models are slow |
| No results for tools | call row + executor log + result row | The tool did not execute |
| Repeated answers | request id + delivery receipt + event seq | Model Repeated Generation |
| No Context Lowered | pressure + threshold + lock/cooldown | The compressor is broken |
| Fallback Failure | retry trace + fallback route + preflight | New provider not available |
| Multiple profile serial | Thread/task profile scope | Session ID is incorrect |
| DB Lock | writer, busy timeout, lock holder | Directly delete WAL |
| schema incompatible | Binary version + DB version | Manually modify table structure |
Recommended Layered Testing:
- Pure contract testing: verdict, payload normalization, state transitions, error classification;
- Component Testing: SessionAuthority, lease, compressor, tool round;
- Integration Test: Gateway/TUI submitted to finalize;
- Recovery Test: Process interrupt,
unknown, stale lease, orphan tool pair; - Compatibility Testing:p rovider adapter, schema migration, RPC wire contract;
- End-to-End Smoking: Only verifies the main chain to reach it, does not replace the top five categories.
5. An observable link
Section titled “5. An observable link”Reproduction command → Record process / profile / HERMES_HOME / session id → Check admission → Check lease and generation → Check request id / provider / preflight → Check tool call / result → Check finalizer / transcript tail → Check delivery receipt / event sequence Form a 'occurred/not occurred' conclusion Write the minimal failing test Run the adjacent layer test again after fixing6. Experiment
Section titled “6. Experiment”Experiment A: Run a turn in a temporary environment
Section titled “Experiment A: Run a turn in a temporary environment”$env:HERMES_HOME = Join-Path $env:TEMP ("hermes-test-" + [guid]::NewGuid())python -m pytest tests/gateway/test_managed_turn_contract.py -qRecord the DB and logs in the temporary directory before and after the test, confirming that no real home is written.
Experiment B: Write the minimum test for tool timeout
Section titled “Experiment B: Write the minimum test for tool timeout”Using a fake tool to block the timeout, asserting the existence of the corresponding tool result, and that subsequent models can see the termination fact.
Experiment C: Writing a contract test for fallback
Section titled “Experiment C: Writing a contract test for fallback”Let the fake provider return a fallback error, asserting that after the fallback, the preflight will rerun, rather than reusing the old request.
Experimental Reference Answers
Section titled “Experimental Reference Answers”- Experiment A: All states should be temporary
HERMES_HOME; can be safely deleted after testing. The real profile should not have a new session. - Experiment B: Assert that the tool call is persistent, the timeout result is canonical, and the turn is not pending; Do not only assert “has been called” in the fake tool.
- Experiment C: Asserts that the new provider’s requests are generated by reassemble/preflight, and the old messages/headers have not been mindlessly reused.
7. Failure and recovery
Section titled “7. Failure and recovery”| Symptoms | Common Root Causes | Proper Management |
|---|---|---|
| turn always queued | No drain/ticker, previous claim not settled | Check owner, lease, event; do not forcibly change the final state |
| turn never outputs | provider stream, tool approval, clarify, lock | query request id, tool status, watchdog |
| Tool result missing | result persist failed or interrupted without filling skipped | Keep pairing, first check side effects |
| Page has answers, database does not | finalize/transcript tail not closed | Check finalizer and delivery order |
| Compression repeatedly fails | lock, cooldown, summary provider, insufficient window | Etc./release/repair provider, cannot retry indefinitely |
| After fallback: 400/overflow | Wire copy not rebuilt: | Re-preflight |
| Repeat tool after restart | unknown is treated as queued |
readback/reconcile + idempotent |
| Multi-profile threading | Background threads/RPC not bound scope | Explicitly bound home/profile |
| state.db locked | Long transaction, error lock, another writer | Holder query, backup, official repair |
| Prompt cache hit drop | Uncompressed path reconstruction prompt | Compare stable bytes with reconstruction call stack |
8. Pass through customs and self-test
Section titled “8. Pass through customs and self-test”- Why is it important to distinguish the process before troubleshooting?
- How to distinguish between “the tool did not run” and “it ran but the result did not reach the model”?
- Why is the temporary
HERMES_HOMEthe test bottom line? - What facts must a recovery test at least cover?
- Why can’t schema migration be changed directly in live DB?
- After fixing a fallback bug, why run preflight and Provider Adapter tests?
Reference answers
Section titled “Reference answers”- Because the entry, host, agent, cron, and UI may be different processes; Searching for logs in error processes will yield a false conclusion of “no execution.”
- Check the canonical call, executor log, result row, and
_incremental_persistence_failed. If only the call log has no result, it may indicate execution failure or persistence failure; If the result is not in the model, check the commit and loop backfill. - It isolates real sessions, leases, admissions, skills, and credentials, preventing tests from corrupting user ledgers or triggering real external side effects; and also makes failures repeatable.
- At least cover: Submitted calls, unknown/missing results, admission status, lease attribution, canonical session, final transcript, and external side effect verification.
- There may be concurrent reader/writer on the live DB; manual modification will damage transactions, indexes, generation, and FTS; You must first stop writing and back up, then migrate the replica to the official site and verify that old data is readable.
- Because fallback changes the provider, it can easily affect request fields, context windows, tool schemas, and preflight; Only testing “final success” will miss protocol incompatibilities and duplicate compression.
9. Source code coordinates
Section titled “9. Source code coordinates”gateway/session_authority.py::SessionAuthoritygateway/run_turn_runner.py::TurnRunneragent/turn_facade_lease.py::DurableTurnLeaseagent/turn_tool_round.py::run_tool_roundagent/turn_api_error.py::handle_api_erroragent/context_compressor.py::ContextCompressorhermes_state.py::SessionDBtests/gateway/tests/tools/tests/agent/tests/cron/tests/tui_gateway/
10. Next
Section titled “10. Next”Enter Stage 9: Combine the boundaries of the first eight stages into a graduate transformation that can be implemented, restored, and tested.