Curation v2 step 7: cleanup, prune paths, implementation notes

Dead code and stale v1 comments removed (clippy -W dead_code clean, the
three world.rs warnings fixed), the Brief chapter's TOC title renamed from
"From the Editor", features prune now also sweeps article_assessments and
generate runs the sweep once after publishing, the example config is
tested key-for-key against Config::default(), README commands match
--help, and docs/plans/2026-08-15-implementation-notes.md records the
Anthropic and Voyage facts, the new tables, the budget-day rule and the
lock.

Implemented by a Claude agent from docs/plans/curation-v2-briefs/step7.md.

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:32:18 +00:00
co-authored by Claude Fable 5.1
parent d261cd485d
commit d403c51edf
18 changed files with 288 additions and 88 deletions
+29 -3
View File
@@ -254,6 +254,9 @@ pub async fn generate(config: &Config, db: &Db, opts: &GenerateOptions) -> Resul
};
db.finish_run(run_id, &report).await?;
if stages.published.is_some() {
prune_retention(config, db).await;
}
// The issue row is written before the report is costed, so stamp the finished
// report onto it now (the paths are preserved by `COALESCE`, §3.13).
if !opts.dry_run
@@ -282,6 +285,28 @@ pub async fn generate(config: &Config, db: &Db, opts: &GenerateOptions) -> Resul
})
}
/// The retention sweep of `features prune` (§7.1, §7.4), run once per
/// published issue. Best effort: a failure is logged and never touches the run.
async fn prune_retention(config: &Config, db: &Db) {
let ranking = &config.curation.ranking;
match telemetry::prune(
db,
ranking.embedding_retention_days,
ranking.telemetry_retention_days,
Timestamp::now(),
)
.await
{
Ok(pruned) => tracing::info!(
embeddings = pruned.embeddings,
candidate_rows = pruned.telemetry,
assessments = pruned.assessments,
"retention prune complete"
),
Err(error) => tracing::warn!(%error, "retention prune failed; continuing"),
}
}
/// What [`run_stages`] hands back; [`generate`] pairs it with the costed report.
#[derive(Debug)]
struct StageOutput {
@@ -1044,10 +1069,11 @@ async fn record_issue(db: &Db, issue: &Issue, published: &Published) -> Result<(
Ok(())
}
/// Build the DeepSeek client, running the weekly profile rebuild when it is due.
/// Build the bulk (DeepSeek) and editor (Claude) clients, running the weekly
/// profile rebuild when it is due.
///
/// Returns `None` for `--skip-llm` and for every configuration/API problem: the
/// caller then curates heuristically instead of failing the run (§3.6).
/// Each client is `None` for `--skip-llm` and for every configuration/API
/// problem: the pipeline then degrades per §17 instead of failing the run.
async fn build_llms(
ctx: &StageContext<'_>,
bulk_meter: &UsageMeter,