feat(core,mcp,gui): never apply a legal rule past what wanderbooks knows
The owner (2026-10-13): when what wanderbooks knows is stale, check the date, research it or ask the owner or CPA, and update the rule for everyone with a pull request (best) or an issue (worst). - ledger_core::rules: a registry of rules set by law that change over time. Each says the years it's known for, its rules.md entry, its vendored official source, and a review date. stale_rules(today) is pure; the engine has no clock. - The 1099 threshold is the first: $600 through 2025, $2,000 in 2026, then indexed for inflation (26 U.S.C. §6041(h)). default_threshold and vendor_1099_report now return Result; for 2027+ they refuse with LedgerError::RuleNotKnown, whose message says what to do: get the figure from the official source or the owner/CPA, pass it explicitly, then open a PR (or issue) at github.com/swaits/wanderbooks. Before, 2027+ silently used $2,000. - MCP: books.vendor_1099 relays that refusal; books.digest gains stale_rules; the server instructions tell the agent the procedure (and how to handle control-account conflicts: explain, never renumber). The CPA package writes a NOT-INCLUDED note instead of a guessed 1099 worksheet. - Viewer: "Needs attention" shows each stale rule with a ready prompt for the agent; the 1099 page explains instead of guessing. Review date for the 1099 rule: 2026-11-01, when the IRS usually publishes next year's inflation adjustments (CONVENTION, marked as such in the registry). Tests: an_unknown_year_threshold_is_never_guessed, dated_rules_go_stale_on_their_review_date (dates passed in, so no clock in the test), MCP stdio: 2027 refused with guidance, explicit threshold accepted, digest carries stale_rules (asserted date-independently). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
This commit is contained in:
1 parent
8559bba6bc
commit
4eaac20311
15 files changed
+349
-47
No files matched your search
@@ -251,6 +251,14 @@ fn mcp_end_to_end_over_stdio() {
|
||||
let digest = mcp.call("books.digest", json!({"since": "2026-01-01"}));
|
||||
assert_eq!(digest["entries"].as_array().unwrap().len(), 1);
|
||||
assert_eq!(digest["entries"][0]["receipt_missing"], false);
|
||||
// Stale legal rules ride along with the digest. Which ones depends on
|
||||
// the server's clock, so assert only what holds on any date.
|
||||
for rule in digest["stale_rules"]
|
||||
.as_array()
|
||||
.expect("stale_rules is a list")
|
||||
{
|
||||
assert!(rule["name"].is_string() && rule["source"].is_string());
|
||||
}
|
||||
let search = mcp.call("books.search_entries", json!({"text": "rent"}));
|
||||
assert_eq!(search["entries"].as_array().unwrap().len(), 1);
|
||||
assert_eq!(search["entries"][0]["receipt_count"], 1);
|
||||
@@ -1578,6 +1586,21 @@ fn mcp_vendor_1099_over_stdio() {
|
||||
mcp.call("books.set_entry_vendor", json!({"entry_id": id}));
|
||||
let r = mcp.call("books.vendor_1099", json!({"year": 2026}));
|
||||
assert_eq!(r["vendors"][0]["paid"], "0.00");
|
||||
// A year whose threshold isn't known yet: refused, with what to do
|
||||
// (rules.md B-19). An explicit threshold works.
|
||||
let why = mcp.call_err("books.vendor_1099", json!({"year": 2027}));
|
||||
for needle in [
|
||||
"only through 2026",
|
||||
"official source",
|
||||
"github.com/swaits/wanderbooks",
|
||||
] {
|
||||
assert!(why.contains(needle), "missing {needle:?} in: {why}");
|
||||
}
|
||||
let r = mcp.call(
|
||||
"books.vendor_1099",
|
||||
json!({"year": 2027, "threshold": "2100"}),
|
||||
);
|
||||
assert_eq!(r["threshold"], "2100.00");
|
||||
let vendors = mcp.call("books.list_vendors", json!({}));
|
||||
assert_eq!(vendors["vendors"][0]["tin_on_file"], true);
|
||||
// Show the entry in the viewer: a request outside the books.
|
||||
|
||||
Reference in new issue
Block a user