sources: chat cursor and purge-tombstone keys are never read as repo names by the forge retire
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
421ace5432
commit
f855f6bad3
3 changed files with 40 additions and 1 deletions
17
crates/huginn-daemon/src/sources/chat.rs
Normal file
17
crates/huginn-daemon/src/sources/chat.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
//! Chat ingest: joined-room history into the shared source collection, beside
|
||||
//! the forge's repos. `refresh_chat` is the only writer of `corpus=chat` points
|
||||
//! and of the cursors object's chat keys.
|
||||
|
||||
/// A chat cursor is the key `chat:<room_id>`; a forge repo name cannot contain
|
||||
/// a colon. The only definition of the prefix.
|
||||
pub const CHAT_CURSOR_PREFIX: &str = "chat:";
|
||||
|
||||
/// A steward's purge tombstone is the key `chat-purge:<room_id>`. The only
|
||||
/// definition of the prefix.
|
||||
pub const CHAT_PURGE_PREFIX: &str = "chat-purge:";
|
||||
|
||||
/// Whether a cursors-object key is chat's (a room cursor or a purge tombstone)
|
||||
/// and not a forge repo name. The only test that tells the two apart.
|
||||
pub fn is_chat_key(key: &str) -> bool {
|
||||
key.starts_with(CHAT_CURSOR_PREFIX) || key.starts_with(CHAT_PURGE_PREFIX)
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
//! the delete and the upserts leaves no cursor, and the repo is ingested whole.
|
||||
//! The cursors object is written whole by this job alone, once per repo.
|
||||
|
||||
pub mod chat;
|
||||
pub mod chunk;
|
||||
pub mod forge;
|
||||
pub mod git;
|
||||
|
|
@ -381,7 +382,7 @@ fn retire<S: SourceIndex>(
|
|||
failed: &mut Vec<String>,
|
||||
) {
|
||||
let listed = |name: &str| catalog.iter().any(|repo| repo.name == name);
|
||||
let mut gone: BTreeSet<String> = cursors.keys().filter(|name| !listed(name)).cloned().collect();
|
||||
let mut gone: BTreeSet<String> = cursors.keys().filter(|name| !chat::is_chat_key(name) && !listed(name)).cloned().collect();
|
||||
if let Ok(entries) = std::fs::read_dir(mirrors) {
|
||||
for entry in entries.flatten() {
|
||||
if let Some(name) = entry.file_name().to_string_lossy().strip_suffix(".git")
|
||||
|
|
|
|||
|
|
@ -974,3 +974,24 @@ fn the_span_bounds_are_inclusive_for_chunks_and_for_messages() {
|
|||
assert!(filter.keeps("!a:x", None, 100, 200) && filter.keeps("!a:x", None, 300, 400));
|
||||
assert!(!filter.keeps("!a:x", None, 100, 199) && !filter.keeps("!a:x", None, 301, 400));
|
||||
}
|
||||
|
||||
/// A chat cursor and a purge tombstone share the forge's cursors object; the
|
||||
/// forge retire never reads either as a repo name, and still retires a repo.
|
||||
#[test]
|
||||
fn chat_cursor_and_purge_tombstone_keys_survive_the_forge_retire() {
|
||||
let (keep, gone) = (Remote::new(), Remote::new());
|
||||
keep.commit(&[("a.rs", b"a\n")], &[]);
|
||||
gone.commit(&[("b.rs", b"b\n")], &[]);
|
||||
let mut rig = Rig::new();
|
||||
rig.converged(&mut FakeForge::of(&[("Keep", &keep), ("Gone", &gone)]));
|
||||
let mut cursors = rig.index.cursors();
|
||||
cursors.insert("chat:!r:s".into(), "1:$e".into());
|
||||
cursors.insert("chat-purge:!r:s".into(), "1..2".into());
|
||||
rig.index.set_cursors(&cursors).unwrap();
|
||||
rig.index.upsert_chat(&[chat_point("!r:s", None, 0, 4, &[("$e", 5)])]).unwrap();
|
||||
rig.converged(&mut FakeForge::of(&[("Keep", &keep)]));
|
||||
let after = rig.index.cursors();
|
||||
assert_eq!(after.keys().map(String::as_str).collect::<Vec<_>>(), ["Keep", "chat-purge:!r:s", "chat:!r:s"], "Gone retired; both chat keys kept");
|
||||
assert!(rig.index.state.lock().unwrap().deletes.iter().all(|(repo, _)| !repo.starts_with("chat")));
|
||||
assert_eq!(rig.index.state.lock().unwrap().chat.len(), 1);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue