Skip to content

Add the lab-only Tally write surface behind five gates - #407

Merged
lamemustafa merged 18 commits into
masterfrom
lab/inventory-rebuild
Sep 15, 2026
Merged

lamemustafa merged 18 commits into
masterfrom
lab/inventory-rebuild

Conversation

@lamemustafa

Copy link
Copy Markdown
Owner

Brings the audit-sprint lab surface onto master. It has been developed on a
branch that was deliberately kept local until it was tested and its safety
gates were verified.

What this is

A lab-only, additive surface for driving a Tally instance during the audit
sprint: guard machinery, one read tool (lab_read_inventory), and two write
tools (lab_import_masters, lab_import_vouchers).

It cannot execute a write for anyone who does not deliberately enable it

Five independent gates, each checked against this branch's tree rather than
taken from a comment:

  1. No default feature. src-tauri/Cargo.toml has no default = [...]
    list at all; lab-writes = [] is pure opt-in and nothing pulls it in.
  2. No workflow enables it. None of the six workflow files names the
    feature or uses --all-features.
  3. A gate on the gate. src-tauri/tests/lab_writes_ci_gate.rs fails the
    build if any workflow ever does, runs unconditionally regardless of which
    features the test binary was built with, and includes a tripwire proving
    the check itself can fail.
  4. Not compiled by default. agent.rs declares the module under
    #[cfg(feature = "lab-writes")], so a default build does not compile it.
  5. Refuses at runtime even when compiled in. Every tool requires
    BRIDGE_LAB_WRITES=1, the endpoint pinned to port 9001, and
    BRIDGE_LAB_TARGET_GUID / BRIDGE_LAB_DENY_GUIDS both present and
    well-formed — re-read on every call, not cached. Every write batch
    re-verifies the loaded-company set and the target's identity immediately
    before dispatch.

Production write guards (agent_import.rs, agent_import_post.rs,
approved_import.rs) are untouched; this adds a parallel surface rather than
changing those.

Read-path defects this found, already on master

