Curation v2 step 6: Behind the paper, stats, run report block, lock

Behind-the-paper chapter (behind.xhtml, both editions) built from the run's
StageCounts and candidate_runs near misses; daily-epub stats [--days N];
StageCounts gains knn/feed gates and verdicts_in_prompt, timings split
into summaries + brief, the four-line §15.4 info block logged once per
run and printed by print_report; src/lock.rs flock guard on
<database_path>.lock for generate, profile rebuild, features backfill and
backfill-social; README updated for the new CLI, env vars and costs.

Implemented by a Claude agent from docs/plans/curation-v2-briefs/step6.md;
reviewed against plan §5, §15.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1rCLQeKBgnBo3oTgHuTMe
This commit is contained in:
2026-09-02 16:21:06 +00:00
co-authored by Claude Fable 5.1
parent 05a74a0dcf
commit d261cd485d
22 changed files with 1583 additions and 113 deletions
+49
View File
@@ -383,6 +383,9 @@ pub struct TasteProfile {
pub text: String,
pub version: i64,
pub built_at: Timestamp,
/// Explicit verdicts rendered into the prompt's "Recent verdicts" block (§8.4).
#[serde(default)]
pub verdicts: usize,
}
// ---------------------------------------------------------------------------
@@ -562,6 +565,9 @@ pub struct Issue {
pub world_briefing: Option<WorldBriefing>,
/// Colophon facts: models used, token cost, feed counts (§3.10).
pub colophon: Colophon,
/// The "Behind the paper" chapter's facts (§15.1); filled by the pipeline.
#[serde(default)]
pub behind: BehindThePaper,
}
/// Resolved model names printed in the colophon (§15.1).
@@ -584,6 +590,49 @@ pub struct Colophon {
pub generator_version: String,
}
/// One of the highest-utility articles that did not make the paper (§15.1).
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct NearMiss {
pub article_id: ArticleId,
pub title: String,
pub feed_title: String,
pub quality: Option<f64>,
pub fit: Option<f64>,
/// `candidate_runs.stage` reached.
pub stage: String,
/// `candidate_runs.excluded_reason`, when the row carries one.
pub reason: Option<String>,
}
/// Facts for the "Behind the paper" chapter (§15.1), derived from the run's
/// report and its `candidate_runs` rows. Templates render it verbatim.
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct BehindThePaper {
/// Deduplicated articles the run looked at.
pub considered: i64,
pub feeds_seen: i64,
pub eligible: i64,
pub triaged: i64,
/// Deep-assessed ("read closely").
pub read_closely: i64,
pub shortlisted: i64,
pub selected: i64,
/// First admitting retriever → count (§11).
pub admitted_by: BTreeMap<String, i64>,
pub rated_with_embeddings: i64,
/// The neighbour signal's gate ramp, 0–1 (§9.2).
pub knn_gate: f64,
/// The feed-affinity gate ramp, 0–1 (§9.3).
pub feed_gate: f64,
pub near_misses: Vec<NearMiss>,
pub models: Models,
pub embedding_model: String,
/// Every provider, Voyage included.
pub cost_usd: f64,
/// Wall clock from the run's start to issue assembly.
pub generation_secs: i64,
}
/// A downloaded, re-encoded image embedded in an edition (§3.10 images).
#[derive(Debug, Clone, PartialEq)]
pub struct ImageAsset {