fix: retain annotated compaction history and honor non-System input policy

This commit is contained in:
2026-10-20 05:34:22 +09:00
parent 90e38551cf
commit 8017c55d82
6 changed files with 446 additions and 259 deletions
+129
View File
@@ -3655,3 +3655,132 @@ async fn journal_snapshot_counts_against_compactor_hard_input_cap_without_mutati
);
}
}
#[tokio::test]
async fn identical_reference_and_retained_inputs_preserve_distinct_metadata_after_compaction() {
use session_store::*;
let client = MockClient::new(vec![
single_text_events("fixture"),
single_text_events("fork checkpoint"),
write_summary_tool_use_events("review-summary", "replacement summary"),
single_text_events("done"),
single_text_events("restore checkpoint"),
]);
let mut fixture = SummaryInputFixture::new(MANUAL_ONLY_MANIFEST_TOML, client).await;
let make = |producer: &str, compaction| {
LoggedSystemHistoryEntry::from_input(
LoggedSessionHistoryOrigin::Provided {
producer: producer.into(),
},
None,
SystemInputEnvelope::new(
"identical model context",
HistoryInputMetadata::new(
HistoryInputUsePolicy {
recording_material: InputEvidenceUse::Exclude,
memory_evidence: InputEvidenceUse::Exclude,
observation: InputObservationUse::Hidden,
compaction,
},
Some(
FeatureInputPayload::encode(
"review:owner",
1,
&serde_json::json!({"producer": producer}),
)
.unwrap(),
),
None,
)
.unwrap(),
)
.unwrap(),
)
.to_history_entry()
};
let historical = make("old-reference", InputCompactionUse::ReferenceInput);
let retained = make("retained-context", InputCompactionUse::Retain);
let repeated = make("second-retained-context", InputCompactionUse::Retain);
assert_ne!(historical.metadata.entry_id, retained.metadata.entry_id);
fixture
.restore_seed(
vec![historical.clone(), retained.clone(), repeated.clone()],
false,
)
.await;
let session = fixture.worker.session_id();
let original = fixture.worker.segment_id();
fixture
.worker
.store()
.append(
session,
original,
&LogEntry::Extension {
ts: 2,
domain: "test.fork_drift".into(),
payload: serde_json::json!({}),
},
)
.unwrap();
fixture.worker.run_text("fork checkpoint").await.unwrap();
let fork = fixture.worker.segment_id();
assert_ne!(fork, original);
let forked = replacement_seed(&fixture.worker);
for expected in [&historical, &retained, &repeated] {
assert!(forked.contains(expected));
}
fixture.worker.compact(1_000_000).await.unwrap();
let seed = replacement_seed(&fixture.worker);
let matching: Vec<_> = seed.iter().filter(|e| e.item == retained.item).collect();
assert_eq!(matching.len(), 2);
assert_eq!(
matching[0].metadata, retained.metadata,
"a retained logical entry must not inherit identity/policy/payload from an equal-body reference that was removed"
);
assert_eq!(matching[1].metadata, repeated.metadata);
let compacted = fixture.worker.segment_id();
let reopened = FsStore::new(fixture._store_dir.path()).unwrap();
let records = reopened.read_all(session, compacted).unwrap();
assert!(
matches!(&records[0], LogEntry::AnnotatedSegmentStart { compacted_from: Some(source), .. } if source.segment_id == fork)
);
fixture.worker.run_text("restore checkpoint").await.unwrap();
let records = fixture.worker.store().read_all(session, compacted).unwrap();
let snapshot = public_snapshot::project_current_session_snapshot(&records);
let checkpoint = snapshot
.entries
.iter()
.rev()
.find(|entry| {
matches!(
entry.data,
protocol::SessionSnapshotEntryData::UserInput { .. }
)
})
.unwrap();
fixture
.worker
.rewind_to(checkpoint.entry_id.clone())
.await
.unwrap();
for expected in [&retained, &repeated] {
assert!(
fixture
.worker
.session_history()
.entries()
.iter()
.any(|entry| entry.annotation == expected.metadata
&& entry.item == Item::from(expected.item.clone()))
);
}
assert!(
!fixture
.worker
.session_history()
.entries()
.iter()
.any(|entry| entry.annotation.entry_id == historical.metadata.entry_id)
);
}