test: the tests P6's gate never reached, under one-record dedup
check / check (push) Canceled after 0s
two-user / two-user (push) Canceled after 0s

P6's branch paused with its gate interrupted, so five tests that
predate plan P6 step 4 (the same bytes filed twice are one attachment,
linked to each subject) still encoded the invariant it replaced, and
one golden carried counts from before extraction records existed.

- store: `blob_dedup_and_hash_verification` expects one attachment
  row under the first filing's name, not two. The `attach_receipt`
  spec block's INVARIANT 2/3 now say what P6 built; the stale "each
  filing is its own attachment" was the one wording left.
- store: the power-loss replay copies the blob store too. The books
  on disk were always journal, database and blobs; verify now reads
  an extraction's text blob (P6 step 2), so a copy without `blobs/`
  cannot verify.
- store: the refusals property allows `AttachReceipt` and
  `FileDocument` to journal nothing when the bytes are already on
  file: the record they return is the record they were.
- viewer: each statement the reconciliation helper files has its own
  bytes (a wrong statement is voided and a new one comes); identical
  bytes would be one record, and a record is one statement.
- viewer: the tall sidebar's shapes golden is re-blessed. The fixture
  books now carry extraction records, so the Overview attention
  count, the event count and the verified-file count the sidebar
  prints moved; no layout changed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
swaitsandClaude Fable 5.1 committed 2026-10-29 07:04:22 -06:00
1 parent 6c26160e26
commit bfd453b679
5 files changed
+38 -21

No files matched your search

+5 -1
View File
@@ -224,12 +224,16 @@ fn reconcile(
};
let opening = report(s, month_start.pred_opt().unwrap());
let closing = report(s, through);
// Each call files a new statement (a wrong one is voided and another
// comes), so each has its own bytes: identical bytes would be one
// record (plan P6 step 4), and a record is one statement.
let filing = s.statements(None).unwrap().len() + 1;
let (att, _, _) = s
.file_document(
"statement",
&format!("statement-{through}.pdf"),
"application/pdf",
format!("statement {through}").as_bytes(),
format!("statement {filing}: {through} for account {account}").as_bytes(),
None,
)
.unwrap();
Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

@@ -1 +1 @@
5ccd68030ec4a54b
b4c03adfe9dc03e6
+5 -3
View File
@@ -4488,9 +4488,11 @@ impl Store {
/// INVARIANTS:
/// 1. The bytes MUST be stored under their SHA-256 and read back
/// identical, verified against it.
/// 2. The same bytes filed twice MUST share one stored file, and the
/// second filing MUST say deduplicated.
/// 3. Each filing MUST be its own attachment, journaled
/// 2. The same bytes filed twice MUST be one record (plan P6 step 4):
/// the second filing returns the first's id, says deduplicated,
/// and links that record to the new subject (`attachment.linked`)
/// when it isn't linked there yet.
/// 3. The first filing of bytes MUST be journaled
/// (`receipt.attached`), with its name, type, and description.
/// SCENARIOS:
/// 1. IF `entry_id` names no entry: SHOULD refuse
+27 -16
View File
@@ -235,6 +235,19 @@ fn damage_before_the_last_newline_still_refuses_to_open() {
assert_eq!(std::fs::read(&journal).unwrap(), raw);
}
/// Every file and directory under `from`, under `to`.
fn copy_tree(from: &std::path::Path, to: &std::path::Path) {
std::fs::create_dir_all(to).unwrap();
for e in std::fs::read_dir(from).unwrap() {
let e = e.unwrap();
if e.file_type().unwrap().is_dir() {
copy_tree(&e.path(), &to.join(e.file_name()));
} else {
std::fs::copy(e.path(), to.join(e.file_name())).unwrap();
}
}
}
/// Power loss at any byte of the last append: whatever prefix reached the
/// disk, the books open with exactly what was acknowledged before it.
#[test]
@@ -246,14 +259,11 @@ fn power_loss_at_any_byte_of_the_last_append_is_recovered() {
let before = ledger_core::reports::snapshot(store.ledger()).unwrap();
drop(store);
// The books as they were on disk before the last append (the database
// is never ahead of the journal: the line is written first).
// is never ahead of the journal: the line is written first), blobs
// included: verify reads the receipt's extracted text from them (plan
// P6 step 2).
let pre = test_dir("tornevery-pre");
for e in std::fs::read_dir(&dir).unwrap() {
let e = e.unwrap();
if e.file_type().unwrap().is_file() {
std::fs::copy(e.path(), pre.join(e.file_name())).unwrap();
}
}
copy_tree(&dir, &pre);
let mut store = Store::open(&dir).unwrap();
store.add_customer("Acme").unwrap();
drop(store);
@@ -262,12 +272,7 @@ fn power_loss_at_any_byte_of_the_last_append_is_recovered() {
assert_eq!(last.last(), Some(&b'\n'));
for cut in 1..last.len() {
let case = test_dir(&format!("tornevery-{cut}"));
for e in std::fs::read_dir(&pre).unwrap() {
let e = e.unwrap();
if e.file_type().unwrap().is_file() {
std::fs::copy(e.path(), case.join(e.file_name())).unwrap();
}
}
copy_tree(&pre, &case);
let mut torn = before_bytes.clone();
torn.extend_from_slice(&last[..cut]);
std::fs::write(case.join(ledger_store::JOURNAL_FILE), &torn).unwrap();
@@ -326,9 +331,12 @@ fn blob_dedup_and_hash_verification() {
assert!(!dedup1);
assert!(dedup2);
assert_eq!(sha1, sha2);
// Only one blob row; two attachment rows.
// One blob row and one attachment row: the same bytes filed twice are
// one record (plan P6 step 4), under the first filing's name.
assert_eq!(store.verify_blobs().unwrap(), 1);
assert_eq!(store.list_attachments(None).unwrap().len(), 2);
let records = store.list_attachments(None).unwrap();
assert_eq!(records.len(), 1);
assert_eq!(records[0].file_name, "w2.pdf");
// Corrupt the blob file on disk → the next read detects it.
let shard = dir
@@ -1691,7 +1699,8 @@ mod refusals {
// The converse law: an accepted command is on the record, or
// changed nothing at all (a batch run with nothing due; a
// setting changed to the value it has; a 1099 decision, W-9
// flag or payment method already so).
// flag or payment method already so; bytes already on file,
// which are the record they were, plan P6 step 4).
if result.is_ok() {
let after = std::fs::read_to_string(&journal).unwrap();
if after.len() == before.len() {
@@ -1711,6 +1720,8 @@ mod refusals {
| Op::SetVendor1099(..)
| Op::SetW9Flag(..)
| Op::SetEntryInstrument(..)
| Op::AttachReceipt(..)
| Op::FileDocument(..)
),
"accepted {:?} but journaled nothing",
op