feat(package): one attention engine over PackageBooks (U0b)
Needs attention was computed by the viewer alone, while books.start hand-picked a handful of next steps. The human's list and the agent's could disagree, and a new item had to be written twice (or, as books.start showed, not at all). Move the engine to ledger-package over PackageBooks: one function, find/find_with(books, today, settings, integrity), reads the ledger, the journal's event types (an entry's origin), the attachments and their links, the statements, the reconciliations, and the database check. The viewer's attention module now only maps Target to Route; books.start turns the same items into its next steps (why = the item's title, top 12), so the list the human sees and the steps the agent gets cannot drift. PackageBooks gains settings, journal, statements, reconciliations and disk_check, implemented for Store and Snapshot, so both views answer alike. The W-9, reconciliation and 1099 predicates move beside the items to keep one copy. ledger-package gains ledger-config and serde_json (the receipt policy and the tool arguments the items carry). Tests: view_model.rs and spec_kept.rs pass unchanged; the new attention_parity.rs compares the viewer and books.start item for item on the demo books and on a book written entry by entry. just check green.
This commit is contained in:
1 parent
eeee9658da
commit
0cf2f8f5c5
17 files changed
+2026
-895
No files matched your search
@@ -237,29 +237,12 @@ pub fn w9_status(b: &Books, p: &Party) -> W9Status {
|
||||
}
|
||||
}
|
||||
|
||||
/// The 1099 role a party files under, in words ("contractor",
|
||||
/// "landlord"), for prompts.
|
||||
fn form_role(p: &Party) -> &'static str {
|
||||
if p.roles.contains(&Role::Landlord) && !p.roles.contains(&Role::Contractor) {
|
||||
"landlord"
|
||||
} else {
|
||||
"contractor"
|
||||
}
|
||||
}
|
||||
|
||||
/// The agent prompt that collects and records a party's W-9.
|
||||
pub fn collect_w9_prompt(b: &Books, p: &Party) -> String {
|
||||
let paid = b.party(p.id).map(|i| i.paid_ytd).unwrap_or_default();
|
||||
let paid = if paid > Decimal::ZERO {
|
||||
format!(", paid {} so far in {}", fmt::dollars(paid), b.today.year())
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!(
|
||||
"In wanderbooks, {} (party {}) is a {}{paid} with no W-9 facts recorded. Remind me to collect a W-9 before the next payment. Once I have it, record what it says with books.set_w9: the date received, the entity type (line 3), any exempt payee or FATCA codes (line 4), the last four digits of the TIN only (the full TIN goes in the vault, never the books), and where the form is kept. Then show me the party.",
|
||||
p.label(),
|
||||
p.id,
|
||||
form_role(p)
|
||||
ledger_package::w9_prompt(
|
||||
p,
|
||||
b.party(p.id).map(|i| i.paid_ytd).unwrap_or_default(),
|
||||
b.today.year(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,13 +33,10 @@ use rust_decimal::Decimal;
|
||||
/// 2. Says where it last stood: reconciled through a date, or never.
|
||||
/// ```
|
||||
pub fn reconcile_prompt(b: &Books, account: AccountId, through: NaiveDate) -> String {
|
||||
let since = match b.reconciled_through(account) {
|
||||
Some(d) => format!("It is reconciled through {d}, so start with the statement after that"),
|
||||
None => "It has never been reconciled, so start with its first statement".to_string(),
|
||||
};
|
||||
format!(
|
||||
"In wanderbooks, reconcile {} through {through}. {since}. For each monthly statement: file the PDF, record it with books.record_statement (its period and opening and closing balances), mark the lines that cleared with books.mark_cleared, then run books.reconcile against it (preview first; the difference must be exactly zero). Show me anything that doesn't match, and the account when it's done.",
|
||||
b.account_label(account)
|
||||
ledger_package::reconcile_prompt(
|
||||
&b.account_label(account),
|
||||
b.reconciled_through(account),
|
||||
through,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user