Correct a Bridge-built import batch in place, under compare-and-swap - #439
Merged
Merged
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
lamemustafa
force-pushed
the
feat/import-amend-cas
branch
from
September 16, 2026 09:56
8633f29 to
31a24a6
Compare
lamemustafa
pushed a commit
that referenced
this pull request
Sep 16, 2026
…admit amendments through tools/call build_import_xml takes proposals_id and proposals_sha256 in place of inline vouchers. The resolver opens only a file parse_bank_statement published, with a well-formed id under bank-statements/, through the hardened local-file open. It requires the declared schema and id and refuses a file whose bytes no longer hash to the digest the parse returned. It then hands the file's vouchers to the unchanged admission path. Through tools/call against the simulator, a proposals build consumes the same 44 requests as the inline build, and the import XML is identical apart from batch identity. The catalog validator did not know the pattern amends_batch_id publishes, so every tools/call naming amends_batch_id was refused argument_invalid:amends_batch_id before the handler ran. The amendment path (#439) was reachable only by calling the handler directly, as its tests do. The validator now checks that exact batch-id shape, and one constant serves both the schema and the validator. An ignored end-to-end test parses the synthetic encrypted HDFC statement through parse_bank_statement, builds it by proposals_id, and gets six vouchers with the wrapped UPI reference intact. CI runs it with the pinned PDFium. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 16, 2026
lamemustafa
pushed a commit
that referenced
this pull request
Sep 16, 2026
…admit amendments through tools/call build_import_xml takes proposals_id and proposals_sha256 in place of inline vouchers. The resolver opens only a file parse_bank_statement published, with a well-formed id under bank-statements/, through the hardened local-file open. It requires the declared schema and id and refuses a file whose bytes no longer hash to the digest the parse returned. It then hands the file's vouchers to the unchanged admission path. Through tools/call against the simulator, a proposals build consumes the same 44 requests as the inline build, and the import XML is identical apart from batch identity. The catalog validator did not know the pattern amends_batch_id publishes, so every tools/call naming amends_batch_id was refused argument_invalid:amends_batch_id before the handler ran. The amendment path (#439) was reachable only by calling the handler directly, as its tests do. The validator now checks that exact batch-id shape, and one constant serves both the schema and the validator. An ignored end-to-end test parses the synthetic encrypted HDFC statement through parse_bank_statement, builds it by proposals_id, and gets six vouchers with the wrapped UPI reference intact. CI runs it with the pinned PDFium. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
lamemustafa
pushed a commit
that referenced
this pull request
Sep 16, 2026
build_import_xml publishes amends_batch_id with a bridge-UUIDv4 pattern that argument admission did not recognize, and an unrecognized pattern refuses every value. So every amendment sent over MCP was refused as argument_invalid:amends_batch_id before the build ran; #439's tests called the builder directly and never crossed that admission. Found by a live end-to-end amendment against the licensed lab. Admission now applies valid_batch_id for that pattern. The matcher lookup is split out so a test can assert every pattern admission reads is recognized, and the amendment refusal test now also goes through call_tool_response. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
lamemustafa
pushed a commit
that referenced
this pull request
Sep 16, 2026
build_import_xml publishes amends_batch_id with a bridge-UUIDv4 pattern that argument admission did not recognize, and an unrecognized pattern refuses every value. So every amendment sent over MCP was refused as argument_invalid:amends_batch_id before the build ran; #439's tests called the builder directly and never crossed that admission. Found by a live end-to-end amendment against the licensed lab. Admission now applies valid_batch_id for that pattern. The matcher lookup is split out so a test can assert every pattern admission reads is recognized, and the amendment refusal test now also goes through call_tool_response. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Every
build_import_xmlbuild draws a fresh random batch identity, so rebuilding a corrected voucher and importing it creates a duplicate. That is the one thing that stops a bank-statement import from moving onto Bridge end to end: the manual workflow corrected a mis-booked row by re-importing under a stable REMOTEID, and Bridge had no equivalent.Bridge deliberately does not let a caller's transaction label become a Tally upsert key, and that stays true. This PR adds an explicit correction path that cannot be entered by accident.
What
build_import_xmlaccepts an optionalamends_batch_id, which must name a batch this Bridge built. The new build re-renders the named transactions under that batch's identity: the same REMOTEID and the same[BRIDGE:…]narration marker. A file import then alters those vouchers in place instead of creating new ones.Tally's upsert is blind. It replaces whatever the voucher holds, including an edit someone made in Tally since. So an amendment is admitted only under a compare-and-swap:
agent_import_amend.rs). The named batch resolves to its original, and every build sharing that identity is loaded (ledger::read_lineage). The amendment is refused if any build in that lineage was posted natively:post_importused a random private REMOTEID beside the marker, so importing over it would create a duplicate. It is also refused on a different company GUID, a different endpoint origin, or a legacy identity scheme.bridge_txn_idmust be one the lineage built, with the samevoucher_typeandvoucher_number. Amounts, entries, date, narration and reference may change. A type change is refused even though Tally was observed applying one (see below).REFERENCEis not compared, because the verification read does not fetch it. The warning says so. One divergent voucher refuses the whole amendment, and nothing is written.import_post_amendment_requires_file_import).amends_batch_id, andattribution_tagand verification use the original identity, soverify_importon the amendment batch reads back the altered vouchers.Limit, stated in the tool output: the compare-and-swap covers the book as read during the build. An edit made in Tally between the build and the hand import is overwritten. The warning tells the operator to import promptly and to expect "altered", not "created".
Measured before building on it
These measurements were made over the XML gateway on licensed TallyPrime 7.1 Silver, into a synthetic company only, with hand-built XML copying Bridge's envelope and voucher shape. A same-REMOTEID import with changed content returned
CREATED=0 ALTERED=1and kept the voucher's GUID in every one of these cases:Before each write, a canary proved that an import naming unknown ledgers fails closed. Every probe used only ledgers absent from the client book that was also loaded, and a count-only read of that book stayed at zero. The table is recorded as a scoped correction in
TALLY_PROTOCOL_REFERENCE.md§9.3.Not measured: importing through Tally's own Import menu (which is how an operator imports these files), other releases or tiers, bill allocations, inventory or tax lines, and cancelled or optional vouchers.
Compatibility surface
agent_import_amend.rsis pinned, raisingMAX_SURFACE_FILESfrom 238 to 239 with a named reason. The pin also has a guard test, because the gate cannot see a pin dropped by a merge.This branch is rebased onto #438 (232→238), as agreed with that branch. Both pin lists and both rationale paragraphs are kept, the cap is 239, and the surface is resealed. The pin set is master's 238 plus this one file, with none dropped.
Tests
The new tests in
agent_import_amend_tests.rscover the lineage, dispatch refusal, company, endpoint and scheme refusals, proposal rules, the date window, the compare-and-swap (admitted, diverged, redated, missing, cancelled, one-of-two diverged), native-post refusal, schema and parsing, refusal before any Tally read, the pin guard, and two simulator end-to-end builds:amends_batch_id.Controls proven by mutation: each of the dispatch check, CAS diff, narration comparison, cancelled check, type check, endpoint check, native-post refusal, tag identity, lineage dispatch fold, window, and the three build wirings (render identity, CAS call, recorded field) was disabled in turn, and at least one test failed each time.
Gates run locally:
cargo fmt --check,cargo test --locked(src-tauri),cargo clippy --workspace --all-targets -D warnings -A clippy::pedantic, tools workspace tests includingreal_tree_has_complete_migration_and_report_surface_coverage, andscripts/reseal.sh --verify.Review
Two Sonnet reviews ran, one on correctness and one on prose accuracy. Fixed from them:
Not in this PR
voucher_presencederives the marker from thebatch_ida caller supplies. For an amended voucher, pass the original batch id.verify_importon the amendment batch would misattribute (read-only). Itspost_importshould refuse: it re-renders the file under the amendment's own id, so the hash no longer matches the saved one (import_batch_changed). That comes from reading master's admission code; it was not run against an older binary.🤖 Generated with Claude Code