fix(ingestion): wire memory persistence contract to ingestion hook#545
fix(ingestion): wire memory persistence contract to ingestion hook#545zachg-devops wants to merge 2 commits intospacedriveapp:mainfrom
Conversation
The ingestion process_chunk function creates a MemoryPersistenceContractState and passes it to the tool server, but never connects it to the SpacebotHook. This means the hook's on_tool_call_result handler can never call set_terminal_outcome() because memory_persistence_contract is None. As a result, has_terminal_outcome() always returns false after prompt_once() completes, causing every chunk to fail with "completed without memory_persistence_complete signal" — even when the model correctly calls the tool and receives a success response. The fix adds .with_memory_persistence_contract(contract_state.clone()) to the hook construction, matching how branch.rs wires the same contract state (line 71). Fixes spacedriveapp#538
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughThe ingestion chunk flow now wires the memory-persistence contract into the Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/agent/ingestion.rs (1)
523-524: Please add a focused regression test for this wiring.Given this is a stateful terminal-signal path, a targeted test would help prevent regressions where
memory_persistence_completesucceeds buthas_terminal_outcome()remains false.Based on learnings:
For changes in async/stateful paths (worker lifecycle, cancellation, retrigger, recall cache behavior), include explicit race/terminal-state reasoning in the PR summary and run targeted tests in addition to just gate-pr.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/agent/ingestion.rs` around lines 523 - 524, Add a focused regression test that exercises the stateful terminal-signal path wired by with_memory_persistence_contract(contract_state.clone()): simulate/drive memory_persistence_complete on the contract_state (or call the same code paths that would trigger it), then assert that has_terminal_outcome() becomes true for the associated worker/operation; ensure the test reproduces the async lifecycle (await necessary futures, use timers or synchronization to force potential races) so it fails if persistence completes but has_terminal_outcome() is not set.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/agent/ingestion.rs`:
- Around line 523-524: Add a focused regression test that exercises the stateful
terminal-signal path wired by
with_memory_persistence_contract(contract_state.clone()): simulate/drive
memory_persistence_complete on the contract_state (or call the same code paths
that would trigger it), then assert that has_terminal_outcome() becomes true for
the associated worker/operation; ensure the test reproduces the async lifecycle
(await necessary futures, use timers or synchronization to force potential
races) so it fails if persistence completes but has_terminal_outcome() is not
set.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ea78a699-c2bb-45c4-bac5-a2ec56b748cb
📒 Files selected for processing (1)
src/agent/ingestion.rs
…pacedriveapp#538) Two tests that verify the root cause and fix: 1. ingestion_hook_without_contract_does_not_record_terminal_outcome Confirms the bug: a hook created without with_memory_persistence_contract() does not record the terminal outcome even when memory_persistence_complete tool call succeeds. 2. ingestion_hook_with_contract_records_terminal_outcome Confirms the fix: a hook created with with_memory_persistence_contract() correctly records the terminal outcome after a successful tool call.
|
Oh this is a great catch for a long time bug, thank you! |
|
Thanks @jamiepine! Happy to contribute. Quick note on scope: the CodeRabbit pre-merge check flagged that issue #538 outlines four objectives but this PR only addresses one (the contract wiring). After testing, the contract wiring fix was the critical missing piece — once we applied it, ingestion completed successfully across 13 files with multiple models (Mistral Small 3.2, GPT-4.1 nano, MiniMax M2.7, Step 3.5 Flash). The other items in #538 (retry loop for ingestion, deduplication on retry, ToolUseEnforcement widening) are separate concerns that could improve robustness further, but this one-line fix resolved the immediate blocker. Happy to submit separate PRs for those if there's interest. The regression tests in the second commit verify both the broken path (hook without contract → |
Summary
MemoryPersistenceContractStateto theSpacebotHookin the ingestionprocess_chunkfunction.with_memory_persistence_contract(contract_state.clone())to match howbranch.rswires the same state (line 71)Problem
The ingestion pipeline creates a
MemoryPersistenceContractStateand passes it to the tool server, but never connects it to theSpacebotHook. Becausememory_persistence_contractisNoneon the hook, theon_tool_call_resulthandler never callsset_terminal_outcome()— even when the model correctly callsmemory_persistence_completeand receives a success response.This causes
has_terminal_outcome()to always returnfalse, making every ingestion chunk fail with:Root Cause
In
ingestion.rsline 517-523, the hook is constructed viaSpacebotHook::new(...)without calling.with_memory_persistence_contract(). Compare withbranch.rsline 70-71 which correctly wires the contract state.Fix
One line: add
.with_memory_persistence_contract(contract_state.clone())to the hook construction inprocess_chunk.Testing
Tested with 4 different models via OpenRouter (MiniMax M2.7, Step 3.5 Flash, GPT-4.1 nano, Mistral Small 3.2). All correctly call
memory_persistence_completebut all fail thehas_terminal_outcome()check. This fix resolves the check by ensuring the hook records the terminal outcome when the tool succeeds.Fixes #538