use super::authority::ArchiveAuthority;
use super::register::{self, Register};
use crate::core::context_admission::carrier::Withheld;
use crate::core::context_admission::stored::{self, Lineage, SourceRef, StoredBinding};
const REGISTER: Register = Register {
name: "bindings",
shard_entries: 128,
max_age_seconds: 180 * 24 * 60 * 60,
};
#[derive(Debug, Clone, Copy)]
pub enum Item<'a> {
KnowledgeFact {
project: &'a str,
category: &'a str,
key: &'a str,
},
SessionFinding {
session: &'a str,
file: Option<&'a str>,
},
}
impl Item<'_> {
fn origin(&self) -> &'static str {
match self {
Self::KnowledgeFact { .. } => "knowledge:fact",
Self::SessionFinding { .. } => "session:finding",
}
}
fn key(&self, text: &str) -> String {
let parts: [&str; 4] = match *self {
Self::KnowledgeFact {
project,
category,
key,
} => ["fact", project, category, key],
Self::SessionFinding { session, file } => {
["finding", session, file.unwrap_or_default(), text]
}
};
let mut hash = blake3::Hasher::new();
hash.update(b"leanctx-item-binding-v1");
for part in parts {
hash.update(b"\0");
hash.update(&(part.len() as u64).to_le_bytes());
hash.update(part.as_bytes());
}
hash.finalize().to_hex().to_string()
}
}
pub fn sources_at<S: AsRef<str>>(project: &str, paths: &[S], tool: &str) -> Option<Vec<SourceRef>> {
if paths.is_empty() {
return None;
}
paths
.iter()
.map(|path| {
let path = std::path::Path::new(project).join(path.as_ref());
SourceRef::read(path.to_str()?, project, tool)
.ok()
.map(|(_, source)| source)
})
.collect()
}
pub(crate) fn sources_of_call(capture: &ArchiveAuthority) -> Option<Vec<SourceRef>> {
let project = capture.project();
capture
.sources()?
.into_iter()
.map(|(path, tool, admitted_digest)| {
SourceRef::read_as_admitted(path, project, tool, admitted_digest)
})
.collect()
}
pub fn bind(item: &Item<'_>, text: &str, project: &str, sources: Vec<SourceRef>) -> bool {
prepare(item, text, project, sources).is_some_and(|binding| write(item, text, binding))
}
pub(crate) fn prepare(
item: &Item<'_>,
text: &str,
project: &str,
sources: Vec<SourceRef>,
) -> Option<StoredBinding> {
if sources.is_empty() {
return None;
}
let lineage = Lineage::of(sources).ok()?;
let (admitted, binding) =
stored::admit_and_bind(text, item.origin(), Some(project), lineage).ok()?;
(admitted == text).then_some(binding)
}
fn write(item: &Item<'_>, text: &str, binding: StoredBinding) -> bool {
REGISTER.update::<StoredBinding>(&item.key(text), register::now(), |_| Some(binding))
}
pub struct Carried(StoredBinding);
impl Carried {
pub fn commit(self, item: &Item<'_>, text: &str) -> bool {
self.0.binds(text) && write(item, text, self.0)
}
}
fn held_sources(
binding: &StoredBinding,
text: &str,
reader_project: Option<&str>,
) -> Result<Option<(String, Vec<SourceRef>)>, Withheld> {
stored::serve_stored(text, Some(binding), reader_project)?;
let Some((project, files)) = binding.file_sources() else {
return Ok(None);
};
let sources = files
.into_iter()
.map(|(path, tool, admitted_digest)| {
SourceRef::read_as_admitted(path, project, tool, admitted_digest)
.ok_or_else(Withheld::source_unavailable)
})
.collect::<Result<Vec<_>, _>>()?;
Ok(Some((project.to_owned(), sources)))
}
pub(crate) fn carry(
derived: &Item<'_>,
text: &str,
origins: &[(Item<'_>, &str)],
reader_project: Option<&str>,
) -> Result<Option<Carried>, Withheld> {
let enforced = stored::source_authority_required();
let mut project: Option<String> = None;
let mut sources = Vec::new();
let mut bound = !origins.is_empty();
for (origin, origin_text) in origins {
let Some(binding) = binding_of(origin, origin_text) else {
bound = false;
continue;
};
let held = held_sources(&binding, origin_text, reader_project).and_then(|held| {
let foreign = held.as_ref().is_some_and(|(root, _)| {
project
.as_deref()
.is_some_and(|joined| joined != root.as_str())
});
if foreign {
Err(Withheld::no_source_authority())
} else {
Ok(held)
}
});
match held {
Ok(Some((root, files))) => {
project = Some(root);
sources.extend(files);
}
Err(withheld) if enforced => return Err(withheld),
Ok(None) | Err(_) => bound = false,
}
}
let Some(project) = project.filter(|_| bound) else {
return Ok(None);
};
carried(derived, text, &project, sources, enforced)
}
fn carried(
derived: &Item<'_>,
text: &str,
project: &str,
sources: Vec<SourceRef>,
enforced: bool,
) -> Result<Option<Carried>, Withheld> {
match prepare(derived, text, project, sources) {
Some(binding) => Ok(Some(Carried(binding))),
None if enforced => Err(Withheld::no_source_authority()),
None => Ok(None),
}
}
pub(crate) fn carry_from(
derived: &Item<'_>,
text: &str,
origin: &StoredBinding,
origin_text: &str,
reader_project: Option<&str>,
) -> Result<Option<Carried>, Withheld> {
let enforced = stored::source_authority_required();
let held = match held_sources(origin, origin_text, reader_project) {
Ok(held) => held,
Err(withheld) if enforced => return Err(withheld),
Err(_) => None,
};
let Some((project, sources)) = held else {
return Ok(None);
};
carried(derived, text, &project, sources, enforced)
}
pub fn held(
item: &Item<'_>,
text: &str,
reader_project: Option<&str>,
) -> Result<Option<(String, Vec<SourceRef>)>, Withheld> {
let Some(binding) = binding_of(item, text) else {
return Ok(None);
};
crate::core::archive::authority::without_capture(|| {
held_sources(&binding, text, reader_project)
})
}
pub fn holds_in_checkout(item: &Item<'_>, text: &str, checkout: &str) -> bool {
use std::path::{Component, Path, PathBuf};
let Some(binding) = binding_of(item, text) else {
return false;
};
let Some((bound_root, files)) = binding.file_sources() else {
return false;
};
let canonical = |path: &str| crate::core::pathutil::safe_canonicalize_or_self(Path::new(path));
let bound_roots = [PathBuf::from(bound_root), canonical(bound_root)];
let relative = |path: &str| -> Option<PathBuf> {
let candidates = [PathBuf::from(path), canonical(path)];
let relative = candidates.iter().find_map(|candidate| {
bound_roots
.iter()
.find_map(|root| candidate.strip_prefix(root).ok())
})?;
relative
.components()
.all(|part| matches!(part, Component::Normal(_)))
.then(|| relative.to_path_buf())
};
let checkout_root = canonical(checkout);
let Some(checkout_str) = checkout_root.to_str() else {
return false;
};
let held = crate::core::archive::authority::without_capture(|| {
files
.iter()
.map(|&(path, tool, digest)| {
let relative = relative(path)?;
let target = checkout_root.join(&relative);
SourceRef::read_as_admitted(target.to_str()?, checkout_str, tool, digest)?;
Some((relative, tool, digest))
})
.collect::<Option<Vec<_>>>()
});
let Some(held) = held else {
return false;
};
for (relative, tool, digest) in held {
if let Some(source) = ArchiveAuthority::admitted_source(
&checkout_root,
relative.to_str().unwrap_or_default(),
tool,
digest,
) {
source.publish();
}
}
true
}
fn binding_of(item: &Item<'_>, text: &str) -> Option<StoredBinding> {
REGISTER
.read::<StoredBinding>(&item.key(text), register::now())
.filter(|binding| binding.binds(text))
}
pub fn serve(item: &Item<'_>, text: &str, reader_project: Option<&str>) -> Result<(), Withheld> {
if !stored::source_authority_required() {
return Ok(());
}
let binding = binding_of(item, text);
if let Some(binding) = &binding {
stored::serve_stored(text, Some(binding), reader_project)?;
}
stored::publish_served(binding.as_ref());
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::archive::delivered::LineageRefusal;
use crate::core::context_admission::stored::lineage_support::{
deliver, governed, project, reusable,
};
use crate::core::policy::runtime::TestPolicyOverride;
fn fact<'a>(project: &'a str, key: &'a str) -> Item<'a> {
Item::KnowledgeFact {
project,
category: "architecture",
key,
}
}
fn recall(root: &str, item: &Item<'_>, value: &str) -> (String, usize) {
deliver(root, || match serve(item, value, Some(root)) {
Ok(()) => format!("[architecture] {value}"),
Err(withheld) => withheld.notice(),
})
}
#[test]
fn a_fact_is_bound_to_the_sources_named_at_its_write_only() {
let _data = governed();
let _community = TestPolicyOverride::set(None);
let (_directory, root) = project();
std::fs::write(format!("{root}/auth.rs"), "pub fn verify_rs256() {}\n").expect("source");
let value = "tokens are verified with RS256";
let bound_fact = fact(&root, "token-check");
let sources = sources_at(&root, &["auth.rs"], "ctx_knowledge").expect("readable source");
assert!(bind(&bound_fact, value, &root, sources));
let (reply, recorded) = recall(&root, &bound_fact, value);
assert!(reply.contains("RS256"), "{reply}");
assert_eq!(recorded, 1);
assert_eq!(reusable(&root, &reply), Ok(()));
let plain = fact(&root, "typed-note");
assert!(sources_at::<&str>(&root, &[], "ctx_knowledge").is_none());
assert!(!bind(&plain, "typed by the agent", &root, Vec::new()));
let (typed, recorded) = recall(&root, &plain, "typed by the agent");
assert!(typed.contains("typed by the agent"), "{typed}");
assert_eq!(recorded, 0);
assert_eq!(reusable(&root, &typed), Err(LineageRefusal::Unknown));
let (replaced, recorded) = recall(&root, &bound_fact, "tokens are opaque now");
assert!(replaced.contains("opaque"), "{replaced}");
assert_eq!(recorded, 0);
std::fs::write(format!("{root}/plan.md"), "*** TOP SECRET ***\nlaunch\n").expect("marked");
assert!(sources_at(&root, &["auth.rs", "plan.md"], "ctx_knowledge").is_none());
assert!(sources_at(&root, &["missing.rs"], "ctx_knowledge").is_none());
assert!(sources_at(&root, &["../outside.rs"], "ctx_knowledge").is_none());
std::fs::remove_file(format!("{root}/auth.rs")).expect("delete");
assert_eq!(
reusable(&root, &reply),
Err(LineageRefusal::SourceNoLongerAdmitted)
);
let (withheld, recorded) = recall(&root, &bound_fact, value);
assert!(!withheld.contains("RS256"), "{withheld}");
assert_eq!(recorded, 0);
}
#[test]
fn a_held_check_leaves_the_capture_untouched() {
use crate::core::archive::authority::capture_sync;
use crate::core::context_admission::stored::lineage_support::scoped;
let _data = governed();
let _community = TestPolicyOverride::set(None);
let (_directory, root) = project();
for name in ["auth.rs", "pool.rs"] {
std::fs::write(format!("{root}/{name}"), format!("pub fn {name}() {{}}\n"))
.expect("source");
}
let (holding, lapsed) = (fact(&root, "token-check"), fact(&root, "pool-size"));
for (item, value, file) in [
(&holding, "tokens are verified with RS256", "auth.rs"),
(&lapsed, "the pool holds eight leases", "pool.rs"),
] {
let sources = sources_at(&root, &[file], "ctx_knowledge").expect("readable source");
assert!(bind(item, value, &root, sources));
}
std::fs::remove_file(format!("{root}/pool.rs")).expect("delete");
let ((first, second), capture) = scoped(&root, || {
capture_sync(|| {
(
held(&holding, "tokens are verified with RS256", Some(&root)),
held(&lapsed, "the pool holds eight leases", Some(&root)),
)
})
});
let (project, sources) = first.expect("holds").expect("bound to a file");
assert_eq!(project, root);
assert_eq!(sources.len(), 1);
assert!(second.is_err(), "its source is gone");
assert!(
capture.is_none(),
"a check publishes nothing, held or refused"
);
}
#[test]
fn an_item_without_a_register_entry_is_originless() {
let data = governed();
let _community = TestPolicyOverride::set(None);
let (_directory, root) = project();
let finding = Item::SessionFinding {
session: "session-1",
file: Some("src/lib.rs"),
};
let (reply, recorded) = deliver(&root, || {
serve(&finding, "an old finding", Some(&root))
.expect("originless items are not refused");
"finding: an old finding".to_owned()
});
assert_eq!(recorded, 0);
assert_eq!(reusable(&root, &reply), Err(LineageRefusal::Unknown));
assert!(!data.path().join("archives").join("bindings").exists());
}
#[test]
fn a_finding_is_bound_to_the_sources_of_the_call_that_produced_it() {
let _data = governed();
let _community = TestPolicyOverride::set(None);
let (_directory, root) = project();
let path = format!("{root}/build.rs");
std::fs::write(&path, "fn main() { panic!(\"linker\") }\n").expect("source");
let admitted =
crate::core::rooted_read::read_file_for_tool_rooted(&path, &root, "ctx_read")
.expect("admitted read");
let capture =
ArchiveAuthority::file(std::path::Path::new(&root), &path, "ctx_read", &admitted)
.expect("protection is active: the read is bound");
let finding = Item::SessionFinding {
session: "session-1",
file: Some("build.rs"),
};
let summary = "build.rs panics in the linker step";
let sources = sources_of_call(&capture).expect("complete source set");
assert!(bind(&finding, summary, &root, sources));
let (reply, recorded) = deliver(&root, || {
serve(&finding, summary, Some(&root)).expect("bound and admitted");
format!("finding: {summary}")
});
assert_eq!(recorded, 1);
assert_eq!(reusable(&root, &reply), Ok(()));
std::fs::write(&path, "fn main() {}\n").expect("the source changes");
assert_eq!(
reusable(&root, &reply),
Err(LineageRefusal::SourceNoLongerAdmitted)
);
}
}