Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A1rCLQeKBgnBo3oTgHuTMe
5.6 KiB
YOUR STEP: 5 — Deep assessment, utility, diversity (plan §21 step 5)
Steps 1–4 have landed; triage and union admission now gate the deep set and the old prefilter is
reduced to hygiene helpers. Read git log -8, then all of src/curate/, src/pipeline.rs,
src/types.rs, src/config.rs, src/report.rs, src/main.rs, src/db.rs.
Scope (plan §12 in full, §13 rendering with facets/neighbours, §7.3 deep rows, §18 module retirements):
src/curate/assess.rsreplacesscore.rs(§12.1):DEEP_INSTRUCTIONSandDEEP_PROMPT_VERSION = 1verbatim from the plan (the section palette is substituted fromcuration.sections). Per article: title, feed, author, length, excerpt-only flag, the triagewhy, the interest and neighbour hint lines from §10, and a representative sample of ~2,000 tokens: body under ~1,500 words sent whole; otherwise first 600 words, 500 around the midpoint, last 400, with visible[BEGINNING]/[MIDDLE]/[END]markers, split on word boundaries. Batches ofdeepseek.deep_batch_size(8, replacesscore_batch_size) throughbuffer_unordered, bulk client, budget check per batch. Auto-includes are assessed too. Tolerant parsing:quality/fitclamped 0–10,categoryvalidated against the palette (invalid →None, the editor decides),paywalled_guess, andfacetswhere every unknown enum token degrades toNoneandspecific_topicsis capped at 3. Every enum token in the prompt must round-trip through the parser (test). Persist toarticle_assessments (stage='deep', score=quality, fit, kind=facets.format, facets_json, rationale, category, paywalled_guess, ...); reuse per §7.3 with--rescorebypass. Facets are shown to the editor, the profile rebuild andexplain; not a numeric signal.src/curate/rank.rs(§12.2–§12.5): normalization (LLM scores ÷ 10; everything else mid-rank percentile over the deep set — reuse/move the step-3 normalizer), the utility blend over present signals with[curation.ranking.weights.utility]renormalized and learned signals multiplied by their gate ramp first, stored 0–100; the preliminary blend stays for the eligible set. Diversified shortlist (§12.5): leader clustering by embedding cosine withcluster_threshold(0.85) andper_cluster_cap(2): sort by utility desc then article id asc; assign each article to the first existing cluster whose leader has cosine ≥ threshold, else make it a new leader; articles without embeddings are singletons; admit in order while the cluster's admitted count is below the cap untilshortlist_keep(60); the toputility_protected(10) by utility and all auto-includes are admitted regardless and still count toward their cluster; exploration picks that reached the deep set get up to 3 reserved slots; if short, relax to cap 3, then uncapped. Persistcluster_id,cluster_rank,rank_utility,utility,excluded_reason ∈ cluster_suppressed | shortlist_cap, stageshortlisted.src/curate/editor.rsreplacesselect.rs: the §13 rendering in full —quality 8.5 · fit 7.0 · triage 8.0 — <deep rationale>,facets: format · depth · evidence · technicality · topic_group,matches:,closest rated:,flags: exploration | always-include | excerpt only,opening:first 60 words. The editor sees the 60-item shortlist.assemble()'shard_maxtrim andselect_without_llmnow order by utility, falling back to the preliminary blend. DeleteScoredArticle::combined_score()andScoredArticleitself;Candidateis the only flow type. Move the tests fromscore.rs/select.rsintoassess.rs/editor.rsand delete the old files (updatecurate/mod.rs,Curator).- Editorial inputs: the Brief and the summaries use quality/fit where the plan says so (§14.2 input lists quality/fit). The weekly profile rebuild's rated lines now get real facets.
- Pipeline/report: stage timings
assessandrank;StageCountsgainsassessed,shortlisted,clusters; the §15.4 curation linecuration: 412 considered → 398 eligible → 398 triaged → 120 assessed → 60 shortlisted → 17 selected.explainprints the deep assessment (quality, fit, category, rationale, facets), utility and rank, cluster id and what suppressed it;--near-missesorders by utility.runs.config_jsongainsDEEP_PROMPT_VERSION.--skip-llm/DeepSeek down ⇒ no deep assessment, utility over present signals (triage/interest dominate, §12.3), editor still runs on what it has (§17). config.example.toml/README:deep_batch_sizereplacesscore_batch_size; a stalescore_batch_sizekey fails loudly namingdeep_batch_size.
Tests (§20 "Triage and deep parsing" deep half, "Normalization", "Clustering", "Editor" utility
parts): realistic deep fixture; malformed items do not sink a batch; unknown facet tokens → None;
every enum token round-trips; representative sample has the three markers and respects word
boundaries, short bodies are sent whole; cached deep rows reused / bypassed with --rescore; constant
signal → 0.5; ties equal; absent values do not shift others; effective weights sum to 1; a candidate
missing a signal is scored on the rest; near-duplicates share a cluster and the third is suppressed;
protected top-N survive and count; the bridge case (AC, BC, A≁B, utility A>B>C) yields two clusters;
articles without embeddings are never suppressed; hard_max trims by utility; select_without_llm
orders by utility; the mocked full pipeline publishes with DeepSeek down and writes shortlisted
rows with cluster ids.