use anyhow::Result;
use rusqlite::{params, Connection};
use super::{compile_project_rules, run_compile_rules_job};
use crate::db::{self, test_support::ScopedTestDataDir};
use crate::rules::store::{artifact_path_for_project, load_artifact_fail_open, ArtifactLoad};
use crate::rules::{RuleAction, RulePredicate};
use crate::runtime_config::RuleCompilationConfig;
const PROJECT: &str = "/tmp/remem";
fn config(min: i64) -> RuleCompilationConfig {
RuleCompilationConfig {
enabled: true,
min_reinforcement: min,
}
}
pub(super) struct PrefSpec<'a> {
pub(super) id: i64,
pub(super) project: &'a str,
pub(super) content: &'a str,
pub(super) memory_type: &'a str,
pub(super) status: &'a str,
pub(super) scope: &'a str,
pub(super) owner_scope: Option<&'a str>,
pub(super) owner_key: Option<&'a str>,
pub(super) updated_at: i64,
pub(super) expires_at: Option<i64>,
pub(super) reinforcement: i64,
pub(super) machine_checkable: i64,
pub(super) risk_class: &'a str,
pub(super) review_status: &'a str,
pub(super) source_trust_class: &'a str,
}
impl Default for PrefSpec<'_> {
fn default() -> Self {
Self {
id: 1,
project: PROJECT,
content: "Use bun, not npm",
memory_type: "preference",
status: "active",
scope: "project",
owner_scope: Some("repo"),
owner_key: None,
updated_at: 100,
expires_at: None,
reinforcement: 3,
machine_checkable: 1,
risk_class: "low",
review_status: "auto_promoted",
source_trust_class: "local_tool_output",
}
}
}
pub(super) fn global_default() -> PrefSpec<'static> {
PrefSpec {
project: "/tmp/global-source",
scope: "global",
owner_scope: Some("user"),
owner_key: Some("user:default"),
..Default::default()
}
}
pub(super) fn insert_pref(conn: &Connection, spec: &PrefSpec<'_>) -> Result<()> {
conn.execute(
"INSERT INTO memory_candidates
(id, scope, memory_type, topic_key, text, evidence_event_ids,
confidence, risk_class, review_status, created_at_epoch, updated_at_epoch,
source_trust_class)
VALUES (?1, ?2, 'preference', ?3, ?4, '[1]',
0.95, ?5, ?6, 1, ?7, ?8)",
params![
spec.id,
spec.scope,
format!("preference-{}", spec.id),
spec.content,
spec.risk_class,
spec.review_status,
spec.updated_at,
spec.source_trust_class,
],
)?;
let owner_key = spec.owner_key.or(spec.owner_scope.map(|_| PROJECT));
conn.execute(
"INSERT INTO memories
(id, project, title, content, memory_type, created_at_epoch, updated_at_epoch,
status, scope, owner_scope, owner_key, expires_at_epoch, source_candidate_id,
source_trust_class)
VALUES (?1, ?2, 'pref', ?3, ?11, 1, ?4, ?5, ?6, ?7, ?8, ?9, ?1, ?10)",
params![
spec.id,
spec.project,
spec.content,
spec.updated_at,
spec.status,
spec.scope,
spec.owner_scope,
owner_key,
spec.expires_at,
spec.source_trust_class,
spec.memory_type,
],
)?;
conn.execute(
"INSERT INTO memory_preference_reinforcements
(memory_id, reinforcement_count, source_evidence,
last_reinforced_at_epoch, created_at_epoch, updated_at_epoch,
machine_checkable, risk_class)
VALUES (?1, ?2, NULL, ?3, ?3, ?3, ?4, ?5)",
params![
spec.id,
spec.reinforcement,
spec.updated_at,
spec.machine_checkable,
spec.risk_class,
],
)?;
Ok(())
}
pub(super) fn compile(conn: &Connection) -> Result<Vec<String>> {
let artifact = compile_project_rules(conn, PROJECT, config(3))?;
Ok(artifact.rules.iter().map(|r| r.rule_id.clone()).collect())
}
#[test]
fn eligible_preference_compiles_with_warn_default() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-eligible");
let conn = db::open_db()?;
insert_pref(&conn, &PrefSpec::default())?;
let artifact = compile_project_rules(&conn, PROJECT, config(3))?;
assert_eq!(artifact.rules.len(), 1);
let rule = &artifact.rules[0];
assert_eq!(rule.rule_id, "pref-1-1");
assert_eq!(rule.source_memory_id, 1);
assert_eq!(rule.reinforcement_count, 3);
assert_eq!(rule.action, RuleAction::Warn);
assert!(!rule.override_state.disabled);
assert!(rule.override_state.action_override.is_none());
assert!(matches!(rule.predicate, RulePredicate::CommandRegex { .. }));
match &rule.predicate {
RulePredicate::CommandRegex { message, .. } => {
assert_eq!(
message,
"Command violates a compiled package-manager preference"
)
}
RulePredicate::CommitTrailerForbidden { .. }
| RulePredicate::GitPushForceForbidden { .. } => unreachable!(),
}
let serialized = serde_json::to_string(&artifact)?;
assert!(!serialized.contains("Use bun, not npm"));
Ok(())
}
#[test]
fn multiple_forbidden_trailers_compile_to_stable_rules() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-multiple-trailers");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
content: "Do not add AI-generated-by or Co-authored-by trailers to commits",
..Default::default()
},
)?;
let artifact = compile_project_rules(&conn, PROJECT, config(3))?;
let ids = artifact
.rules
.iter()
.map(|rule| rule.rule_id.as_str())
.collect::<Vec<_>>();
assert_eq!(ids, vec!["pref-1-1", "pref-1-2"]);
assert!(artifact.rules.iter().all(|rule| matches!(
&rule.predicate,
RulePredicate::CommitTrailerForbidden { message, .. }
if message == "Commit message violates a compiled trailer preference"
)));
Ok(())
}
#[test]
fn forbidden_force_push_compiles_to_structural_v2_predicate() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-forbidden-force-push");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
content: "Never run git push --force",
..Default::default()
},
)?;
let artifact = compile_project_rules(&conn, PROJECT, config(3))?;
assert_eq!(artifact.rules.len(), 1);
assert!(matches!(
&artifact.rules[0].predicate,
RulePredicate::GitPushForceForbidden { message }
if message == "Command violates a compiled forbidden-command preference"
));
Ok(())
}
#[test]
fn below_threshold_preference_is_not_compiled() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-below-threshold");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
reinforcement: 2,
..Default::default()
},
)?;
assert!(compile(&conn)?.is_empty());
Ok(())
}
#[test]
fn inactive_preference_is_not_compiled() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-inactive");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
status: "stale",
..Default::default()
},
)?;
assert!(compile(&conn)?.is_empty());
Ok(())
}
#[test]
fn expired_preference_is_not_compiled() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-expired");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
expires_at: Some(1),
..Default::default()
},
)?;
assert!(compile(&conn)?.is_empty());
Ok(())
}
#[test]
fn unresolved_owner_scope_is_not_compiled() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-no-owner");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
owner_scope: None,
..Default::default()
},
)?;
assert!(compile(&conn)?.is_empty());
Ok(())
}
#[test]
fn ambiguous_preference_is_not_compiled() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-ambiguous");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
content: "I like clean code",
machine_checkable: 0,
..Default::default()
},
)?;
assert!(compile(&conn)?.is_empty());
Ok(())
}
#[test]
fn high_risk_preference_is_not_compiled() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-high-risk");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
risk_class: "high",
..Default::default()
},
)?;
assert!(compile(&conn)?.is_empty());
Ok(())
}
#[test]
fn unreviewed_preference_is_not_compiled() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-unreviewed");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
review_status: "pending_review",
..Default::default()
},
)?;
assert!(compile(&conn)?.is_empty());
Ok(())
}
#[test]
fn machine_checkable_state_drift_fails_compilation() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-classification-drift");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
content: "I like clean code",
machine_checkable: 1,
..Default::default()
},
)?;
let error = compile_project_rules(&conn, PROJECT, config(3))
.expect_err("canonical machine_checkable drift must fail closed");
assert!(error.to_string().contains("machine_checkable"), "{error:#}");
Ok(())
}
#[test]
fn non_low_risk_preference_is_not_compiled() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-risk");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
risk_class: "medium",
..Default::default()
},
)?;
assert!(compile(&conn)?.is_empty());
Ok(())
}
#[test]
fn untrusted_preference_is_not_compiled() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-trust");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
source_trust_class: "external_content",
..Default::default()
},
)?;
assert!(compile(&conn)?.is_empty());
Ok(())
}
#[test]
fn suppressed_source_removes_rule() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-suppressed");
let conn = db::open_db()?;
insert_pref(&conn, &PrefSpec::default())?;
assert_eq!(compile(&conn)?.len(), 1);
conn.execute(
"INSERT INTO memory_suppressions
(target_kind, target_id, reason, actor, status, created_at_epoch, updated_at_epoch)
VALUES ('memory', 1, 'noisy', 'user', 'active', 1, 1)",
[],
)?;
assert!(compile(&conn)?.is_empty());
Ok(())
}
#[test]
fn deleted_source_removes_rule() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-deleted");
let conn = db::open_db()?;
insert_pref(&conn, &PrefSpec::default())?;
assert_eq!(compile(&conn)?.len(), 1);
conn.execute("DELETE FROM memories WHERE id = 1", [])?;
assert!(compile(&conn)?.is_empty());
Ok(())
}
#[test]
fn override_merge_applies_disable_and_action() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-override");
let conn = db::open_db()?;
insert_pref(&conn, &PrefSpec::default())?;
conn.execute(
"INSERT INTO preference_rule_overrides
(project, rule_id, disabled, action_override, updated_at_epoch)
VALUES (?1, 'pref-1-1', 1, 'block', 1)",
params![PROJECT],
)?;
let artifact = compile_project_rules(&conn, PROJECT, config(3))?;
assert_eq!(artifact.rules.len(), 1);
let rule = &artifact.rules[0];
assert_eq!(rule.action, RuleAction::Warn);
assert!(rule.override_state.disabled);
assert_eq!(rule.override_state.action_override, Some(RuleAction::Block));
Ok(())
}
#[test]
fn conflicting_predicates_keep_newest_source() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-conflict");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
id: 1,
content: "Use bun, not npm",
updated_at: 100,
..Default::default()
},
)?;
insert_pref(
&conn,
&PrefSpec {
id: 2,
content: "Prefer pnpm over npm",
updated_at: 200,
..Default::default()
},
)?;
let ids = compile(&conn)?;
assert_eq!(ids, vec!["pref-2-1".to_string()]);
Ok(())
}
#[test]
fn project_rule_precedes_newer_global_conflict() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-project-over-global");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
id: 1,
content: "Use bun, not npm",
updated_at: 100,
..Default::default()
},
)?;
insert_pref(
&conn,
&PrefSpec {
id: 2,
project: "/tmp/global-source",
content: "Use npm, not bun",
scope: "global",
owner_scope: Some("user"),
owner_key: Some("user:default"),
updated_at: 200,
..Default::default()
},
)?;
assert_eq!(compile(&conn)?, vec!["pref-1-1".to_string()]);
Ok(())
}
#[test]
fn rerouted_project_rule_precedes_newer_global_conflict() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-rerouted-project-over-global");
let conn = db::open_db()?;
insert_pref(
&conn,
&PrefSpec {
id: 1,
project: "/tmp/original-authority",
content: "Use bun, not npm",
updated_at: 100,
..Default::default()
},
)?;
conn.execute(
"UPDATE memories
SET target_project = ?1, owner_scope = 'repo', owner_key = ?1
WHERE id = 1",
[PROJECT],
)?;
insert_pref(
&conn,
&PrefSpec {
id: 2,
project: "/tmp/global-source",
content: "Use npm, not bun",
scope: "global",
owner_scope: Some("user"),
owner_key: Some("user:default"),
updated_at: 200,
..Default::default()
},
)?;
assert_eq!(compile(&conn)?, vec!["pref-1-1".to_string()]);
Ok(())
}
#[test]
fn pure_compile_does_not_write_artifact() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-no-write");
let conn = db::open_db()?;
insert_pref(&conn, &PrefSpec::default())?;
compile_project_rules(&conn, PROJECT, config(3))?;
let data_dir = db::absolute_data_dir()?;
let path = artifact_path_for_project(&data_dir, PROJECT);
assert!(
!path.exists(),
"pure compile must not write the artifact file"
);
Ok(())
}
#[test]
fn worker_job_writes_artifact_and_records_diagnostic() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-worker-write");
crate::runtime_config::init_config()?;
crate::runtime_config::set_config_value("rule_compilation.enabled", "true")?;
let conn = db::open_db()?;
insert_pref(&conn, &PrefSpec::default())?;
drop(conn);
let outcome = run_compile_rules_job(PROJECT)?.expect("compilation should run when enabled");
assert_eq!(outcome.rule_count, 1);
let loaded = load_artifact_fail_open(&outcome.artifact_path);
match loaded {
ArtifactLoad::Loaded(artifact) => assert_eq!(artifact.rules.len(), 1),
other => panic!("expected loaded artifact, got {other:?}"),
}
let conn = db::open_db()?;
let (status, rule_count): (String, i64) = conn.query_row(
"SELECT status, rule_count FROM preference_rule_diagnostics
WHERE project = ?1 AND event_kind = 'compile'
ORDER BY id DESC LIMIT 1",
params![PROJECT],
|row| Ok((row.get(0)?, row.get(1)?)),
)?;
assert_eq!(status, "ok");
assert_eq!(rule_count, 1);
Ok(())
}
#[test]
fn success_diagnostic_failure_restores_previous_artifact() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-success-diagnostic-rollback");
crate::runtime_config::init_config()?;
crate::runtime_config::set_config_value("rule_compilation.enabled", "true")?;
let conn = db::open_db()?;
insert_pref(&conn, &PrefSpec::default())?;
drop(conn);
let first = run_compile_rules_job(PROJECT)?.expect("initial compilation should succeed");
let previous = std::fs::read(&first.artifact_path)?;
let conn = db::open_db()?;
conn.execute(
"UPDATE memories
SET content = 'Use npm, not yarn', updated_at_epoch = updated_at_epoch + 1
WHERE id = 1",
[],
)?;
conn.execute_batch(
"CREATE TRIGGER fail_success_compile_diagnostic
BEFORE INSERT ON preference_rule_diagnostics
WHEN NEW.event_kind = 'compile' AND NEW.status = 'ok'
BEGIN
SELECT RAISE(ABORT, 'forced success diagnostic failure');
END;",
)?;
drop(conn);
let error = run_compile_rules_job(PROJECT)
.expect_err("success diagnostic failure must fail artifact publication");
assert!(
format!("{error:#}").contains("forced success diagnostic failure"),
"{error:#}"
);
assert_eq!(std::fs::read(&first.artifact_path)?, previous);
Ok(())
}
#[test]
fn success_diagnostic_failure_removes_new_artifact() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-new-artifact-diagnostic-rollback");
crate::runtime_config::init_config()?;
crate::runtime_config::set_config_value("rule_compilation.enabled", "true")?;
let conn = db::open_db()?;
insert_pref(&conn, &PrefSpec::default())?;
conn.execute_batch(
"CREATE TRIGGER fail_success_compile_diagnostic
BEFORE INSERT ON preference_rule_diagnostics
WHEN NEW.event_kind = 'compile' AND NEW.status = 'ok'
BEGIN
SELECT RAISE(ABORT, 'forced success diagnostic failure');
END;",
)?;
drop(conn);
let artifact_path = artifact_path_for_project(db::absolute_data_dir()?, PROJECT);
assert!(!artifact_path.exists());
let error = run_compile_rules_job(PROJECT)
.expect_err("success diagnostic failure must retract a new artifact");
assert!(
format!("{error:#}").contains("forced success diagnostic failure"),
"{error:#}"
);
assert!(!artifact_path.exists());
Ok(())
}
#[test]
fn disabled_config_skips_compilation() -> Result<()> {
let _dir = ScopedTestDataDir::new("compile-disabled");
crate::runtime_config::init_config()?;
let conn = db::open_db()?;
insert_pref(&conn, &PrefSpec::default())?;
drop(conn);
assert!(run_compile_rules_job(PROJECT)?.is_none());
let data_dir = db::absolute_data_dir()?;
assert!(!artifact_path_for_project(&data_dir, PROJECT).exists());
Ok(())
}