K-1 shares print by value; filed documents may print the company's own EIN

The first company's P1 cutover found the K-1 worksheet printing a
member's share as typed: "50%" read from a settings file printed
0.50, the same share kept by the books as 0.5 printed 0.5. Shares now
print at least two places whatever their spelling.

Every company document now goes into the books (D-20, owner,
2026-10-17), and formation papers and plans print the company's EIN,
which the filing scan refused as a printed TIN. It now passes the
company's own EIN, as its settings hold it, printed 2-7, and nothing
else; a vault TIN is still refused (rules.md B-26). Its refusal no
longer suggests converting a file to get past it.

plan/served.md: D-20; correcting a record's source without changing
its words (P5).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-17 21:16:34 -06:00
1 parent 6c04342427
commit 8c123a13b1
7 files changed
+196 -22

No files matched your search

+40 -2
View File
@@ -763,9 +763,20 @@ impl BooksServer {
file_name,
|| known_tin_last4(store),
|| self.vault_tins(store),
self.company_ein(),
)
}
/// The company's own EIN, as its settings hold it, as nine digits:
/// printed 2-7 in a filed document, it is no person's TIN
/// (tin_files.rs INVARIANT 3b).
fn company_ein(&self) -> Option<u32> {
let settings = self.settings();
let (_, ein) = settings.company.profile.iter().find(|(k, _)| k == "ein")?;
let digits: String = ein.chars().filter(char::is_ascii_digit).collect();
(digits.len() == 9).then(|| digits.parse().ok()).flatten()
}
/// Every full TIN the vault holds for a party of the books, for the
/// filing scan only (tin_files.rs INVARIANT 3a): never returned or
/// logged. None when the vault can't be opened (its key missing or
@@ -2359,6 +2370,18 @@ fn money(n: Decimal) -> String {
n.to_string()
}
/// A member's share as the K-1 worksheet prints it: at least two places,
/// however it was written ("50%", "0.5" and "0.500" all print "0.50"; a
/// share of a third keeps its places). The printed text depends on the
/// share, never on its spelling.
fn share_text(n: Decimal) -> String {
let mut n = n.normalize();
if n.scale() < 2 {
n.rescale(2);
}
n.to_string()
}
/// Preview lines with resulting balances for raw (account, side, amount) lines.
fn preview_lines_for(
store: &Store,
@@ -8348,7 +8371,7 @@ impl BooksServer {
let mut cells = vec![
csv_escape(&m.name),
party.map(|id| id.to_string()).unwrap_or_default(),
m.share.to_string(),
share_text(m.share),
m.capital_account.clone(),
];
match &vault {
@@ -8420,7 +8443,7 @@ impl BooksServer {
let mut cells = vec![
csv_escape("Total, all members (not a K-1)"),
String::new(),
share.to_string(),
share_text(share),
String::new(),
String::new(),
String::new(),
@@ -9845,6 +9868,21 @@ mod tests {
assert_eq!(m("12.3456"), "12.3456");
}
/// A share prints by its value, never its spelling: at least two
/// places, a third keeping its own.
#[test]
fn a_share_prints_the_same_however_it_was_written() {
let t = |s: &str| share_text(Decimal::from_str(s).unwrap());
for half in ["0.5", "0.50", "0.500000"] {
assert_eq!(t(half), "0.50", "{half}");
}
assert_eq!(t("1"), "1.00");
assert_eq!(t("1.0"), "1.00");
assert_eq!(t("0.25"), "0.25");
assert_eq!(t("0.3333"), "0.3333");
assert_eq!(t("0.125"), "0.125");
}
/// Every tool says what it is and what it does to the books (plan
/// 4.9, "Annotations (P0)"): a title and readOnlyHint on all; a
/// mutating tool also says whether it is destructive. None asks the