fix: observe original presence through saved legacy Journal links

This commit is contained in:
2026-10-17 00:10:35 +09:00
parent b544e53bd7
commit 6645ea0a47
2 changed files with 73 additions and 1 deletions
@@ -404,6 +404,64 @@ fn original_source_metadata_failure_keeps_saved_journal_readable_and_does_not_cl
}
}
#[test]
fn legacy_segment_entry_links_reuse_saved_session_for_presence_without_mutating_provenance() {
let mut f = Fixture::new();
let mut candidate = f.candidate("legacy-presence", &[0, 1]);
for source in &mut candidate.source_refs {
source.session_id = None;
}
let candidate = f.store().stage_candidate(candidate).unwrap();
let selector = f.apply(&candidate);
let entry = f.fixed("first", 1, vec![]);
f.links(&selector, true);
let before = f.snapshot();
f.reopen();
for request in [
MemoryJournalRequest::Links {
selector: selector.clone(),
},
MemoryJournalRequest::Read {
selector: selector.clone(),
source_index: 1,
journal_id: f.journal_id.clone(),
entry_id: entry.clone(),
expected_token: None,
byte_offset: 0,
max_bytes: 4096,
},
] {
let mut calls = Vec::new();
let response = f
.store()
.execute_memory_journal_with_source_presence(&f.host, request, |session| {
calls.push(session.to_owned());
Some(false)
})
.unwrap();
assert_eq!(calls, ["session"]);
match response {
MemoryJournalResponse::Links(page) => {
assert_eq!(page.sources.len(), 2);
assert!(
page.sources
.iter()
.all(|s| s.original_source == OriginalSourceAvailability::Missing)
);
}
MemoryJournalResponse::Read(read) => {
assert_eq!(
read.source.original_source,
OriginalSourceAvailability::Missing
);
assert_eq!(read.page.unwrap().text, "Journal context for first");
}
_ => panic!("saved provenance result required"),
}
assert_eq!(f.snapshot(), before);
}
}
#[test]
fn multiple_segment_anchors_map_to_fixed_intervals_and_subject_crossing_is_denied() {
let f = Fixture::new();
+15 -1
View File
@@ -7,6 +7,7 @@ use super::memory_links::{
MemoryJournalRequest, MemoryJournalResponse, OriginalSourceAvailability,
};
use super::{JournalHostContext, JournalResult, SubjektivStore};
use rusqlite::{OptionalExtension, params};
impl SubjektivStore {
/// Executes the saved-reference operation first, then annotates original-source absence using
@@ -39,7 +40,20 @@ impl SubjektivStore {
let source = candidate
.as_ref()
.and_then(|c| c.source_refs.get(link.source_index));
let session = source.and_then(|s| s.session_id.as_deref());
// Legacy anchors may lack a Session ID. A connected edge already resolved it
// from immutable attribution and stable Segment/entry identity; reuse that saved
// identity rather than fetching history or modifying the original candidate.
let saved_session: Option<String> = self.database.try_with_connection(|tx| {
Ok::<_, super::JournalError>(tx.query_row(
"SELECT source_session_id FROM memory_journal_links
WHERE subject_id=?1 AND candidate_id=?2 AND source_index=?3 AND reason='linked'",
params![host.subject_id, selector.candidate_id, link.source_index],
|row| row.get(0),
).optional()?)
})?;
let session = saved_session
.as_deref()
.or_else(|| source.and_then(|s| s.session_id.as_deref()));
let observed = if let Some(session) = session {
if let Some(value) = observations.get(session) {
*value