merge: P6, the evidence pipeline (branch P6, checkpoint ce366408)
The last of the paused branches, from its "not for merge" checkpoint:
extraction in ledger-import (text, pages, page images; capped, caught),
journaled as attachment.text_extracted; dedup by hash with
books.find_attachment and books.consolidate_duplicates; annotations and
books.set_entry_settles; the company record categories; the two fuzz
targets; feature attachment_text. Nineteen files conflicted with the
four P4b merges, resolved as plan 8.0.2 says: P6 retakes schema 18 (it
had taken 16, which S4.3 holds; S5 holds 17); the tool table is 129 and
the destructive count 31; both sides' appends kept with their seams
restored; one B-27 paragraph naming every feature; in the old-books
fixture P6's hand-written reimbursement is entry 7 (its ids collided
with S4.3's invoice); the replay fuzzer's list gains S7's
invoice.written_off; S4.3's issued-PDF test expects the extraction
record P6 journals right after a filing.
One integration defect between S7 and P6, found by the arap property
test and fixed here: under P6's dedup, a write-off whose evidence bytes
were filed already (the same collection letter for a second write-off of
the same invoice) was refused only after its entry had been journaled,
at the final link to the invoice ("already linked"). The write-off now
resolves the dedup before anything is journaled and keeps a link the
record already has to the invoice instead of refusing it; an explicit
existing_attachment_id already on an entry is still refused (S7's
one-receipt-once rule). P6's gate never ran before the pause; it runs on
main after this merge, and its ledger-store -> ledger-import dependency
is the open review point (plan 8.0.1).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
commit
2ad192bbcd
70 files changed
+4618
-384
No files matched your search
@@ -726,3 +726,41 @@ pub(crate) fn sent_out(s: &ledger_core::arap::Sent) -> SentOut {
|
||||
pub(crate) fn last_sent(sends: &[ledger_core::arap::Sent]) -> Option<String> {
|
||||
sends.iter().map(|s| s.on).max().map(|d| d.to_string())
|
||||
}
|
||||
|
||||
/// An annotation as a tool answers with it (plan P6 step 5).
|
||||
pub(crate) fn annotation_out(a: &ledger_core::Annotation) -> AnnotationOut {
|
||||
AnnotationOut {
|
||||
receipt_number: a.fields.receipt_number,
|
||||
payee: a.fields.payee.clone(),
|
||||
amount: a.fields.amount.map(money),
|
||||
date: a.fields.date.map(|d| d.to_string()),
|
||||
instrument: a.fields.instrument.clone(),
|
||||
category: a.fields.category.clone(),
|
||||
source: a.fields.source.clone(),
|
||||
reimbursable_to: a.fields.reimbursable_to.clone(),
|
||||
note: a.note.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A file name's stem, for a page picture's or a text file's name: what is
|
||||
/// before the last dot, or "attachment" when there is nothing.
|
||||
pub(crate) fn stem(file_name: &str) -> String {
|
||||
let stem = file_name
|
||||
.rsplit_once('.')
|
||||
.map_or(file_name, |(stem, _)| stem)
|
||||
.trim();
|
||||
if stem.is_empty() {
|
||||
"attachment".to_string()
|
||||
} else {
|
||||
stem.chars().take(80).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// The largest character boundary at or below `at` in `text`.
|
||||
pub(crate) fn floor_char_boundary(text: &str, at: usize) -> usize {
|
||||
let mut cut = at.min(text.len());
|
||||
while !text.is_char_boundary(cut) {
|
||||
cut -= 1;
|
||||
}
|
||||
cut
|
||||
}
|
||||
Reference in new issue
Block a user