pub struct Spend {
pub selected: Vec<ContextEntry>,
pub excluded: Vec<Excluded>,
pub deduplicated: Vec<Deduplicated>,
pub conflicts: Vec<Conflict>,
pub budget: Budget,
}Expand description
Everything the budget decided, as four lists and the arithmetic behind them: what the session is given, what it is not and why, what collapsed into what, and what disagrees with what.
The four lists are disjoint views of one walk rather than four separate answers, and the
one invariant that joins them to the budget is that budget.used_tokens is exactly the
sum of selected’s costs — nothing is spent that is not in the answer, and nothing is in
the answer that was not spent. An under-filled context is debugged from excluded, not
by guessing, which is why it carries a reason and a cost per row.
Every field is populated on every call, including with nothing reached: the per-tier spend always has all six tiers, so a reader never has to distinguish “this tier was empty” from “this tier is missing from the answer”.
use std::collections::{BTreeMap, BTreeSet};
use majordomus_cli::devcontext::budget::{spend, Spend};
use majordomus_cli::devcontext::select::Selection;
use majordomus_cli::devcontext::{Tier, DEFAULT_BUDGET_TOKENS};
use majordomus_cli::model::Diagnostic;
use majordomus_cli::synthetic::SyntheticRepository;
let repo = SyntheticRepository::small().unwrap();
let index = repo.index().unwrap();
let nothing = Selection {
candidates: BTreeMap::new(),
unresolved: Vec::new(),
diagnostics: Vec::new(),
};
// the answer and what could not be read, side by side and neither an error
let (s, diagnostics): (Spend, Vec<Diagnostic>) = spend(
&index, nothing, &BTreeSet::new(), DEFAULT_BUDGET_TOKENS, &BTreeMap::new(),
);
// reaching nothing is an answer, not an error, and it is a complete one
assert!(s.selected.is_empty() && s.excluded.is_empty());
assert!(s.deduplicated.is_empty() && s.conflicts.is_empty());
assert!(diagnostics.is_empty());
// the per-tier spend is always total, so no tier is merely absent
let tiers: Vec<Tier> = s.budget.tiers.iter().map(|t| t.tier).collect();
assert_eq!(tiers, Tier::ORDER.to_vec());
// and the invariant that ties the lists to the arithmetic
let spent: u64 = s.selected.iter().map(|e| e.cost_tokens).sum();
assert_eq!(s.budget.used_tokens, spent);
assert_eq!(s.budget.remaining_tokens, DEFAULT_BUDGET_TOKENS);Fields§
§selected: Vec<ContextEntry>What to give the session, in the order it was spent.
excluded: Vec<Excluded>What was reached and not given.
deduplicated: Vec<Deduplicated>What collapsed into what.
conflicts: Vec<Conflict>What does not agree with what.
budget: BudgetThe ceiling and the per-tier spend.