Building it against a real book surfaced four defects that no synthetic
fixture could reach, each losing an entire window rather than one voucher.
Three are already fixed on master (#378/#389, #387 and #388 via #393); the
fourth, #379, is fixed on this branch by the most recent merge here and stays
open until this lands.

Notes for review

  • The compatibility surface is resealed against master's pin list, which grew
    by four entries in Decide which proposed vouchers are already in the book #294. scripts/reseal.sh --verify reports current.
  • The Rust third-party inventory is regenerated; this branch had added
    sha1_smol without it. Matches 365 locked components.
  • The lab module's own files are deliberately not pinned in the
    compatibility surface: that surface attests compatibility claims, and this
    module states in its own header that no signed evidence exists for anything
    it reads.

Local verification: cargo fmt --check clean, 936 default-feature tests,
1036 with --features lab-writes, tools workspace 13/13 suites, dependency
inventory clean.

🤖 Generated with Claude Code

t and others added 16 commits September 14, 2026 01:50
Audit-sprint 2026-09-14 Phase 3.1. Not a default feature; adds no
dependencies. A CI gate test (next commit) fails the build if any
.github/workflows file ever enables it.

Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
Gate G4 (audit-sprint 2026-09-14 Phase 3.1). Scans every
.github/workflows/*.yml|yaml for the feature name (either spelling) or
--all-features and fails with the offending line if found. Runs
unconditionally, independent of which features the test binary itself
was built with. Includes a tripwire test proving the check can fail.

Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
Audit-sprint 2026-09-14 Phase 3.1. Adds the agent_lab module
declaration (cfg-gated on lab-writes) and, additive alongside the
existing tools, the lab_read_inventory dispatch arm and catalog entry.
Registration requires both the compiled feature and BRIDGE_LAB_WRITES=1
(checked fresh per catalog build, not cached at startup); the internal
tool registry hides it from existence checks the same way when the env
var is unset, and tool_payload additionally refuses in-process calls
with lab_writes_disabled. No change to any production write path
(build_import_xml/post_import/agent_import_post.rs/approved_import.rs).

Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
Audit-sprint 2026-09-14 Phase 3.1/3.2. Adds
render_agent_lab_inventory_vouchers, a thin lab-only wrapper over the
existing render_windowed_vouchers machinery with an
ALLINVENTORYENTRIES.* FETCH list in place of vouchers'/ledger_movement's
ALLLEDGERENTRIES.* -- reuses the same windowing (and therefore the same
window_honoured corroboration path) rather than a parallel one.
Compiled only behind lab-writes.

Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
Audit-sprint 2026-09-14 Phase 3.1/3.2. Additive, lab-writes-only module:

- Runtime guards, all fail-closed with explicit error codes:
  BRIDGE_LAB_WRITES=1, BRIDGE_TALLY_PORT=9001, BRIDGE_LAB_TARGET_GUID
  and BRIDGE_LAB_DENY_GUIDS (comma list, both well-formed GUIDs).
- admit_lab_target: reserved for the Phase 3.5 lab writer -- re-verifies
  the port/env guards, that the observed loaded-company set includes
  the target and excludes every deny GUID
  (lab_source_company_loaded/lab_target_not_loaded), and the target
  identity (4 fields) via the existing verified_company. Every request
  this module builds renders SVCURRENTCOMPANY from that verified
  identity's exact display name, making the SVCURRENTCOMPANY-equals-
  target requirement structural rather than a separate check. Not
  called by lab_read_inventory (a read does not need the target to be
  the company read, or to be the company currently loaded) -- kept for
  the next pass to reuse.
- lab_read_inventory (read-only): units, godowns, stock groups, stock
  items (parent, base unit, opening qty/rate/value, GST/HSN fields as
  returned, unclassified -- no signed compatibility evidence exists yet
  for any of them), and inventory entries per voucher (with nested
  batch allocations) for a date window, reusing window_honoured.
- Every lab request/response persisted under data_dir/lab/, named and
  manifested by their own sha256 (contrast egress_log, which never
  persists raw bodies).
- Unit tests on synthetic fixtures only (BRIDGE CORPUS GST-style
  shapes), no client data, no live Tally connection.

Does not modify agent_import_post.rs or approved_import.rs.

Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
Audit-sprint 2026-09-14 Phase 3.4/3.5 groundwork. Adds
TallyRuntime::post_lab_import, a lab-only method that posts already-built
import XML directly to the Tally XML gateway through the same serialized
session queue and retry policy as every other operation (post_probe_xml),
but without the native-approval / durable-dispatch-ledger machinery
post_approved_import layers on top for the production Journal path. That
machinery is the production path's safety (one human-approved Journal at a
time); the lab writer's safety is its caller's admit_lab_target re-check
before every batch, not this method.

Compiled only behind the lab-writes feature. Does not modify
agent_import_post.rs or tally/approved_import.rs.

Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
Audit-sprint 2026-09-14. Two lab-only write tools, feature+env gated the
same way as lab_read_inventory, both re-verifying admit_lab_target's
loaded-company/deny-list/target-identity guard before every batch:

- lab_import_masters: creates units, godowns, stock groups, groups, ledgers,
  stock items (in that plan order) from the book model's masters section.
  Refuses before any write if the target already carries a same-name master
  under any requested kind (the Create-overwrite trap, TALLY_PROTOCOL_
  REFERENCE.md Sec 9.4), using the master-name fold measured on licensed
  TallyPrime 7.1 (Sec 9.4d). Batches of <=200; every batch is read back
  field-by-field (name, parent, opening balance/qty, GST fields) and the
  call stops on the first mismatch.
- lab_import_vouchers: creates Journal/Payment/Receipt/Contra (own renderer,
  not agent_import.rs's -- see module doc for why) plus accounting- and
  invoice-mode Sales/Purchase/Credit Note/Debit Note, sorted by date and
  batched at <=100. Before sending a batch, reads its date window back and
  matches every voucher by type/date/ledger-amount plus a narration marker
  or voucher number; a fully-matched batch is skipped (resume), a partial
  match stops rather than guessing, only an unmatched batch is sent. Every
  sent batch is read back the same way; stops on the first mismatch.

Group/Unit/Godown/StockGroup/StockItem master XML and every invoice type but
Sales are UNVERIFIED for the gateway (no live capture in this repository) --
documented in the module's own doc comment and this worker's receipt/report.

Unit tests (agent_lab_import_tests.rs, 33 tests): golden XML fixtures for
every master kind and every voucher shape, the Sec 9.4d name-fold
(including the negative cases: en dash/underscore not folded, NFC/NFD not
normalised), read-back mismatch detection, resume matching (marker, voucher
number, cancelled-voucher exclusion, content-only rejection per Sec 9.3),
and the book-model parser (inline JSON and book_path file). cargo test
--features lab-writes --lib: 962 passed, 0 failed. clippy -D warnings clean
both feature states. cargo fmt clean. Release bridge_mcp --features
lab-writes built; sha256 recorded in the worker receipt.

Does not modify agent_import_post.rs or tally/approved_import.rs (diff
against origin/master is empty).

Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
…se 3.4)

lab_import_masters previously refused on ANY same-name master, including
Tally's own auto-created defaults every new company already has (ledger
Cash under Cash-in-Hand, Profit & Loss A/c under the reserved primary root,
and any reserved Group) -- the first live rehearsal attempt hit exactly
this refusing on Cash/Profit & Loss A/c collisions with zero writes made.

- is_default_ledger/is_default_group: recognise a default by name *and*
  observed parent/RESERVEDNAME, so a renamed or relocated same-name master
  still falls through to the ordinary collision refusal.
- A default is never (re-)Created (Create-overwrite trap); for Ledger, a
  partial Alter carries only a genuinely-changed writable field
  (OPENINGBALANCE only -- GST fields are Alter-inert per §8.3, never
  offered). Defaults are included in the mandatory read-back diff.
- Parent comparison normalises Tally's reserved-primary marker via
  bridge_tally_protocol::is_tally_reserved_root (reused, not reinvented)
  without ever writing the marker back.
- New guard found while inspecting the rehearsal book: a requested Group
  literally named with the sanitized U+0004 marker (the self-referential
  root, mis-captured by build_book.py as a "custom" group) is refused
  outright -- it never collision-matches Tally's own plainly-named
  "Primary" row, so without this it would have been silently Created with
  a garbled name.

10 new unit tests (default skip, default opening alter, true collision
still refused, reserved parent normalisation, reserved-root name guard).
Production write guards (agent_import_post.rs, approved_import.rs)
untouched -- diff vs origin/master is empty.
…eporting

The 2026-09-14 10:10 IST rehearsal sent 17 ledger Creates to BRIDGE
REHEARSAL and Tally answered CREATED=0 ERRORS=0 EXCEPTIONS=17 (no
mutation). Diffing the sent request against the shape this exact
company's masters were live-created with on TallyPrime 7.1
(babul-masters-complete.xml) found four divergences, all fixed in the
master renderers (ledgers, and the same conventions applied to
groups/units/godowns/stock groups/stock items and the default-ledger
partial Alter):

- every renderer now includes a <NAME> child mirroring the NAME
  attribute (missing before)
- <ISBILLWISEON> is now always explicit (defaulting to No), never
  omitted
- <OPENINGBALANCE>/<OPENINGVALUE> are only emitted when non-zero,
  matching the proven capture's zero-balance ledgers
- <TAXTYPE> is only emitted when the book value is a real GST/duty
  classification (not empty/"Others") and the ledger's parent is
  Duties & Taxes -- the rehearsal request sent TAXTYPE=Others on every
  ledger including a bank account and a wages ledger
- xmlns:UDF="TallyUDF" is dropped everywhere: nothing here ever emits
  a UDF-namespaced element, so the declaration bound to nothing

Also: lab_import_masters/lab_import_vouchers now check the import
response's ERRORS/EXCEPTIONS counters immediately after posting, before
the mandatory read-back, and report an outright rejection explicitly as
state "tally_rejected" with the full counter set and any <LINEERROR>
text -- instead of proceeding to a read-back whose only signal is "not
found", indistinguishable from a request that was never sent.

Golden XML tests updated to the proven shape, with three new fixtures
derived byte-for-byte from babul-masters-complete.xml (HDFC Bank 1649,
Sales, Sri Ram Cables Private Limited), plus a TAXTYPE-suppression test
and coverage for the new tally_rejected reporting path using the exact
captured rehearsal response.

CODE ONLY -- no live Tally writes performed by this change.

cargo test --features lab-writes: 985 lib + 4 + 2 passed, 0 failed
cargo test (no feature): passed, 0 failed
cargo clippy --features lab-writes --all-targets -- -D warnings: clean
cargo clippy --all-targets -- -D warnings: clean (module is
  feature-gated out entirely without lab-writes)
cargo fmt -- --check: clean
Production write guards (agent_import.rs, agent_import_post.rs,
  tally/approved_import.rs): unchanged vs origin/master
Release build (bridge_mcp --features lab-writes):
  sha256 485289ff2b2a4bafec0ddeb87ae8b8bca7b3ba295db56803dc4f15d2319905c5

Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
…master resume

Live rehearsal follow-up (2026-09-14): the prior commit's shape fix
worked -- Tally CREATED all 17 ledgers, counters clean -- but the run
then stopped on false mismatches from the lab read-back parser:
parents observed as "Duties  Taxes" / "Loans  Advances (Asset)" (note
the double space -- the `&amp;` entity dropped entirely, not left
literal). Production `ledger_masters` against the same company
correctly reports "Duties & Taxes"; the bug was local to the lab
read-back parsers.

Root cause: quick_xml delivers a general entity/character reference
(`&amp;`, `&#4;`, ...) as its own `Event::GeneralRef`, separate from
the surrounding `Event::Text` events -- every other native-collection
parser in this crate (agent_voucher_parse.rs, agent_change_parse.rs,
agent_company_checkpoint.rs, source_draft_xml.rs) already handles this
event via `decoded_agent_reference`; the lab read-back parsers did not,
so the reference was silently dropped by their catch-all match arm.

Fixed in all three lab read-back parsers (agent_lab.rs's
parse_lab_master_rows and parse_lab_inventory_vouchers,
agent_lab_import.rs's parse_voucher_readback_nested) by adding the
identical GeneralRef-handling arm the production parsers already use.
find_readback_row/readback_mismatches needed no change -- they consume
already-decoded rows, so fixing the parser fixes them too.

Also, idempotent resume for lab_import_masters: a same-name master
already in the target is no longer an automatic collision. If it
matches the book on every field this tool would itself have written
(parent, bill-wise flag, opening balance, and GST fields only where the
book specifies a real classification -- new
already_present_verified_mismatches/ledger_already_present_mismatches,
reusing diff_unit/diff_parented/diff_stock_item for non-ledger kinds),
it is reported as already_present_verified and excluded from the
Create batch rather than refused or re-Created. Any field difference
still falls through to the ordinary lab_master_already_exists refusal.
An all-already_present_verified result still reports ok:true with
nothing new created, so a resumed run proceeds straight to vouchers.

run_rehearsal.py (Brain, not part of this repo): added operator-visible
logging for the already_present_verified/idempotent-resume case: the
existing `ok` gate already treats it as success, this only narrates it
instead of looking like a suspiciously quiet masters step.

New tests: entity-decoding coverage for parent names (master read-back,
using the exact "Duties & Taxes"/"Loans & Advances (Asset)" case) and
for party/ledger names and narration (voucher read-back); idempotent-
resume coverage for a clean match, a bill-wise mismatch, an opening-
balance mismatch, a real-GST-type mismatch, TAXTYPE="Others" correctly
ignored outside Duties & Taxes, a non-ledger kind via the shared diff
functions, and an end-to-end classification test mirroring the
precheck loop for both the match and the mismatch path.

CODE ONLY -- no live Tally writes performed by this change.

cargo test --features lab-writes --lib: 994 passed, 0 failed (77 in
  agent::lab::*::tests, up from 68)
cargo test --features lab-writes (full): passed, 0 failed
cargo clippy --features lab-writes --all-targets -- -D warnings: clean
cargo clippy --all-targets -- -D warnings: clean (lab module is
  feature-gated out entirely without lab-writes)
cargo fmt -- --check: clean
Production write guards (agent_import.rs, agent_import_post.rs,
  tally/approved_import.rs): unchanged vs origin/master
Release build (bridge_mcp --features lab-writes):
  sha256 1abab7b71b99780cb8cc087616171815ffe7ec55e1491ce28dfe9fbbf20bca43

Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
…rtial Alter

Second live rehearsal (2026-09-14): the prior commit's entity-decoding fix
worked -- lab read-back no longer drops the & entity -- but the run then
refused with lab_master_already_exists on Ledger:Profit & Loss A/c.

Root cause: that same fix now correctly decodes the XML numeric reference
&#4; to its literal Unicode scalar value (raw U+0004), but
is_default_ledger's reserved-primary check called
bridge_tally_protocol::is_tally_reserved_root, which only strips its own
sanitized placeholder ("\u{fffd}#4;"), not the raw control character --
so a live-correct read-back of "\u{4} Primary" no longer matched, and
Profit & Loss A/c was misclassified as a true collision instead of
Tally's own default. book.json separately carries a third spelling
("\u{fffd}#4; Primary", literally that placeholder text) via its own
Python-side reimplementation.

Fixed with is_reserved_root_any_spelling, a new function local to
agent_lab_import.rs recognising all three spellings (raw control
character, sanitized placeholder, undecoded "&#4;" text) and wired into
both of this module's reserved-root comparisons. Deliberately NOT a
change to bridge_tally_protocol::is_tally_reserved_root itself: that
function's narrower definition is a considered, tested choice for the
production group-ancestry walk (group_ancestry.rs's own
every_refusal_is_distinguishable_and_none_is_an_answer test pins an
unrecognised raw marker there as a safe refusal), so widening the shared
function would have silently changed behaviour for every one of its
other consumers. build_book.py's own is_tally_reserved_root is widened
the same way (no equivalent narrower-by-design split to preserve on the
Python side).

Also, a real data gap the coordinator found: book.json had
is_billwise_on=null for every ledger (the source snapshot never fetched
ISBILLWISEON), so the 17 created ledgers were bill-wise No in Tally
while 53 voucher lines carry bill allocations against two of them --
importing vouchers would have silently dropped those bills. Fixed:

- snapshot.py/direct_read.py now fetch ISBILLWISEON/ISCOSTCENTRESON/GST
  fields directly from Tally for future snapshots (a raw Ledger Export
  request, bypassing the ledger_masters MCP tool's fields=compliance
  profile, which does not return them at all).
- For the already-closed rehearsal source, build_book.py infers
  is_billwise_on=true for any ledger with voucher bill allocations
  (Tally would never have accepted BILLALLOCATIONS.LIST against a
  bill-wise-No ledger), records is_billwise_on_basis for provenance, and
  gained a new validator rule (find_billwise_violations) failing the
  build if any ledger with bill allocations is not bill-wise Yes.
  book.json.gz regenerated from the existing (source-closed) snapshot --
  no live Tally read -- TB validator still passes, exactly the two
  parties the coordinator named now resolve to is_billwise_on=true.

Finally, lab_import_masters can now reconcile an existing target ledger
via partial Alter instead of refusing it, when the difference is
confined to writable fields (parent already matches): ledger_alter_fields
is widened from OPENINGBALANCE-only to also offer ISBILLWISEON,
PARTYGSTIN, and TAXTYPE (gated as at Create) -- excluding only
GSTDUTYHEAD, which TALLY_PROTOCOL_REFERENCE.md §8.3 specifically measured
as settable at Create but silently not updated at Alter. The three
newly-offered fields have no equivalent citation; an earlier version of
this function excluded them anyway, generalising the one measured field
to three unmeasured ones, so they are now attempted and the mandatory
post-Alter read-back (via ledger_already_present_mismatches, the same
full-equality check the idempotent-resume precheck uses) is what actually
proves whether Tally applied them. A parent difference (ledger_parent_mismatch)
still refuses unconditionally. The Tally-default-ledger Alter path
(Cash/Profit & Loss A/c) is unified with this same mechanism rather than
kept as a separate, narrower implementation. Tool result now reports
created/already_present_verified/altered_verified per master name;
refused masters remain reported via the existing lab_master_already_exists
error path (collisions are collected across every kind before any single
refusal is returned).

CODE ONLY -- no live Tally writes performed by this change.

cargo test --features lab-writes --lib: 1001 passed, 0 failed (84 in
  agent::lab::*::tests, up from 77)
cargo clippy --features lab-writes --all-targets -- -D warnings: clean
cargo clippy --all-targets -- -D warnings: clean (lab module is
  feature-gated out entirely without lab-writes)
cargo fmt -- --check: clean
Production write guards (agent_import.rs, agent_import_post.rs,
  tally/approved_import.rs): unchanged vs origin/master
Release build (bridge_mcp --features lab-writes):
  sha256 04cca1188b488d8c26bcd8e2b1600c4b921e15ac7a9ccf7277385f798809c4b9

Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
…ucher mismatch detail

2026-09-14 rehearsal batch 1 stopped with `readback_mismatch`,
`verified_on_readback: 89` and no per-voucher detail. Root-caused all 11
misses: every field Tally stored (date, type, ledger lines, amounts,
narration) was correct; only VOUCHERNUMBER differed from the book's
requested value, exactly per brain/10-domains/11-tally/
tally-rewrites-what-you-import.md #6 (TallyPrime silently reassigns
VOUCHERNUMBER to its own per-type sequential series in receipt order,
supplied value discarded). Two same-date groups in the batch (Payment
20250518 #98-106, Contra 20250609 #9-11) cross a power-of-10 boundary, and
`lab_import_vouchers`'s own batch sort compared `voucher_number` as a
string ("100" before "98"), scrambling the order those vouchers were
POSTED in -- so Tally's receipt-order renumbering landed on different
values than the book's for exactly those 11.

Three fixes:

- `voucher_sort_key`: sort `voucher_number` numerically within a date, not
  lexicographically, so posting order matches the book's own order and
  Tally's receipt-order renumbering keeps lining up with it going forward.
- `lab_marker_id`: the narration marker embedded via `narration_with_marker`
  is now a deterministic UUIDv5 of `source_guid`, not a fresh `Uuid::new_v4()`
  discarded once the write call returns. The old scheme could never be
  reconstructed on a later precheck/readback, so `voucher_already_verified`'s
  marker branch was permanently dead code in practice -- confirmed by this
  rehearsal's own evidence. `voucher_already_verified` also gained a third
  alternate identity key, `narration_text` (the stored narration minus any
  `[BRIDGE-LAB:...]` suffix), so vouchers already posted under the
  pre-fix random marker -- like the 89 verified and the 11 unverified from
  this run -- can still be recognised on resume without relying on Tally's
  reassigned VOUCHERNUMBER.
- `voucher_mismatch_detail`: `lab_import_vouchers` now reports, for every
  unverified voucher, the closest observed candidate and exactly which
  fields (voucher_number / narration_marker / narration_text / each ledger
  line) differ from the book -- both on `readback_mismatch` and on
  `partially_verified_uncertain`. Previously only a bare count was reported.

Verified against the rehearsal evidence
(~/Library/Application Support/Bridge/lab/, batch 1 write request +
readback response, sha256 a30a9617.../c6332bf8...): all 100 batch-0 and
100 batch-1 vouchers are present in BRIDGE REHEARSAL exactly once, no
duplicates, no cancellations; the 11 differ from the book only in
VOUCHERNUMBER, matching the receipt-order mapping the scrambled posting
order predicts exactly.

8 new tests (agent_lab_import_tests.rs): `lab_marker_id` determinism,
marker-only and narration-text-only resume matches, a narration-text
collision still refused without matching ledger content, numeric
`voucher_sort_key` ordering (the exact 98/99/100 boundary), and
`voucher_mismatch_detail`'s not-found and field-diff shapes.

cargo test --features lab-writes --lib: 1009 passed, 0 failed.
cargo check --lib (default features, lab-writes off): clean -- guard
unchanged. cargo clippy --features lab-writes --lib -- -D warnings: clean.
cargo fmt: clean. Release binary rebuilt (bridge_mcp,
--features lab-writes): sha256
d337663039a912f11f9803a97a95ee865132c778011d63151c63f87c359c3bfc.

No Tally writes performed; diagnosis is read-only evidence analysis plus
this code fix. See
brain/50-projects/audit-sprint-2026-09-14/snap/rehearsal/batch1_diagnosis.md
for the full per-voucher table and resume plan.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…inventory

This branch had edited five pinned files without resealing, and added a
dependency without regenerating the third-party inventory. Both are gates, so
the branch was failing CI on its own: a control run on the bare base failed
job-for-job identically to a pull request stacked on it, which made a stacked
PR's own result unreadable.

- `scripts/reseal.sh` (ordinary sequence; the pin list itself did not change,
  only the contents of already-pinned files). `rehash-surface` reported exactly
  5 changed hashes, matching an independent hash of all 212 pinned entries
  against the manifest: `agent.rs`, `agent_read_profiles.rs`,
  `tally/runtime.rs`, `Cargo.toml`, `Cargo.lock`.
- `scripts/generate-rust-licenses.mjs` for the inventory, which had been
  missing `sha1_smol 1.0.1` since this branch added it. Now matches 365 locked
  components.

The lab module's own files are deliberately not pinned: the surface attests
compatibility claims, and this module states in its own header that no signed
evidence exists for anything it reads.

Order checked rather than assumed: regenerating the inventory rewrites
`THIRD_PARTY_LICENSES_RUST.txt`, which would invalidate a reseal done before
it, but that file is not among the pinned entries and `reseal.sh --verify`
still reports the surface current afterwards.

Verified: `tools` workspace 24/24 on the surface-coverage test that was the
dominant failure, `cargo fmt --check` clean, default-feature lib suite 915
passing, dependency inventory clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three parsers in the lab surface tracked a bare `current_tag` and appended
every text event to it, choosing the owning row from a depth-shifted `path`.
Two properties of real Tally responses defeat that, and no fixture in the
module had either, because every one was compact:

- Responses are CRLF-indented, so `trim_text(false)` delivers the whitespace
  between every pair of siblings as its own `Text` event -- 398 such runs in
  one captured inventory entry.
- They are dense with self-closing elements -- 274 in that same entry -- which
  arrive as `Event::Empty` and never updated `current_tag`.

One stale tag therefore absorbed a whole indentation run: an entry's `AMOUNT`
read back with forty-odd `"\r\n      "` fragments, a `GODOWNNAME` picked up the
whitespace closing the batch below it, and master rows grew phantom
whitespace-only fields. Separately, a master row was seeded from its `NAME=`
attribute and then had the `<NAME>` child appended onto it, giving `KgsKgs`.

Replaced with a buffer that holds an element's text and releases it only when
that element closes with no child having intervened, resolving row ownership
at `End` where the path is unambiguous. `Text`, `GeneralRef` and `CData` all
feed one buffer, so a value split across any combination rejoins in order. The
`NAME=` attribute became the fallback for a row with no `<NAME>` element.

Deliberately not a trim: Tally left-pads a quantity with one real space to
hold the sign position, on every `ACTUALQTY` and `BILLEDQTY` in the captured
days. Trimming would corrupt correct output while making the symptom vanish.
The fixtures carry that shape and assert it exactly.

`parse_voucher_readback_nested` in `agent_lab_import.rs` carried the identical
defect and is what the import mismatch report compares, so an `AMOUNT` growing
an indentation tail reported a false mismatch against a voucher Tally had
stored correctly. Converted to the same buffer.

Eight regression tests, each confirmed to fail against the parser as it stood
and pass after. Verified beyond the fixtures by replaying the fixed parser over
three captured inventory days -- 42 vouchers, 46 entries, 17 batch allocations,
no accumulation -- where the previous parser fails on the first entry. Those
captures are client data and stay outside the repository.

Two independent reviews. The first found `Event::CData` was being dropped,
which is what turned up the third parser. The second found the read-back test
used a nested element no assertion read, so it passed against the broken
parser; it now collides with a field the entry binds.

Does not close bridge#379: this lands on the lab branch, not master.
# Conflicts:
#	docs/tally/compatibility/compatibility-matrix.json
#	docs/tally/compatibility/compatibility-surface.json
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

t added 2 commits September 16, 2026 00:53
# Conflicts:
#	docs/tally/compatibility/compatibility-matrix.json
#	docs/tally/compatibility/compatibility-surface.json
# Conflicts:
#	docs/tally/compatibility/compatibility-matrix.json
#	docs/tally/compatibility/compatibility-surface.json
@lamemustafa
lamemustafa merged commit 579538d into master Sep 15, 2026
13 checks passed
@lamemustafa
lamemustafa deleted the lab/inventory-rebuild branch September 15, 2026 19:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant