Skip to content

Pin each RowCutOff arm of the native ledger parser with a cut response (#718) - #1200

Merged
lamemustafa merged 4 commits into
masterfrom
test/718-row-cut-points
Oct 4, 2026
Merged

lamemustafa merged 4 commits into
masterfrom
test/718-row-cut-points

Conversation

@akshit-khandelwal47

@akshit-khandelwal47 akshit-khandelwal47 commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Part of #718

Summary

The test-only PR the maintainers agreed on #718, in place of slice 3, which was measured as unneeded there. It closes the optional P3 from #1184's review.

Measured, from that review: of the four end-of-input arms in native_ledger_collection.rs that return RowCutOff since #1184, only the LEDGER row's arm had a test (a_ledger_row_refused_is_typed_by_class, which cuts before <OPENINGBALANCE>). Reverting any of the other three to an untyped bail! turned MalformedResponse into RowUnusable with every test green. These arms are reachable: unlike the legacy parsers (see #718), the native ledger parser has no whole-document pre-check.

New test: a_response_cut_inside_any_ledger_field_is_malformed (native_ledger_collection_tests.rs). It uses master_response_with_extra with one party-master field, cuts the response inside that field, and asserts NativeCollectionError::MalformedResponse:

Cut Arm it reaches
<TAXTYPE>GS read_scalar_rejecting_nested_markup
<EMAIL>accounts read_flattened_optional_text
after <LEDGSTREGDETAILS.LIST><APPLICABLEFROM>20250401</APPLICABLEFROM>, with the list still open read_gst_registration_entry

Each uncut response reads, as a control.

The GST case is cut between the entry's children, not inside APPLICABLEFROM's text. Cut inside the text, the end of input is met by the inner field reader, not the entry's own loop, and that arm's mutant survived. Moving the cut is what makes it reach the arm (see Mutants).

Test

At babd936c, the head: the code commit with master merged in after #1184.

rustup run 1.96.0 cargo fmt --manifest-path src-tauri/Cargo.toml --all -- --check          # clean
rustup run 1.96.0 cargo clippy --locked --manifest-path src-tauri/Cargo.toml -p bridge-tally-protocol --all-targets -- -D warnings -A clippy::pedantic   # clean
rustup run 1.96.0 cargo test --locked --no-fail-fast --manifest-path src-tauri/Cargo.toml -p bridge-tally-protocol   # 578 passed, 0 failed
node scripts/check-surface-ack.mjs --mode pull_request --pr 1200 --base origin/master       # touched pinned files (none); surface ack check ok

No production code changes, so the bridge lib suite is not affected.

P4

  1. What existing component could do this? a_ledger_row_refused_is_typed_by_class's fixture helpers (master_response_with_extra) and the typed variant Name why a native ledger export was refused for one row (#718, slice 2) #1184 added. One new test function, no new helper.
  2. What is deleted? Nothing.
  3. What breaks if this is not built? Three typed arms can regress to the wrong class unnoticed: a truncated response would read as a bad row.

Net LOC

From git diff --numstat against master 4e1de421: +34 / −0 in native_ledger_collection_tests.rs. All test code. The file is not pinned, so there is no ack.

Mutants

Each reverted one RowCutOff arm to an untyped bail! in a saved copy of native_ledger_collection.rs, ran the protocol crate's lib tests with --no-fail-fast, and restored the copy. All four arms are now killed.

# Arm reverted Killed by
C1 the LEDGER row (parse_native_ledger_collection_row_with_master_fields) a_ledger_row_refused_is_typed_by_class (from #1184)
C2 read_gst_registration_entry a_response_cut_inside_any_ledger_field_is_malformed (with the cut inside APPLICABLEFROM's text it survived, which is why the cut moved)
C3 read_scalar_rejecting_nested_markup the same
C4 read_flattened_optional_text the same

Not measured

  • A live response cut inside these fields. The cases are the crate's synthetic master_response, cut in memory.

Needs a lab run: none

This adds a test only; no request, response handling or tool output changes.

Migration, security and rollback

  • Migration: none.
  • Security: none. No production code changes.
  • Rollback: revert the PR's commits.

Review checklist

review-checklist.md line 10: Errors are actionable without exposing sensitive values.

🤖 Generated with Claude Code

akshit-khandelwal47 and others added 4 commits October 5, 2026 00:09
Every failure of the native ledger row parsers propagated as text, so
ledger_masters refused as ledger_export_invalid with no cause. The one call
into the row parser now classes its failure with
NativeCollectionError::from_row: what the row holds is RowUnusable; a
quick-xml failure or a response that ends inside the row is
MalformedResponse, which the row parsers' four end-of-input arms now mark
with RowCutOff. A failure the row parser already typed (a foreign-currency
opening, #675) keeps its own class. The empty LEDGER row and the record-count
overflow are typed where they are raised. Two tests that matched row-error
text now assert the variant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#718)

Three of the four end-of-input arms that return RowCutOff had no test, so
reverting one to an untyped bail! would turn MalformedResponse into
RowUnusable with every test green (#1184's review). A party-master response
is now cut inside a scalar field (TAXTYPE), inside a flattened field (EMAIL)
and between a GST registration entry's children, and each must be
MalformedResponse; each uncut response reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ints

# Conflicts:
#	src-tauri/crates/bridge-tally-protocol/src/native_ledger_collection_tests.rs
@lamemustafa

Copy link
Copy Markdown
Member

Independent review (local, Opus) of babd936

Verdict: clean (no P1/P2/P3). Test only; clippy not run by me, CI pending at this head. Reviewed clean at babd936. Mark it ready and I'll queue that same head.

  1. The test reaches each arm. I ran the protocol crate's lib tests filtered to a_response_cut_inside_any_ledger_field_is_malformed: 1 passed at this head. Then I reverted each of the three RowCutOff arms to an untyped bail! in turn (read_gst_registration_entry, read_scalar_rejecting_nested_markup and read_flattened_optional_text, at lines 1020, 1077 and 1183). The new test fails on each: 0 passed, 1 failed. The file was restored and checked clean each time. Together with the LEDGER-row arm (line 933) that Name why a native ledger export was refused for one row (#718, slice 2) #1184's test already pins, every RowCutOff arm now has a test that kills its revert.
  2. The assertions are typed. Each cut asserts downcast_ref::<NativeCollectionError>() == Some(MalformedResponse), and each uncut response is a control that reads. Moving the GST cut between the entry's children, rather than inside APPLICABLEFROM, is the right fix: it makes the entry's own loop meet the end of input.
  3. Scope. +34/−0 in one unpinned test file, no ack needed. The head merges master 4e1de421, so it is not behind.

@akshit-khandelwal47

Copy link
Copy Markdown
Contributor Author

Thank you for the review of babd936, and for re-running each arm's revert.

1-3. Reach, typed assertions and scope: nothing to change. This head stays babd936. I'll mark it ready when its CI is green.

Open: none.

@akshit-khandelwal47
akshit-khandelwal47 marked this pull request as ready for review October 4, 2026 20:27
@akshit-khandelwal47

Copy link
Copy Markdown
Contributor Author

Ready for review at babd936

@lamemustafa
lamemustafa enabled auto-merge October 4, 2026 20:29
@lamemustafa

Copy link
Copy Markdown
Member

Queued at babd936 (the reviewed head): it enters the merge queue as soon as this head's pull-request checks finish green. Please push nothing more here. If it leaves the queue, I'll say why here.

@lamemustafa
lamemustafa added this pull request to the merge queue Oct 4, 2026
Merged via the queue into master with commit 180321f Oct 4, 2026
29 of 30 checks passed
@lamemustafa
lamemustafa deleted the test/718-row-cut-points branch October 4, 2026 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:tally Tally integration severity:p4 Cleanup type:chore Chore

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants