Skip to main content

spend

Function spend 

Source
pub fn spend(
    index: &Index,
    sel: Selection,
    seeds: &BTreeSet<String>,
    limit: u64,
    supersedes: &BTreeMap<String, Vec<String>>,
) -> (Spend, Vec<Diagnostic>)
Expand description

Compile the selection into the answer: fold, decide, order, spend.

The four stages run in that order and none of them re-reads the repository. seeds is what the request asked about, and it is the one input that can push the answer over its ceiling: a seed is never dropped for budget, and neither is the policy or the scope, so limit bounds what the budget chooses and not what it must keep. When the entries it may not drop cost more than the limit, the answer says over_budget rather than returning a context missing the thing it was compiled about.

supersedes maps an identifier to the identifiers it declares it supersedes, and is what turns two versions of one thing in the answer into one entry and a recorded conflict. The diagnostics that come back alongside are what could not be read — never an error, because an absent source is an answer.

use std::collections::{BTreeMap, BTreeSet};

use majordomus_cli::devcontext::budget::spend;
use majordomus_cli::devcontext::select::{Candidate, Selection};
use majordomus_cli::devcontext::{Discovery, Selector, Tier};
use majordomus_cli::synthetic::SyntheticRepository;

let repo = SyntheticRepository::small().unwrap();
let index = repo.index().unwrap();

let seed_uri = "majordomus://issue/I0301";
let candidate = Candidate {
    uri: seed_uri.into(),
    kind: "issue".into(),
    title: Some("the thing being worked on".into()),
    tier: Tier::Task,
    relevance: 1.0,
    depth: 0,
    discovered_by: vec![Discovery {
        selector: Selector::Seed,
        reason: "named in the request".into(),
        via: None,
        edge: None,
        depth: 0,
        confidence: 1.0,
        weight: 1.0,
    }],
    object: None,
    path: None,
    status: None,
    // 40_000 bytes is 10_000 tokens, against a ceiling of 10
    facts: BTreeMap::from([("bytes".to_string(), "40000".to_string())]),
};
let selection = Selection {
    candidates: BTreeMap::from([(seed_uri.to_string(), candidate)]),
    unresolved: Vec::new(),
    diagnostics: Vec::new(),
};
let seeds = BTreeSet::from([seed_uri.to_string()]);

let (s, _) = spend(&index, selection, &seeds, 10, &BTreeMap::new());

// the seed survives a ceiling it does not fit in: a context that lost the thing it was
// compiled about is not a smaller answer, it is the wrong one
assert_eq!(s.selected.len(), 1);
assert!(s.selected[0].required);
assert!(s.excluded.is_empty());

// and the answer says so rather than pretending it stayed inside the budget
assert!(s.budget.over_budget);
assert_eq!(s.budget.used_tokens, 10_000);
assert_eq!(s.budget.required_tokens, 10_000, "all of it is undroppable");
assert_eq!(s.budget.remaining_tokens, 0, "floored, never negative");