Make the interests table the only source of standing interests (step 3)
The OPML file and the profile's ## Interests section become one-time import inputs; the prompt groups by the stored category and the OPML config key is gone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K9PrjtUS16PAQve8D4bHgc
This commit is contained in:
@@ -35,7 +35,6 @@ fn config_check_prints_the_facts_and_exits_zero_without_keys() {
|
||||
"config: ",
|
||||
"database_path: /var/lib/daily-epub/daily-epub.db",
|
||||
"profile_path: data/profile.md",
|
||||
"interests_opml: data/scour-interests.opml",
|
||||
"llm.bulk: deepseek · openai · deepseek-v4-flash",
|
||||
"key MISSING (set DAILY_EPUB_PROVIDERS__DEEPSEEK__API_KEY)",
|
||||
"llm.editor: anthropic · anthropic · claude-opus-5 · effort high · max_daily_usd $3.00",
|
||||
@@ -66,4 +65,8 @@ fn config_check_exits_non_zero_on_an_invalid_config() {
|
||||
let (code, _, stderr) = run("[deepseek]\nmodel = \"x\"\n");
|
||||
assert_ne!(code, 0);
|
||||
assert!(stderr.contains("[providers.deepseek]"), "{stderr}");
|
||||
|
||||
let (code, _, stderr) = run("interests_opml = \"data/scour-interests.opml\"\n");
|
||||
assert_ne!(code, 0);
|
||||
assert!(stderr.contains("interests_opml"), "{stderr}");
|
||||
}
|
||||
|
||||
+10
-7
@@ -21,6 +21,7 @@ use daily_epub::curate::assess::parse_deep_response;
|
||||
use daily_epub::curate::editor::parse_selection_response;
|
||||
use daily_epub::curate::editorial::BriefResponse;
|
||||
use daily_epub::curate::profile;
|
||||
use daily_epub::interests;
|
||||
|
||||
fn repo(rel: &str) -> PathBuf {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR")).join(rel)
|
||||
@@ -183,13 +184,14 @@ fn stage_c_fixture_parses_into_the_brief() {
|
||||
assert!(value.get("section_intros").is_none());
|
||||
}
|
||||
|
||||
/// The taste profile is seeded from this file; a broken export would silently
|
||||
/// gut the system prompt (§3.6a).
|
||||
/// The importer relies on this file; a broken export would silently lose
|
||||
/// standing interests (§3.6a).
|
||||
#[test]
|
||||
fn scour_opml_still_yields_the_interest_list() {
|
||||
let interests = profile::parse_interests(&repo("data/scour-interests.opml"))
|
||||
.expect("the shipped OPML must parse");
|
||||
let unique: BTreeSet<String> = interests.iter().map(|n| n.to_lowercase()).collect();
|
||||
let raw = std::fs::read_to_string(repo("data/scour-interests.opml"))
|
||||
.expect("the shipped OPML must be readable");
|
||||
let names = interests::parse_opml(&raw);
|
||||
let unique: BTreeSet<String> = names.iter().map(|n| n.to_lowercase()).collect();
|
||||
|
||||
assert!(
|
||||
unique.len() > 180,
|
||||
@@ -206,7 +208,7 @@ fn scour_opml_still_yields_the_interest_list() {
|
||||
assert!(unique.contains(expected), "{expected} disappeared");
|
||||
}
|
||||
assert!(
|
||||
!interests.iter().any(|n| n.contains("token=")),
|
||||
!names.iter().any(|n| n.contains("token=")),
|
||||
"interest names must not leak the Scour token"
|
||||
);
|
||||
|
||||
@@ -217,9 +219,10 @@ fn scour_opml_still_yields_the_interest_list() {
|
||||
"/data/profile.md"
|
||||
)))
|
||||
.expect("profile file");
|
||||
let grouped = vec![("Imported interests".to_string(), names)];
|
||||
let document = profile::build(
|
||||
&profile_file.body,
|
||||
&interests,
|
||||
&grouped,
|
||||
profile::NO_LEARNED_ADJUSTMENTS,
|
||||
&[],
|
||||
60,
|
||||
|
||||
Reference in New Issue
Block a user