PR2 — native outstandings protocol and runtime - #138
Conversation
516dbf9 to
5802e11
Compare
5802e11 to
125dd0b
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 125dd0bea8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
bridge/src-tauri/src/commands.rs
Lines 2081 to 2082 in 421707c
On Education-mode Tally, an SVTODATE whose day is not 1, 2, or 31 is silently ignored and the period can widen while still returning success, but this command sends the workstation's date on every calendar day; for example, a read on August 15 can return whole-book data labelled as an August 15 Complete result. The all-companies command repeats the same construction, so both paths need a typed, profile-valid date boundary or must withhold the result when today's date cannot be represented safely.
AGENTS.md reference: AGENTS.md:L97-L102
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0a71a0ce98
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0590f12f93
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 89f828c21b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Ok(( | ||
| party.ok_or(NativeOutstandingsError::InvalidResponse( | ||
| "bills_fixed_missing_billparty", | ||
| ))?, |
There was a problem hiding this comment.
When Tally emits <BILLPARTY></BILLPARTY>, this only verifies that the element was present and accepts an empty join key. The named amount is then counted under "", while compute_residuals cannot match it to the actual ledger and can count that ledger's full balance again as unallocated, returning Complete with overstated exposure; reject empty or otherwise invalid party values at this boundary.
AGENTS.md reference: AGENTS.md:L112-L118
Useful? React with 👍 / 👎.
| if !path.is_empty() { | ||
| anyhow::bail!("company collection response ended before its root closed"); | ||
| } | ||
| Ok(records) |
There was a problem hiding this comment.
Require the company collection envelope
In the probe_tally path, a STATUS=1 response that omits BODY/DATA/COLLECTION reaches this return with zero records, so company_discovery_evidence marks XML support as observed and reports company_not_loaded instead of falling back to the legacy discovery request. Track that the collection was structurally present so an unsupported or malformed response cannot masquerade as a legitimate empty company list.
AGENTS.md reference: AGENTS.md:L143-L145
Useful? React with 👍 / 👎.
| let residual = ledger | ||
| .closing_balance | ||
| .checked_subtract(receivable_sum) | ||
| .and_then(|value| value.checked_subtract(payable_sum)) | ||
| .map_err(|_| NativeOutstandingsError::ArithmeticOverflow)?; |
There was a problem hiding this comment.
Withhold Complete when opening bills are unresolved
The captured validation fixture proves this case: BVL Gamma Opening has OPENINGBALANCE=-44444 from a named opening bill but is absent from both native bill reports. This loop ignores ledger.opening_balance, classifies the full gap as unallocated, and lets the runtime return Complete, so the named bill disappears from receivable totals, bill counts, details, and ageing while being falsely described as carrying no reference; return Partial for a nonzero bill-wise opening unless its allocations are recovered separately.
AGENTS.md reference: AGENTS.md:L101-L105
Useful? React with 👍 / 👎.
| std::fs::write(&path, contents.as_bytes()) | ||
| .map_err(|error| format!("Bridge could not write the export: {error}"))?; |
There was a problem hiding this comment.
Avoid truncating an existing download
When Downloads/<file_name> already exists, std::fs::write silently truncates it before writing the export. A repeated same-name export or collision with an unrelated user file therefore destroys the previous contents, unlike the browser download behavior this command replaces; create a unique suffixed path or fail in-band when the destination exists.
AGENTS.md reference: AGENTS.md:L154-L159
Useful? React with 👍 / 👎.
| # this gate exists so the ~4,800 lines of scan machinery (date/AlterID | ||
| # partitioning, segment completeness proofs, the wildcard voucher fetch) do | ||
| # not compile into a shipped build at all. Not a default feature. | ||
| voucher-scan = ["bridge-tally-protocol/voucher-scan", "bridge-tally-transport/voucher-scan"] |
There was a problem hiding this comment.
Update the write-canary runbook after removing its feature
The manifest no longer declares fixture-canary-runtime-dispatch, and the command registration was removed, but docs/tally/compatibility/synthetic-write-canary-fixture.md:39 still promises that a Bridge build with this feature exposes the reviewed Tauri command. Following that procedure now fails immediately with Cargo's unknown-feature error, so either restore the feature boundary or update the runbook to state that the canary is unavailable.
AGENTS.md reference: AGENTS.md:L174-L179
Useful? React with 👍 / 👎.
| //! Folded from the former standalone `bridge-tally-canonical` crate: it had exactly one consumer | ||
| //! inside this crate (`tally::connector` and `tally::connection`), so the crate boundary earned | ||
| //! nothing except hiding this module's true dead code from the `dead_code` lint. This module |
There was a problem hiding this comment.
Finish removing the crates copied into private modules
This adds a second full canonical implementation while crates/bridge-tally-canonical remains both a workspace member and a direct bridge dependency; the same incomplete fold remains for runtime and observability. Consequently cargo test --workspace still compiles and tests the old copies while production calls the new private modules, allowing fixes or passing tests to land against code that is not shipped; delete the former crates/dependencies or continue using them as the single implementation.
AGENTS.md reference: AGENTS.md:L124-L131
Useful? React with 👍 / 👎.
Records source-surface digest changes only; no compatibility claim, evidence file, or trusted key changed.
Resealed entries: .github/workflows/ci.yml; src-tauri/Cargo.toml; bridge-tally-protocol Cargo.toml, lib.rs, outstandings/{model,parser,request,wire}.rs, xml_read_profiles.rs, simulator_corpus.rs; bridge-tally-transport Cargo.toml and lib.rs; src-tauri commands.rs, lib.rs, tally/{connection,connector,mod,runtime}.rs. Matrix changed only compatibility_surface_sha256.
Fix confirmed native-outstandings money defects without changing magnitudes. Future-due bills stay in receivable/payable totals but are excluded from ageing buckets, ageing counts, and oldest_bill_age_days; the DueDate crosscheck compares them as Tally's zero BILLOVERDUE. Two-digit display years now resolve only when exactly one valid candidate falls in the pinned BooksFrom..as-of window; zero and multiple candidates fail closed with distinct stable codes.
Red proof before fix: not_yet_due_bill_is_reported_without_becoming_overdue failed with InvalidDate("native_date_after_as_of"). resolves_a_century_boundary_year_into_the_active_book failed: left 19260401, right 20260401.
Mutation proof: changing future due to age 0 made the synthetic bill enter days_0_30 (left ExactDecimal("100"), right ExactDecimal("0")); omitting the BooksFrom lower bound made the century test fail with native_date_year_ambiguous_book_window. Both mutations were reverted; focused tests passed.
Bill dates remain bounded by BooksFrom through as_of. Due dates retain the BooksFrom bound while allowing a future candidate in the active century range; ambiguous two-digit years still fail closed. Statement source rows carry an explicit unaged age instead of being dropped, and residual direction is retained with its magnitude. Proof: the raw-byte regression failed before this change at native_date_year_outside_book_window, then after only the parser correction at the all_open_bill_rows assertion (0 rows). Targeted date-role test and app test compilation passed with rustup 1.96.0.
This is a REPAIR OF GIT-INDUCED CORRUPTION, reconstructed from fully determined content: the 21-byte envelope plus its independently asserted CRLF terminator is 23 bytes. It is NOT A CAPTURE and does not represent re-captured live Tally evidence.
Include direct Sundry Debtor/Creditor ledgers in residual reconciliation even when bill-wise details are disabled, place future-due bills in the first ageing bucket and open-bill count while preserving age_days=None, and accept empty BILLOVERDUE values at the XML boundary. Validation-lab arithmetic: named receivables 255553 + residuals 77777 (Beta 33333 + Gamma 44444) = ledger exposure 333330. Future-due amount 22222 remains unaged but contributes to first-bucket 244442 and open-bill count 5. Captured read-only synthetic validation-lab receivable, payable, and ledger responses are committed byte-for-byte with lengths, SHA-256 digests, status bracketing, and privacy scan recorded in PROVENANCE.md. Mutation proof: routing the future-due amount to 31-60 made the exact money test fail with exit 101 (222220 != 244442); reverting residual eligibility to bill-wise-only made it fail with exit 101 because BVL Beta was absent; returning Some(0) for the future bill made the exact runtime test fail with exit 101 (Some(0) != None). After restoration, the protocol native-outstandings suite passed 13/13 with exit 0 and the exact runtime test passed 1/1 with exit 0. Signed-off-by: Tapish Khandelwal <tapishkhandelwal13@gmail.com>
Signed-off-by: Tapish Khandelwal <tapishkhandelwal13@gmail.com>
Apply the existing tolerant XML numeric-reference sanitizer at both shipped native parser boundaries before quick-xml sees Tally bytes. Raw-byte experiment before the fix: bill party/reference and ledger name/parent decoded  into U+0004; bill and ledger amount fields rejected it as InvalidAmount. Numeric fields remain fail-closed. Red before fix: both focused text-field tests exited 101 and observed U+0004 instead of the replacement marker. Mutation proof bypassing each sanitizer also exited 101 for both parsers. Restored focused parser suite: 16 passed, exit 0. Rust 1.96.0; cargo fmt check exit 0.
Retain the opening GUID-verified company extent, repeat the same paired extent probe after the currency read, and reject any book drift before parsing or returning currency. Red before fix: the synthetic twelve-request identity test returned INR after only the opening extent and currency pair, exit 101. Mutation proof disabling the closing comparison again returned INR and exited 101. Restored focused test rejected the changed LASTVOUCHERDATE, exit 0. Rust 1.96.0; cargo fmt check exit 0.
Reseal only the surfaced runtime digest after the closing identity probe change. Claims, evidence, and trusted-evidence-keys remain byte-identical to the preserved PR2 head. Compatibility gate: exit 0, unknown_claims=11, evidenced_claims=0. Claims/evidence/trusted identity checks: exit 0.
89f828c to
f83bfb5
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f83bfb50a3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| //! That matters because of the trap recorded in the hub as "a fixture must | ||
| //! prove its own premise": a test whose expectations are copied from the | ||
| //! implementation it checks will survive the implementation being wrong. |
There was a problem hiding this comment.
Replace the private-hub citation with a public source
Because this test is committed publicly, describing its premise as “recorded in the hub” sends readers to a non-public source and leaves the claimed test oracle unauditable; repository guidance explicitly says a citation into a private document is worse than none. Move the rationale into TALLY_PROTOCOL_REFERENCE.md and cite its section, or keep this explanation self-contained.
AGENTS.md reference: AGENTS.md:L56-L57
Useful? React with 👍 / 👎.
| open_receivable_bill_count, | ||
| ageing_bill_counts, | ||
| top_parties, | ||
| source_voucher_count: 0, |
There was a problem hiding this comment.
Stop reporting native reads as voucher verification
On every default outstandings read this is now zero because the native path reads bills reports and ledgers rather than vouchers, but the unchanged OutstandingsScreen.tsx line 143 renders it as “0 vouchers verified,” while lines 158-162 and 231-235 claim that voucher segments are being checked twice. A successful read therefore describes a verification path that did not run; expose a source/verification mode in the result and render native-report provenance instead.
Useful? React with 👍 / 👎.
| total_bytes, | ||
| )?; | ||
|
|
||
| Ok(OutstandingsLoadResult::Complete { |
There was a problem hiding this comment.
Withhold Complete when the overdue cross-check disagrees
When a receivable row's BILLOVERDUE differs from Bridge's independently computed due-date age, compute_native_outstandings records a nonzero overdue_crosscheck_mismatches, but this path discards that signal and unconditionally publishes Complete. On a Tally version with different ageing behavior, or whenever date resolution selected the wrong date, users therefore receive ageing buckets even though the response itself contradicts them; return Partial or surface the mismatch instead of silently treating the result as verified.
AGENTS.md reference: AGENTS.md:L149-L152
Useful? React with 👍 / 👎.
| self.fetch_outstandings_native( | ||
| config, | ||
| company, | ||
| expected_company_guid, | ||
| as_of, | ||
| currency_assertion, | ||
| ) |
There was a problem hiding this comment.
Gate the native path on a qualified Tally profile
In the default build every outstandings request reaches the native implementation without checking product, release, licence mode, or third-party TDL state. However, docs/tally/TALLY_PROTOCOL_REFERENCE.md:25-38 establishes these report semantics only for TallyPrime Edit Log 7.0 Educational with no custom TDL and explicitly says they are not established for licensed mode, standard TallyPrime, ERP 9, or custom TDL; OutstandingsScreen.tsx also gates only on GUID and the operator's INR assertion. Those unqualified environments can therefore receive Complete whenever their response happens to satisfy this single-SKU parser, so require qualified capability evidence or return Partial before dispatching the native flow.
AGENTS.md reference: AGENTS.md:L86-L90
Useful? React with 👍 / 👎.
Reject paired-empty, whitespace-only, and self-closing BILLPARTY values at the raw native-response boundary with the distinct bills_fixed_empty_billparty error. The synthetic raw-byte regression pins the former overstatement: a 100 bill under the empty key plus the named ledger's 100 residual disclosed 200 instead of failing closed. Mutation proof: disabled the paired/whitespace guard, then ran 'rustup run 1.96.0 cargo test --locked -p bridge-tally-protocol --test native_outstandings bill_party -- --nocapture'. It exited 101: the raw-byte case reported the double-counted total 200, and the whitespace case returned a NativeBillRow with an empty party. Restoring the guard made all 18 native_outstandings integration tests pass with exit 0.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c6556f9070
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /// unallocated remainder was Rs 2.79 crore -- so a screen showing only | ||
| /// the bills would be short by 96% with nothing to indicate it. | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| unallocated_total: Option<ExactDecimal>, |
There was a problem hiding this comment.
Surface unallocated exposure in the outstandings screen
For any company with On Account postings, the default native path puts that exposure only in unallocated_total/unallocated_by_party, while OutstandingsScreen.tsx still types and renders only report.receivable_total, report.payable_total, and report.top_parties, all of which contain named bills only. The backend comment immediately above notes a measured book where this makes the visible result 96% short, yet the command returns Complete; include the residual directions in the displayed totals or render them as an explicit additional exposure section.
AGENTS.md reference: AGENTS.md:L86-L86
Useful? React with 👍 / 👎.
| .await | ||
| .map_err(tally_runtime_command_error)?; |
There was a problem hiding this comment.
Keep sweeping after one company read fails
When one company produces a company-specific runtime error, such as a GUID mismatch, unloaded book, or malformed native response, this ? returns from the entire all-companies command, discarding already collected entries and skipping every later company. That contradicts the command's stated per-company failure behavior; convert these failures into a per-entry error/Partial result so one unreadable book does not terminate the sweep.
Useful? React with 👍 / 👎.
| bill_date, | ||
| due_date, | ||
| closing_balance, | ||
| tally_overdue_days: row.overdue, |
There was a problem hiding this comment.
Require every bill row to carry BILLOVERDUE
If an unsupported or structurally partial response omits BILLOVERDUE from a row but still closes the envelope, overdue_seen remains false and this converts the absence to tally_overdue_days: None, which is indistinguishable from the legitimate present-but-empty field. The parser then accepts the row and the runtime can publish Complete without the documented cross-check field; reject rows where overdue_seen is false while continuing to allow an explicitly empty element.
AGENTS.md reference: AGENTS.md:L117-L118
Useful? React with 👍 / 👎.
| let data = xml | ||
| .find("<DATA>") | ||
| .and_then(|start| { | ||
| xml[start..] | ||
| .find("</DATA>") | ||
| .map(|end| &xml[start + 6..start + end]) | ||
| }) | ||
| .unwrap_or(""); |
There was a problem hiding this comment.
Reject failed currency collection responses
When Tally returns STATUS=0, a malformed response, or a response without BODY/DATA, this fallback parses an empty slice and returns a successful CompanyCurrency with zero rows instead of reporting that the request failed. detect_tally_base_currency therefore cannot distinguish a valid empty result from a failed currency probe; parse and require the successful collection envelope before inspecting its rows.
AGENTS.md reference: AGENTS.md:L143-L145
Useful? React with 👍 / 👎.
| ledger.bill_wise_on | ||
| || ledger | ||
| .parent | ||
| .as_deref() | ||
| .is_some_and(|parent| matches!(parent.trim(), "Sundry Debtors" | "Sundry Creditors")) |
There was a problem hiding this comment.
Resolve party ledgers through the group hierarchy
When a debtor or creditor ledger is placed under a custom subgroup and has bill-wise tracking disabled, its immediate PARENT is the subgroup name and bill_wise_on is false, so this predicate drops it. That ledger also has no native bill rows, meaning its entire closing balance disappears from the residual calculation while the runtime still returns Complete; fetch and resolve group ancestry or another stable primary-group identity instead of matching only the immediate English parent name.
Useful? React with 👍 / 👎.
Keep paired-empty and whitespace-only BILLREF values as distinct parsed bill rows, because the raw-byte investigation found no reference-keyed deduplication, grouping, totals, residual joins, or filenames. Normalize only the client-facing OpenBillRow reference to 'No reference reported'; self-closing BILLREF remains rejected by the native parser. Red proof: rustup run 1.96.0 cargo test --manifest-path src-tauri/Cargo.toml raw_empty_bill_references_preserve_each_amount_and_get_explicit_label -- --nocapture exited 101 after proving two rows and exact total 100, with actual references ["", ""] instead of the disclosure label. Mutation proof: disabled only the blank-reference substitution and reran the same command. It exited 101 at the reference assertion while row count and exact total still passed. Restoring the substitution made the focused test pass, and rustup run 1.96.0 cargo test --manifest-path src-tauri/Cargo.toml tally::runtime::tests --lib passed all 20 runtime tests with exit 0.
Record the PR2 runtime digest for the missing BILLREF presentation disclosure. Compatibility claims, evidence, and trusted-evidence keys remain unchanged. Compatibility gate: exit 0, unknown_claims=11, evidenced_claims=0. Claims/evidence/trusted identity checks: exit 0.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e15275ed99
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| &receivable_rows, | ||
| &payable_rows, | ||
| &ledger_rows, | ||
| AgeingAnchor::DueDate, |
There was a problem hiding this comment.
Disclose the hard-coded due-date ageing basis
When a bill has a credit period, selecting DueDate here can place it in a different bucket from Tally's valid “Ageing by Bill Date” view, but OutstandingsScreen.tsx renders only a generic “Receivable ageing” label and the result carries no anchor field. docs/tally/TALLY_PROTOCOL_REFERENCE.md:1170-1181 explicitly records that both bases are valid and requires the tool to state which it used, so serialize and display the selected basis to prevent unexplained bucket discrepancies.
AGENTS.md reference: AGENTS.md:L174-L179
Useful? React with 👍 / 👎.
| _ => {} | ||
| } | ||
| } | ||
| Ok(TallyCompany { name, guid }) |
There was a problem hiding this comment.
Reject company collection rows that omit the GUID
When a STATUS=1 company row omits the requested nested GUID—for example because the responder ignored that fetch field—this returns a successful TallyCompany with guid: None. company_discovery_evidence consequently marks the collection supported and skips the legacy fallback, while the frontend treats the nonempty list as trusted and cannot save any selection because saving requires a GUID; fail this parser so the fallback/bootstrap path remains reachable.
AGENTS.md reference: AGENTS.md:L110-L118
Useful? React with 👍 / 👎.
| let amount = row | ||
| .closing_balance | ||
| .abs() | ||
| .map_err(|_| NativeOutstandingsError::ArithmeticOverflow)?; |
There was a problem hiding this comment.
Reject bill rows whose sign contradicts the report
If a Bills Receivable response contains a positive BILLCL—or the symmetric Bills Payable response contains a negative one—these loops erase the contradiction with abs() and publish the amount under whichever side was requested. Because the native response carries no report identifier, a swapped, mis-scoped, or unsupported response can therefore become plausible Complete totals instead of failing closed; validate the expected debit/credit sign before computing magnitudes.
AGENTS.md reference: AGENTS.md:L110-L118
Useful? React with 👍 / 👎.
| || trimmed.contains('/') | ||
| || trimmed.contains('\\') | ||
| || trimmed.contains("..") | ||
| || trimmed.starts_with('.') |
There was a problem hiding this comment.
Sanitize export names for Windows filesystem rules
On Windows, a company-derived file name containing characters such as :, ?, *, <, >, or | passes this validation even though those characters are accepted by the company-name validator but invalid in ordinary Windows file names; : can additionally select an NTFS alternate data stream instead of creating the visible download. Such companies therefore get a failed or hidden export, so normalize Windows-invalid characters and reserved device basenames before writing.
AGENTS.md reference: AGENTS.md:L187-L188
Useful? React with 👍 / 👎.
Resolve nested debtor and creditor ancestry with the existing GUID-bound group export, reject structural and sign contradictions from raw response bytes, serialize the ageing anchor, verify currency for every company in a sweep, and reserve portable report filenames without overwriting prior exports.Operational profile disclosure: the native path now performs four paired report reads rather than three by adding Group Export V1. This adds two group-export HTTP requests plus the existing intervening health checks and group-response bytes/latency; no profile definition changes.Red-before-fix evidence included compile failure for the missing group ancestry contract and failing raw-byte tests for absent BILLPARTY/BILLOVERDUE, sign contradictions, missing collections, ambiguous Rs. currency identity, and missing company GUIDs.Mutation proof: ancestry bypass, sign-check removal, GUID acceptance, Rs.-symbol acceptance, and truncating download writes each failed their focused regression with exit 101.Verified with Rust 1.96.0: bridge-tally-protocol full suite and bridge --lib (245 tests), exit 0.
Bind the repaired native outstandings boundaries and write-canary documentation to the exact local source surface. Claims, evidence, and trusted keys are unchanged.Compatibility gate: exit 0, unknown_claims=11, evidenced_claims=0. Pre-reseal negative control: surface_file_changed, exit 1.
Group ledger and hierarchy inputs into one same-read snapshot so nested-party classification remains explicit without an eight-argument computation boundary. Clippy red: exit 101 before this refactor; focused native suites and default all-target Clippy pass at exit 0.
Stacked on
chore/tools-workspace-split— review only this PR's own diff.Local gate green at this level: workspace tests and clippy in both feature configurations, fmt, tools tests, live-read boundary, fixture byte integrity, licence inventory, tsc, production build, and the workflow-consistency gate.
CI is NOT proven: the GitHub-hosted Windows and bundle-smoke jobs have never run, and one read-transport harness test fails deterministically on macOS with an unknown ubuntu outcome.