fix: persist Hermes session state across runs#497
Conversation
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
WalkthroughHermes overlay creation now pre-populates durable state paths in the shared source home. Tests verify state content, sequential state preservation, cleanup, and failure when ChangesHermes durable state
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant PreparedAgentLaunch
participant HermesOverlay
participant source_home
PreparedAgentLaunch->>HermesOverlay: prepare first run
HermesOverlay->>source_home: persist state.db and session.json
PreparedAgentLaunch->>HermesOverlay: restore first run
HermesOverlay->>source_home: retain durable state
PreparedAgentLaunch->>HermesOverlay: prepare second run
source_home->>HermesOverlay: provide persisted state
PreparedAgentLaunch->>HermesOverlay: restore second run
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
RELAY-516 design walkthroughThe diagrams below separate the pre-fix failure from the post-fix lifecycle. Click a code-backed node to open the corresponding immutable source lines. Before: state first created in the temporary overlay is deletedflowchart TD
A["Live Hermes launch resolves caller HERMES_HOME"] --> B["Relay creates a private temporary overlay"]
B --> C["populate_overlay reads entries that already exist in caller HERMES_HOME"]
C --> D{"Do state.db and sessions/ already exist?"}
D -- "yes" --> E["link_state binds existing entries into the overlay"]
D -- "no: fresh home" --> F["No durable state link is created"]
E --> G["Hermes receives the overlay as HERMES_HOME"]
F --> G
G --> H["Hermes v0.18.2 creates state.db only inside the overlay"]
H --> I["Relay process exits and restore removes every temp_dir"]
I --> J["The only state.db is deleted"]
J --> K["Next Relay process uses --resume"]
K --> L["Session not found"]
click A "https://github.com/NVIDIA/NeMo-Relay/blob/3a88c4cc64ec34afeec99205742c61819efb05e3/crates/cli/src/agents/hermes/launch.rs#L35-L41" "Resolve the source Hermes home"
click B "https://github.com/NVIDIA/NeMo-Relay/blob/3a88c4cc64ec34afeec99205742c61819efb05e3/crates/cli/src/agents/hermes/launch.rs#L53-L73" "Create the old overlay"
click C "https://github.com/NVIDIA/NeMo-Relay/blob/3a88c4cc64ec34afeec99205742c61819efb05e3/crates/cli/src/agents/hermes/launch.rs#L75-L102" "Enumerate only pre-existing source entries"
click D "https://github.com/NVIDIA/NeMo-Relay/blob/3a88c4cc64ec34afeec99205742c61819efb05e3/crates/cli/src/agents/hermes/launch.rs#L84-L102" "Missing source entries produce no links"
click E "https://github.com/NVIDIA/NeMo-Relay/blob/3a88c4cc64ec34afeec99205742c61819efb05e3/crates/cli/src/agents/hermes/launch.rs#L92-L98" "Link existing entries"
click F "https://github.com/NVIDIA/NeMo-Relay/blob/3a88c4cc64ec34afeec99205742c61819efb05e3/crates/cli/src/agents/hermes/launch.rs#L84-L102" "Fresh homes have nothing to enumerate"
click G "https://github.com/NVIDIA/NeMo-Relay/blob/3a88c4cc64ec34afeec99205742c61819efb05e3/crates/cli/src/agents/hermes/launch.rs#L41-L49" "Inject overlay as HERMES_HOME and register it for cleanup"
click H "https://github.com/NVIDIA/NeMo-Relay/blob/3a88c4cc64ec34afeec99205742c61819efb05e3/crates/cli/src/agents/hermes/launch.rs#L41-L49" "Hermes writes beneath the injected overlay home"
click I "https://github.com/NVIDIA/NeMo-Relay/blob/3a88c4cc64ec34afeec99205742c61819efb05e3/crates/cli/src/process/launcher.rs#L491-L501" "Remove temporary launch directories"
click J "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/tests/coverage/agents/launcher_tests.rs#L1034-L1056" "Regression boundary: first overlay is removed"
click K "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/tests/coverage/agents/launcher_tests.rs#L1058-L1072" "Regression boundary: second launch"
click L "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/tests/coverage/agents/launcher_tests.rs#L998-L1073" "Failure reproduced by the new sequential-run test before the fix"
classDef failure fill:#ffebe9,stroke:#cf222e,color:#24292f;
class F,H,I,J,K,L failure;
After: durable state is provisioned before overlay populationflowchart TD
A["prepare keeps --dry-run side-effect free"] --> B["Live launch calls create_overlay"]
B --> C["ensure_durable_state_paths runs first"]
C --> D["create_dir_all caller HERMES_HOME/sessions"]
D --> E["create_new caller HERMES_HOME/state.db"]
E --> F{"state.db already exists?"}
F -- "no" --> G["Create an empty durable database without truncation"]
F -- "yes / concurrent creator" --> H["Treat AlreadyExists as success"]
G --> I["Create temporary overlay"]
H --> I
I --> J["populate_overlay now discovers state.db and sessions/"]
J --> K["link_state binds both paths back to caller HERMES_HOME"]
K --> L["Hermes writes through overlay links into durable state"]
L --> M["Relay cleanup removes only the temporary overlay"]
M --> N["Second Relay process links the same durable state"]
N --> O["Hermes --resume finds the session"]
click A "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/src/agents/hermes/launch.rs#L28-L34" "Dry-run exits before filesystem preparation"
click B "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/src/agents/hermes/launch.rs#L35-L49" "Live Hermes overlay preparation"
click C "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/src/agents/hermes/launch.rs#L53-L69" "Provision durable paths before overlay population"
click D "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/src/agents/hermes/launch.rs#L76-L77" "Create caller-owned home and sessions directory"
click E "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/src/agents/hermes/launch.rs#L78-L82" "Open state.db with create_new"
click F "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/src/agents/hermes/launch.rs#L82-L86" "Handle the create result"
click G "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/src/agents/hermes/launch.rs#L78-L83" "Create only when absent"
click H "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/src/agents/hermes/launch.rs#L83-L85" "Tolerate a pre-existing file or concurrent creator"
click I "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/src/agents/hermes/launch.rs#L59-L68" "Create the process-private overlay"
click J "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/src/agents/hermes/launch.rs#L89-L116" "Enumerate durable source entries"
click K "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/src/agents/hermes/launch.rs#L106-L112" "Bind entries into the overlay"
click L "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/tests/coverage/agents/launcher_tests.rs#L1034-L1053" "Write through the first overlay and observe durable state"
click M "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/src/process/launcher.rs#L491-L501" "Remove only registered temporary directories"
click N "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/tests/coverage/agents/launcher_tests.rs#L1055-L1067" "Second overlay sees the same state"
click O "https://github.com/NVIDIA/NeMo-Relay/blob/416b3e75260bbff7f71b7a764cb4febea7fcf5cb/crates/cli/tests/coverage/agents/launcher_tests.rs#L1069-L1072" "Caller-owned state survives both overlay cleanups"
classDef durable fill:#dafbe1,stroke:#1a7f37,color:#24292f;
class C,D,E,G,H,J,K,L,M,N,O durable;
The behavioral change is intentionally before |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/cli/src/agents/hermes/launch.rs`:
- Around line 76-85: Update ensure_durable_state_paths so the AlreadyExists
fallback succeeds only when source_home.join("state.db").is_file() is true;
return the existing I/O error for directories or other invalid entries. Add a
regression test covering an existing state.db directory and verify it is
rejected before populate_overlay can link it.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 7275d02d-9028-4224-9a3b-e83dfa08a632
📒 Files selected for processing (2)
crates/cli/src/agents/hermes/launch.rscrates/cli/tests/coverage/agents/launcher_tests.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
- GitHub Check: Rust / Test (windows-amd64)
- GitHub Check: Rust / Test (macos-arm64)
- GitHub Check: Rust / Test (linux-amd64)
- GitHub Check: Rust / Test (windows-arm64)
- GitHub Check: Rust / Test (linux-arm64)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.rs
📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)
**/*.rs: Any Rust change must runjust test-rust
Any Rust change must runcargo fmt --all
Any Rust change must runcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allfor all FFI work since it is Rust work
Runjust test-rustto validate FFI changes
Runcargo clippy --workspace --all-targets -- -D warningsto enforce strict linting on FFI workWhen Rust files changed as part of Go work, also run
cargo fmt --all,just test-rust, andcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allwhen Rust files are changed as part of Node work
Runcargo clippy --workspace --all-targets -- -D warningswhen Rust files are changed as part of Node work
Runjust test-rustwhen Rust files are changed as part of Node workWhen changing the core Rust runtime or Rust-facing API surface, format Rust code with
cargo fmt(rustfmt defaults), keepcargo clippy -- -D warningsclean, and satisfycargo deny checkperdeny.toml.
**/*.rs: If any Rust code changed, always runjust test-rust.
If any Rust code changed, also runcargo fmt --all.
If any Rust code changed, also runcargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, runcargo fmt --allandcargo clippy --workspace --all-targets -- -D warningseven if relying on pre-commit.
Files:
crates/cli/src/agents/hermes/launch.rscrates/cli/tests/coverage/agents/launcher_tests.rs
**/*.{rs,py}
📄 CodeRabbit inference engine (AGENTS.md)
Follow binding naming conventions in Rust and Python: use
snake_case.
Files:
crates/cli/src/agents/hermes/launch.rscrates/cli/tests/coverage/agents/launcher_tests.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{rs,py,js,mjs,cjs,ts,tsx}: UseJson = serde_json::Valuein Rust-facing runtime APIs where the existing code expects JSON payloads.
UseResult<T>withFlowErrorin core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
Files:
crates/cli/src/agents/hermes/launch.rscrates/cli/tests/coverage/agents/launcher_tests.rs
**/*.{rs,py,go,js,ts,c,h}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Use language-appropriate naming conventions: Rust
snake_case, C FFI exports prefixednemo_relay_, GoPascalCase, Node.jscamelCase, and Pythonsnake_case.
Files:
crates/cli/src/agents/hermes/launch.rscrates/cli/tests/coverage/agents/launcher_tests.rs
**/*.{rs,go,js,ts}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding
//comment form.
Files:
crates/cli/src/agents/hermes/launch.rscrates/cli/tests/coverage/agents/launcher_tests.rs
{crates/**/src/**/*.rs,python/**/*.py}
📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)
Do not add tests under
src; Rust tests belong in cratetests/trees, and Python SDK tests belong underpython/tests.
Files:
crates/cli/src/agents/hermes/launch.rs
**/*
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, usemaintain-dynamic-pluginsand include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, preferuv run pre-commit run --files <changed files...>.
Before review or handoff, runuv run pre-commit run --all-files.
Files:
crates/cli/src/agents/hermes/launch.rscrates/cli/tests/coverage/agents/launcher_tests.rs
**/*.{rs,py,go,js,ts}
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
If a language surface changed, always run that language's test target even when Rust core did not change.
Files:
crates/cli/src/agents/hermes/launch.rscrates/cli/tests/coverage/agents/launcher_tests.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}
📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)
Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.
Files:
crates/cli/src/agents/hermes/launch.rscrates/cli/tests/coverage/agents/launcher_tests.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}
⚙️ CodeRabbit configuration file
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.
Files:
crates/cli/tests/coverage/agents/launcher_tests.rs
🔇 Additional comments (4)
crates/cli/src/agents/hermes/launch.rs (2)
58-58: 🎯 Functional CorrectnessVerify dry-run bypasses durable-state provisioning.
Line 58 creates caller-home paths. The supplied regression invokes
PreparedAgentLaunch::new(..., false), so it does not prove that--dry-runremains side-effect free. Confirm the dry-run path never reachescreate_overlay, or add a regression for it.
58-87: 📐 Maintainability & Code QualityRun the required Rust validation before merge.
Provide successful results for
cargo fmt --all,cargo clippy --workspace --all-targets -- -D warnings,just test-rust, anduv run pre-commit run --all-files. As per coding guidelines, “Any Rust change must runjust test-rust,”cargo fmt --all,cargo clippy --workspace --all-targets -- -D warnings, and “Before review or handoff, runuv run pre-commit run --all-files.”Source: Coding guidelines
crates/cli/tests/coverage/agents/launcher_tests.rs (2)
974-977: LGTM!
998-1073: LGTM!
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
…2-hermes-session-resume-fails-across-relay
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/cli/tests/coverage/agents/launcher_tests.rs`:
- Line 1108: Update the regression assertion around the launcher test’s
`CliError::Io` result to verify the wrapped I/O error has kind
`ErrorKind::AlreadyExists`, rather than accepting any I/O error. Retain the
existing platform-specific side-effect checks and the test’s coverage of the
existing-directory error path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: de50ac86-f39d-4b98-8e47-60bc032af688
📒 Files selected for processing (1)
crates/cli/tests/coverage/agents/launcher_tests.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
- GitHub Check: Check / Run
🧰 Additional context used
📓 Path-based instructions (9)
**/*.rs
📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)
**/*.rs: Any Rust change must runjust test-rust
Any Rust change must runcargo fmt --all
Any Rust change must runcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allfor all FFI work since it is Rust work
Runjust test-rustto validate FFI changes
Runcargo clippy --workspace --all-targets -- -D warningsto enforce strict linting on FFI workWhen Rust files changed as part of Go work, also run
cargo fmt --all,just test-rust, andcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allwhen Rust files are changed as part of Node work
Runcargo clippy --workspace --all-targets -- -D warningswhen Rust files are changed as part of Node work
Runjust test-rustwhen Rust files are changed as part of Node workWhen changing the core Rust runtime or Rust-facing API surface, format Rust code with
cargo fmt(rustfmt defaults), keepcargo clippy -- -D warningsclean, and satisfycargo deny checkperdeny.toml.
**/*.rs: If any Rust code changed, always runjust test-rust.
If any Rust code changed, also runcargo fmt --all.
If any Rust code changed, also runcargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, runcargo fmt --allandcargo clippy --workspace --all-targets -- -D warningseven if relying on pre-commit.
Files:
crates/cli/tests/coverage/agents/launcher_tests.rs
**/*.{rs,py}
📄 CodeRabbit inference engine (AGENTS.md)
Follow binding naming conventions in Rust and Python: use
snake_case.
Files:
crates/cli/tests/coverage/agents/launcher_tests.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{rs,py,js,mjs,cjs,ts,tsx}: UseJson = serde_json::Valuein Rust-facing runtime APIs where the existing code expects JSON payloads.
UseResult<T>withFlowErrorin core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
Files:
crates/cli/tests/coverage/agents/launcher_tests.rs
**/*.{rs,py,go,js,ts,c,h}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Use language-appropriate naming conventions: Rust
snake_case, C FFI exports prefixednemo_relay_, GoPascalCase, Node.jscamelCase, and Pythonsnake_case.
Files:
crates/cli/tests/coverage/agents/launcher_tests.rs
**/*.{rs,go,js,ts}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding
//comment form.
Files:
crates/cli/tests/coverage/agents/launcher_tests.rs
**/*
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, usemaintain-dynamic-pluginsand include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, preferuv run pre-commit run --files <changed files...>.
Before review or handoff, runuv run pre-commit run --all-files.
Files:
crates/cli/tests/coverage/agents/launcher_tests.rs
**/*.{rs,py,go,js,ts}
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
If a language surface changed, always run that language's test target even when Rust core did not change.
Files:
crates/cli/tests/coverage/agents/launcher_tests.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}
📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)
Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.
Files:
crates/cli/tests/coverage/agents/launcher_tests.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}
⚙️ CodeRabbit configuration file
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.
Files:
crates/cli/tests/coverage/agents/launcher_tests.rs
|
/merge |
Overview
Fix Hermes session resume across separate
nemo-relay run --agent hermesprocesses when the caller starts with a freshHERMES_HOME.Hermes Agent v0.18.2 stores session metadata in
$HERMES_HOME/state.db. Relay's temporary Hermes overlay previously linked only entries that already existed in the caller's home. A database first created inside the overlay was therefore deleted with that overlay, causing the next process to fail withSession not found.Details
HERMES_HOMEand durablesessions/directory before populating the temporary overlay.state.dbonly when it is absent, usingcreate_new(true)so existing state is never truncated and a concurrent creator is tolerated.state.dbandsessions/back to the durable home.--dry-runbehavior and per-runconfig.yamlisolation.state.dbis not truncated.No public CLI or API surface changes. The Switchyard example and documentation are unchanged because seeding the database there would mask the general launcher defect.
Validation
Passed:
cargo test -p nemo-relay-cli sequential_hermes_runs_preserve_state_from_a_fresh_homecargo test -p nemo-relay-cli hermes_cargo fmt --allcargo clippy --workspace --all-targets -- -D warningsuv run pre-commit run --all-fileswith only the unrelatedattributions-rusthook skipped; all remaining hooks passed, including Cargo formatting, clippy, check, deny, FFI synchronization, Python, Go, Node, and documentation checks.20260720_133907_e11614; a second Relay process resumed the same ID, and the caller-owned SQLite row remained with four messages after both temporary overlays were removed.Known environment limitations:
just test-rustcompleted all Hermes and launcher coverage successfully, including this regression. The two full-suite attempts were not fully green because unrelated tests flaked under suite load: one temporary dynamic-plugin manifest lookup on the first run, then eight loopback HTTP/MCP health timeouts on the second. The first failure passed immediately in isolation.attributions-rusthook rewrites an unrelatedmd-5license entry already present onrelease/0.6; that generated change was restored and excluded from this PR.8f9db9a6a47f848cdff1d262276ba25a8ae9cbc8and both required Ollama models were otherwise available.Where should the reviewer start?
Start with
ensure_durable_state_pathsincrates/cli/src/agents/hermes/launch.rs, then reviewsequential_hermes_runs_preserve_state_from_a_fresh_homeincrates/cli/tests/coverage/agents/launcher_tests.rsfor the cross-process lifecycle being protected.Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)
Summary by CodeRabbit
Bug Fixes
state.dbpaths safely before overlay population.Tests
state.dbcontents, and failure behavior whenstate.dbis an existing directory.