Fix world briefing & add LLM enrichment, colophon fix, x4 cover fix

- World Briefing now starts with yesterday, preserves complete nested
event hierarchies and Wikipedia links, fetches deduplicated article
leads concurrently, and adds stable-ID summaries plus a daily overview.
- Enrichment runs after normal editorial work and degrades safely with
report warnings.
- Colophon facts now use X4-safe block paragraphs.
- Standard cover remains 1200×1600 RGB PNG.
- X4 cover is a 480×800 baseline RGB JPEG with consistent XHTML and OPF
declarations.
- Added CrossInk cache-clearing guidance to README.md.
- Worked around an epub-builder duplicate XML-ID defect discovered by
validation.
This commit is contained in:
2026-08-15 21:13:56 +00:00
parent 9e27a32fb6
commit 38fb493989
12 changed files with 897 additions and 319 deletions
+20 -11
View File
@@ -2,8 +2,8 @@
//!
//! ```text
//! Miniflux ingest ─▶ dedupe ─▶ extraction ─▶ persist ─▶ social enrichment
//! ─▶ pre-filter ─▶ LLM scoring ─▶ selection ─▶ comments ─▶ world briefing
//! ─▶ editorial ─▶ EPUB (standard + X4) ─▶ XTC ─▶ publish ─▶ report
//! ─▶ pre-filter ─▶ LLM scoring ─▶ selection ─▶ comments ─▶ editorial
//! ─▶ world briefing ─▶ EPUB (standard + X4) ─▶ XTC ─▶ publish ─▶ report
//! ```
//!
//! Failure policy (notes §3):
@@ -422,15 +422,7 @@ async fn run_stages(
report.counts.discussions = comments::fetch_all(&http, &mut lineup.picks).await as i64;
report.timings.record("comments", elapsed_ms(stage));
// --- Stage 9: world briefing (§3.8) — non-fatal by construction ---
let stage = Timestamp::now();
let world_briefing = world::fetch_optional(&http, date, config.world_briefing).await;
if config.world_briefing && world_briefing.is_none() {
report.warn("the world briefing was unavailable; the section is omitted");
}
report.timings.record("world", elapsed_ms(stage));
// --- Stage 10: editorial (§3.6 C) ---
// --- Stage 9: editorial (§3.6 C) ---
let stage = Timestamp::now();
let editorial = match curator.editorial(&lineup).await {
Ok(editorial) => editorial,
@@ -444,6 +436,23 @@ async fn run_stages(
apply_summaries(&mut lineup, &editorial);
report.timings.record("editorial", elapsed_ms(stage));
// --- Stage 10: completed-day World Briefing (§3.8), best effort ---
// Editorial retains budget priority; only the remaining metered budget is
// available for per-event summaries and the overview.
let stage = Timestamp::now();
let mut world_briefing = world::fetch_optional(&http, date, config.world_briefing).await;
if config.world_briefing {
match world_briefing.as_mut() {
Some(briefing) => {
for warning in world::enrich(&http, briefing, curator.llm.as_ref()).await {
report.warn(warning);
}
}
None => report.warn("the world briefing was unavailable; the section is omitted"),
}
}
report.timings.record("world", elapsed_ms(stage));
// --- Stage 11: assemble the issue (§3.10) ---
let issue_number = db
.next_issue_number(date)