Description
In crates/zeph-core/src/agent/tool_execution/focus.rs, select_messages_for_compression builds to_compress by iterating over a HashSet<usize> (line 323):
```rust
let to_compress: Vec<zeph_llm::provider::Message> = to_remove_indices
.iter()
.map(|&i| self.msg.messages[i].clone())
.collect();
```
HashSet does not guarantee iteration order, so the messages passed to build_compression_prompt arrive in a random chronological order on each run. This produces variable summarization quality and makes results non-reproducible across identical sessions.
Note: apply_compression_removals already handles this correctly by sorting the to_remove_indices before deletion — the fix needed is only in the to_compress construction.
Reproduction Steps
- Enable
compress_context or request_compaction in a session.
- Observe that the bullet list in the compression prompt has messages in non-chronological order across multiple runs with identical input.
Expected Behavior
Messages passed to the compression LLM prompt should be in the same chronological order as they appear in self.msg.messages (ascending index order).
Actual Behavior
Messages are ordered by HashSet iteration, which is non-deterministic.
Fix
Replace the HashSet iteration with a sorted iteration:
```rust
let mut sorted_indices: Vec = to_remove_indices.iter().copied().collect();
sorted_indices.sort_unstable();
let to_compress: Vec<zeph_llm::provider::Message> = sorted_indices
.iter()
.map(|&i| self.msg.messages[i].clone())
.collect();
```
Environment
- Commit: 08021ee
- Feature:
compress_context / ARC request_compaction
Logs / Evidence
Code review of crates/zeph-core/src/agent/tool_execution/focus.rs:317-328 (CI-889 arch audit).
Description
In
crates/zeph-core/src/agent/tool_execution/focus.rs,select_messages_for_compressionbuildsto_compressby iterating over aHashSet<usize>(line 323):```rust
let to_compress: Vec<zeph_llm::provider::Message> = to_remove_indices
.iter()
.map(|&i| self.msg.messages[i].clone())
.collect();
```
HashSetdoes not guarantee iteration order, so the messages passed tobuild_compression_promptarrive in a random chronological order on each run. This produces variable summarization quality and makes results non-reproducible across identical sessions.Note:
apply_compression_removalsalready handles this correctly by sorting theto_remove_indicesbefore deletion — the fix needed is only in theto_compressconstruction.Reproduction Steps
compress_contextorrequest_compactionin a session.Expected Behavior
Messages passed to the compression LLM prompt should be in the same chronological order as they appear in
self.msg.messages(ascending index order).Actual Behavior
Messages are ordered by
HashSetiteration, which is non-deterministic.Fix
Replace the HashSet iteration with a sorted iteration:
```rust
let mut sorted_indices: Vec = to_remove_indices.iter().copied().collect();
sorted_indices.sort_unstable();
let to_compress: Vec<zeph_llm::provider::Message> = sorted_indices
.iter()
.map(|&i| self.msg.messages[i].clone())
.collect();
```
Environment
compress_context/ ARCrequest_compactionLogs / Evidence
Code review of
crates/zeph-core/src/agent/tool_execution/focus.rs:317-328(CI-889 arch audit).