P3 review: P2 can read the books until the owner consents
check / check (push) Canceled after 0s

A schedule-convention setting, the two new tax categories, a disposal
before the in-service date and a depreciation catch-up each need their
feature now (tax_categories_gain_exempt, disposal_before_service,
depreciation_catch_up), so no write before consent leaves books P2 refuses
or misreads; replayed names are read with P2's TIN detector (B-28). The
cutover runs end to end through the real P2 and P3 binaries in a test.

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-21 17:43:45 -06:00
1 parent 6288ead747
commit 2c5b5a7171
78 files changed
+1375 -4

No files matched your search

+90
View File
@@ -467,9 +467,99 @@ fn full_tin_runs(chars: &[char]) -> Vec<(usize, usize)> {
/// the space, hyphen, dot, slash and underscore separate.
pub fn has_full_tin(text: &str) -> bool {
let chars: Vec<char> = text.chars().collect();
if REPLAYING.with(std::cell::Cell::get) {
return !journaled_tin_runs(&chars).is_empty();
}
!full_tin_runs(&chars).is_empty()
}
std::thread_local! {
/// Set while a journaled line is replayed ([`replaying`]).
static REPLAYING: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
}
/// Run `f` as the replay of a journaled line: within it,
/// [`has_full_tin`] (and so [`check_text`] and the facts' checks) finds a
/// TIN as the version before P3 did, never more.
///
/// ```text
/// GOAL: books an earlier version wrote open, forever, however a later
/// version widens what reads as a full TIN.
/// SOURCE: rules.md B-28 ("Replay accepts every line an earlier version
/// accepted": the bounds are the commands', never replay's);
/// B-26 (no full TIN in the books).
/// INVARIANTS:
/// 1. Within it, a text is refused only if P2's detector
/// ([`journaled_tin_runs`]) refuses it: every name, reason and fact
/// P2 took replays.
/// 2. Outside it (every command), the detector is P3's, which finds
/// everything P2's did and more (Unicode spaces and dashes,
/// invisible characters).
/// 3. Restored on the way out, a panic included.
/// SCENARIOS:
/// 1. IF a P2 journal holds a vendor named with a fullwidth-dash run of
/// nine digits: SHOULD replay (P3 review, 2026-10-21: P3 refused to
/// open such books).
/// ```
pub fn replaying<T>(f: impl FnOnce() -> T) -> T {
struct Restore(bool);
impl Drop for Restore {
fn drop(&mut self) {
REPLAYING.with(|r| r.set(self.0));
}
}
let _restore = Restore(REPLAYING.with(|r| r.replace(true)));
f()
}
/// The separators P2 (and every version before P3) read between a TIN's
/// digits: space, hyphen, dot, slash, underscore, the no-break space and
/// the dashes U+2010 to U+2014 and U+2212.
fn journaled_tin_separator(c: char) -> bool {
matches!(
c,
' ' | '-' | '.' | '/' | '_' | '\u{a0}' | '\u{2010}'..='\u{2014}' | '\u{2212}'
)
}
/// [`full_tin_runs`] as P2 found them: [`journaled_tin_separator`] only,
/// one to [`MAX_TIN_GAP`] between neighbors, no invisible characters.
fn journaled_tin_runs(chars: &[char]) -> Vec<(usize, usize)> {
let mut runs = Vec::new();
let mut i = 0;
while i < chars.len() {
if !chars[i].is_numeric() {
i += 1;
continue;
}
let (mut digits, mut last, mut j) = (1, i, i + 1);
while j < chars.len() {
if chars[j].is_numeric() {
digits += 1;
last = j;
j += 1;
continue;
}
let gap = chars[j..]
.iter()
.take_while(|c| journaled_tin_separator(**c))
.count();
if (1..=MAX_TIN_GAP).contains(&gap)
&& chars.get(j + gap).is_some_and(|c| c.is_numeric())
{
j += gap;
} else {
break;
}
}
if digits >= 9 {
runs.push((i, last));
}
i = last + 1;
}
runs
}
/// `text` with every run [`has_full_tin`] finds masked: each of its
/// digits becomes `•`, separators kept. For refusals that quote what they
/// were given, so no error echoes a full TIN (docs/spec/rules.md B-26).