Skip to content

Decide which proposed vouchers are already in the book - #294

Open
lamemustafa wants to merge 91 commits into
masterfrom
claude/voucher-presence
Open

lamemustafa wants to merge 91 commits into
masterfrom
claude/voucher-presence

Conversation

@lamemustafa

@lamemustafa lamemustafa commented Sep 10, 2026

Copy link
Copy Markdown
Owner

HELD for operational qualification. Source-side completeness evidence for nonempty voucher windows remains unavailable. Current local tests do not establish live presence verdicts or authorize importing vouchers. Addresses the presence review debt tracked by #317.

Functional summary

The presence contract compares proposals with an observed book window using bounded identity and diagnostic evidence. The production adapter refuses unqualified nonempty windows with presence_window_incomplete; even agreeing bounded reads do not establish source cardinality. Only the existing qualified empty-window control can construct Complete.

A supplied, observed REMOTEID conflicts with a numbered voucher whose REMOTEID is missing or different. Unread identity evidence withholds a decision before conflict classification; duplicate proposal numbers and unobserved voucher types retain their existing rules. A remote-identified row cannot settle against a contradictory absent manual number. No candidate is chosen automatically. An unread proposed REMOTEID remains the reported reason for withholding a decision even when resemblance or nondecisive-number candidates exist; those candidates remain visible. An identified voucher with a different exact voucher type reports both spellings through a voucher_type difference.

Admission caps the proposal/window product at 1,000,000 and indexed resemblance work at 5,000,000 units. The weighted bound includes posting-list walks, pooled candidates and party-key fanout, including work for identity-settled proposals. Exact observed ledger and party spellings must exist in the catalogue; raw spellings and folded comparison keys remain separate and bounded. Numbering declarations retain count and aggregate-byte limits, and internal voucher-number whitespace remains significant. A shared raw-observation budget additionally limits voucher count, 100,000 raw entries and 4 MiB of ledger/amount bytes before parsing, cloning or folding entries. The adapter applies it before allocating entry descriptors, and the public core batch boundary protects direct callers. Retained book metadata has a separate 4 MiB bound. Raw proposals have their own 100,000-entry, 4 MiB entry-byte and 4 MiB metadata budgets before conversion; their positions must be unique. The core request accepts only an opaque admitted ProposedBatch, so callers cannot concatenate separately converted vectors around admission. ADR0017 preserves voucher-number Unicode form with outer trimming only, keeps voucher-type spelling exact, and documents missing or unequal observed REMOTEID as conflict.

Test or reproduction command

Candidate 17bfe9786bf48794b0f2a0a197042e8be29f5e0e, based on master. Master 06528eeb615b827896b9649ea476034143bbf7f9 is in its ancestry.

  • Rust 1.96 cargo test --locked --manifest-path src-tauri/Cargo.toml -p bridge-tally-core --lib: 263 passed on final runtime/test bytes. Controls cover all three nondecisive candidate classes with unread REMOTEID and serialize the exact voucher-type difference.
  • Core all-target/all-feature Clippy with -D warnings and direct core-manifest rustfmt checks passed.
  • Existing controls retain actual weighted-key fanout, valid 4 MiB/next-byte and 100,000/next-entry raw boundaries, opaque batch construction and unique source positions.
  • cargo test --locked --manifest-path src-tauri/Cargo.toml -p bridge --lib agent::presence::tests: 19 passed, 1 existing authorized-live test ignored. The adapter preserves voucher-type differences through party marking. No live replay was run.
  • cargo test --locked --manifest-path tools/Cargo.toml -p bridge-tally-compatibility: 24 library and 4 binary tests passed with the shell's normal umask 022. A separate existing CLI mode-preservation defect under umask 077 is tracked for rectification; this pin change does not alter that writer.
  • The recursively executed agent_catalog.rs admission schema is now a required compatibility pin. The bound rises by exactly one for that file; 216 unique paths match actual bytes. The required-path omission regression rejects dropping it. Rehash/seal/repoint/gate and git diff --check passed.
  • Root reviewed the final reason/type implementation and tests. Independent Luna reviewed the required schema pin, exact membership and byte hashes. Hosted checks and provider review must qualify the published candidate separately.

Migration compatibility

No destructive database migration or persisted-schema change. Direct core callers must use BookWindow::from_observations and ProposedVoucher::from_inputs, which returns an opaque ProposedBatch required by PresenceRequest::new. Individual proposal construction exists only for crate tests; per-voucher and preconverted-window construction remains crate-private. Admitted batches expose read-only views, preserving the aggregate boundary. Incomplete reads remain typed refusals. The operational live-evidence gate remains open; clean status against a feature branch does not establish master acceptance.

Rollback notes

Revert the PR to remove this presence contract and endpoint extension. Preserve unknown-outcome lookup and never automatically replay financial writes. Windows interactive/package qualification and source-complete nonempty presence behavior remain unproven.

Security impact

This is a read-only comparison path. Company admission, exact catalogue coverage, bounded declarations and refusal on incomplete evidence remain enforced. No credential changes, live Tally write, import, or client financial mutation occurred in this rectification.

  • Existing component reused: shared master binder, company admission, voucher reader and compatibility tooling.
  • What is deleted: unchecked proposal-vector admission and duplicate resemblance-reporting paths; no new transport or dependency.
  • What breaks if this is not built: individual row bounds fail to constrain aggregate allocation, missing identity evidence is obscured, and admission-schema drift escapes the compatibility digest.

Windows validation evidence: native/package CI passed on the previous 6ce6e0c candidate; final-head hosted checks and interactive qualification remain outstanding.
macOS validation evidence: the local Rust 1.96 core, adapter and tools suites above passed; nonempty live qualification remains held.

lamemustafa and others added 7 commits September 10, 2026 21:45
Every failure across four document-import engagements was binding a
document's entities to the target book's masters, never reading the
document. Bridge was growing two answers to it: an MCP-private prefix
matcher that named one near-miss candidate as `exact_live_spelling`, and
a desktop screen that correctly ranks nothing but narrows nothing either.

Add `bridge_tally_core::master_binding` as the single contract both
surfaces consume, per ADR 0016. It matches an identifier embedded in a
master name before the name itself, binds only where a rule is unique on
both sides, and never resolves a near-miss: it reports candidates with
the rule that surfaced each, and no score. An empty catalogue is a typed
refusal rather than a report full of "missing".

Deletes `master_match` and `master_key` from agent_import and moves
unicode-normalization down a layer with them. The write gate is
unchanged: build_import_xml and the approved-post recheck still admit
byte-exact names only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…scale

Self-review found a real defect: a catalog name was trimmed at the
boundary, so a bound row reported a spelling the book does not contain.
The write gate compares byte-exact against Tally's own name, so that
would have refused with no explanation. Observed names are now retained
verbatim; only source names are trimmed. Names differing solely in
surrounding whitespace are an ambiguity, not a refused catalog.

Adds a characterization suite over one fabricated 200-master book with
the recorded naming pathologies. The assertion that matters is that no
entity binds to a master a human would not have chosen; the counts are
pinned underneath so loosening a threshold has to move a number.

The mutation sweep was checked against two positive controls rather than
trusted for passing: resolving a near-miss to its first candidate trips
it, and binding a lone candidate does not. Both results are recorded in
the test, so it is read as "no mutation reaches the wrong master" and
not as "no rule change can loosen binding".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rzakhani-94af40

# Conflicts:
#	docs/tally/compatibility/compatibility-matrix.json
#	docs/tally/compatibility/compatibility-surface.json
Ten review findings, read against the tree rather than taken at face
value, and all reproduced. The severe one: bind_one selected Identifier
before ExactName, so a ledger carrying a number, requested byte-exactly,
returned match_state identifier while the write gate admits exact only.
Every ledger with a phone or account number in its name was permanently
unimportable — the exact population this contract was built for. When
the two signals agree it now reports the byte-level fact.

Also: neither side trims a name any more, so a trailing space cannot
claim byte equality it does not have; digits inside a mixed code are no
longer emitted as a standalone numeric; all admitted eight-digit date
orders are excluded, not just year-first; more identifiers than the
bound is refused rather than truncated, which could hide a conflict; the
source-entity bound now covers what the source parser admits and the
desktop states whether narrowing ran; desktop candidate bytes are capped
in aggregate; a binding refusal keeps the catalogue evidence it already
read; and master_binding.rs is sealed into the compatibility surface,
which needed a deliberate one-file cap raise since it now decides
admission outcomes.

The larger finding came from running the binder over 470 real ledger
names from all 16 synthetic books. Prefix matching offered a median of
40 candidates, 63% of the catalogue, and omitted the right master a
third of the time: a truncated name reaches a whole family, and an
alphabetically capped slice of DN Party 001..120 does not contain
DN Party 057. A prefix matching more masters than the cap is now counted
and deliberately not listed. Re-measured on the same names: where
candidates are listed the right master is present in 403 of 403 rows,
median list length 2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The remaining review finding, and the one I had triaged but not fixed.
FallbackBinding::assign took an EntityBinding detached from its report
plus any catalog, so a stock-item binding could be parked against a
ledger catalog and the result carried no provenance for anything
downstream to detect.

Assignment is now a method on BindingReport taking an index into its own
entities, so an entity from another report cannot be named at all, the
catalog class is checked, and the binding carries its class forward.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A fix is a change and generates its own findings; the re-review of the
previous commits raised four, all reproduced.

An unusable ledger name in a parsed draft was skipped while the response
still claimed a complete narrowing pass, so the rows that vanished were
exactly the ones worth looking at; the pass is now reported unavailable.
The reported candidate total took the larger of the suppressed family and
the retained candidates, which under-reports when they are different
masters; it is now their union. An identifier hint reached extraction
without the bound applied to every other name. And the operator workflow
in docs/agent/README.md still told readers to correct only near_miss
rows, which now leaves a bound-but-not-exact row refused at the build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two further findings from the re-review, both reproduced.

`Purchases FY25` and `Sales FY25` both yielded the code identifier
`FY25`, so identifier-first matching bound the source to whichever
existed before it ever compared the names. A fiscal-period label
identifies a period, not a party, and is now excluded by shape; the
minimum code length also rises from four to six, since a four-character
mixed token is weak evidence of identity and the failure mode here is
money against the wrong party. Re-measured against the same 470 live
ledger names: no change to the distribution, so the tightening costs
nothing observed.

Candidate collection recounted every prefix from the start of the name,
making it quadratic in a field the source parser lets reach 4 KiB. It
now carries the character count forward in one pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-12T22:51:19.930389Z 17bfe97 New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@lamemustafa lamemustafa added the area:tally Tally integration label Sep 10, 2026
No book on the instance carried an embedded identifier: across 470 live
ledger names from all 16 loaded companies, zero yielded a numeric
identifier and exactly one a code identifier. The rule that separates
this from fuzzy matching had no live coverage at all.

Ten `MB ` ledgers now exist in BRIDGE CORPUS OPENING, parented to
Suspense A/c so no receivable, payable or ageing measurement moves, and
documented in TEST_CORPUS.md section 9 with the import method and the
company-choice reasoning. BRIDGE PROBE B SANDBOX was rejected as the
target despite the manufacturing precedent: it shares a GUID with a
second loaded company and Bridge's own reads refuse it as
company_identity_ambiguous.

Within minutes the pair sharing one identifier exposed a defect no
fabricated fixture had produced. A byte-exact request for a ledger whose
embedded number is shared with another was refused as
IdentifierConflict, making that ledger permanently unimportable, since
the write gate admits exact only. Byte equality with an observed master
name is now decisive: it names exactly one master, and an ambiguous
identifier does not undermine it. Only a decisive identifier pointing
elsewhere still outranks an exact name, and that stays a reported
conflict.

Re-measured over 485 live names, 2,330 cases: identifier binds 3 -> 11,
every uppercase mutation now binds, and where candidates are listed the
right master is present in 434 of 434 rows at a median list length of 2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@lamemustafa
lamemustafa force-pushed the claude/voucher-presence branch from 8ded397 to 7bc66ad Compare September 10, 2026 19:14

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ded39786b

ℹ️ 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".

Comment thread src-tauri/src/agent_presence.rs Outdated
Comment thread src-tauri/src/agent_presence.rs
Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs Outdated
Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs Outdated
Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs Outdated
Comment thread src-tauri/src/agent_presence.rs
Comment thread src-tauri/src/agent_presence_tests.rs Outdated
Comment on lines +315 to +317
ScenarioPlan::new(Fixture::SyntheticXml(body))
.with_encoding(WireEncoding::Utf16Le)
.with_framing(ResponseFraming::ContentLength)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Obtain live evidence before shipping the presence engine

The only end-to-end cycle feeds this hand-authored voucher window through Fixture::SyntheticXml, so it verifies the parser and decision rules against data this repository invented rather than against an actual Tally response; the ADR itself states that no verdict from this contract has been checked against a real book. Because this change adds a large production decision engine whose Absent result authorizes importing vouchers, replace or precede this synthetic cycle with captured live evidence before treating the component as shippable.

AGENTS.md reference: AGENTS.md:L79-L90

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, and not fixed — this one cannot be closed by code, so I want to be plain rather than argue it away.

You are right about AGENTS.md P1, and ADR 0017 states the same limit in its own Consequences: the fixtures are fabricated from a placeholder alphabet, and no verdict from this contract has been checked against a real book. Binding is pure computation over already-observed data, so P1's live-evidence requirement is satisfied upstream by the two qualified reads that feed it — but that argument covers the reads, not the decision rules, and it does not cover the claim that the output is useful on the 214-ledger book that motivated it.

I have surfaced this to the repository owner as a merge decision rather than resolving it myself, with the concrete next step: replay the twenty-invoice engagement that motivated the work, where fifteen should return present and the one posted short by a dropped GST head should return present with an amount difference. That is a measurement, not a test, and it belongs to someone with access to a real book.

Leaving this thread open deliberately.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accepted, and not closed. I want to be exact about what has and has not changed here rather than resolve this on adjacent work.

Where you are right. The only end-to-end cycle drives Fixture::SyntheticXml — a voucher window this repository wrote — so it exercises the parser and the decision rules against data we invented. It cannot show that a real Tally response parses into the rows this engine assumes, and the ADR says as much. For a component whose Absent result authorizes an import, that is the wrong kind of evidence to ship on.

What has changed since you filed this, and why it does not close it. Your sibling thread on WindowRead::Complete turned out to be the sharp end of the same problem: the engine was asserting that a nonempty read saw its whole window, which is precisely the assumption a synthetic fixture cannot test, because a synthetic fixture is never short. That is now corroborated against a second, wider read rather than assumed. It is a better failure mode, and it is still not live evidence.

What is blocking the rest. Both lab endpoints (9000 and 9001) went down mid-session and have not returned. I have a watch on 9001. So I cannot capture the slice now, and I am not going to argue that fixtures plus a corroboration rule add up to a live cycle.

What would close it, so it is actionable by whoever gets there first: a captured Vouchers response from a lab company with known contents, retained as bytes, replayed through this engine, with the verdicts checked against what that book actually holds — including at least one proposal that is present and one that is absent. The MB -prefixed ledgers seeded for the binder are synthetic and in a lab company, so the data stays fabricated while the response becomes real, which is the distinction your thread is drawing.

Until that exists this thread stays open, and the ADR keeps saying no verdict has been checked against a real book.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The replay ran. Against a licensed TallyPrime 7.1 Silver instance (education_mode: false), over a real book, through the shipped vouchers read profile and this decision engine.

totals: {"requested":20, "present":16, "possibly_present":0, "absent":4}
book:   {"window_voucher_count":82, "unmatched_book_vouchers":66,
         "duplicate_number_group_count":0, "unbalanced_voucher_count":0}

Fifteen invoices the book already holds came back present by manual_voucher_number, every one with an empty differences list. One short by the engagement's 36.13 came back present with an amount difference -- the finding that engagement needed and nobody had asked for. Four new-customer invoices came back absent.

Committed as replay_the_twenty_invoice_engagement on claude/presence-live-replay, branched from this PR's head. It is #[ignore]d and environment-driven, following this repository's existing live-test convention.

Three things about it you should weigh rather than take on trust.

It is read-only, deliberately. Seeding a book would need a disposable company and this MCP cannot create one, so the proposals are built from the book's own rows -- the present ones are present by construction. That makes the present arm a weaker claim than a seeded book would give: it proves the rules identify a voucher they were shown, not that they would identify one posted independently.

It inverts the shortfall. The engagement's voucher was short in the book; read-only, the harness puts the shortfall on the proposal side. The difference detected is the same one, the side missing the GST head is reversed, and the test says so in place rather than only here.

It does not close your second finding. A silently short window still needs a source-side control total; this replay is over a window whose completeness rests on the same unproven cardinality, so it is evidence about the rules, not about the read.

Two earlier runs of this harness produced wrong answers, both my fault, and one is worth recording because it looked like a finding: with the invented proposals accidentally carrying an unmodified amount, the engine returned possibly_present / resembles_book_voucher rather than absent. That was correct -- in a one-day window every row shares the date, so a known party resembles something on date and party and absent is properly withheld. I nearly wrote it up as engine behaviour worth questioning; it is the contract working, and the NoDiscriminatingCandidate pressure ADR 0017 predicted from the 470-name measurement showing up on a real book.

I am leaving this thread open for you: whether a read-only replay on a lab book meets the bar you set is your call, not mine to close.

Comment thread src-tauri/src/agent_presence.rs Outdated
@lamemustafa
lamemustafa force-pushed the claude/voucher-presence branch from 7bc66ad to 6294652 Compare September 10, 2026 19:20
lamemustafa and others added 2 commits September 11, 2026 00:54
Two findings from the latest re-review, both reproduced.

Enumerating the period shapes that must not become identifiers was a
losing game: FY25 was fixed, then APR2025 and 2025Q1 were still binding
two unrelated ledgers that merely share a period. The rule is now a
shape — every run in the token is a short alphabetic marker or a number
reading as a year or small ordinal, at most three runs — and a code
identifier additionally needs eight alphanumerics, three digits and two
letters. Requiring real length is the part that does not depend on
having thought of every label. Measured against 485 live ledger names,
exactly one yields a code identifier at all, and it still does.

The aggregate candidate budget was applied to the consumer's copy, so
the report's own clones were already allocated by then; capping the copy
bounded only the copy. The budget now lives in bind() and is spent in
entity order, and the desktop's second budget is deleted as redundant.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The voucher-presence lane needs the same fold for voucher numbers and
voucher-type names that master names use, and is exposing this function
crate-wide to get it. That is the right call — a second, subtly
different normaliser is the divergence ADR 0016 exists to end, and it
would diverge silently, agreeing on every name tested by hand and
differing on the punctuation nobody thinks to try.

Records that obligation at the function, and its corollary: changing
what this folds changes every consumer's notion of sameness at once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0c56a03765

ℹ️ 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".

Comment thread src-tauri/src/agent_presence.rs Outdated
Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs Outdated
@lamemustafa
lamemustafa force-pushed the claude/voucher-presence branch 2 times, most recently from e4004bd to bff99de Compare September 10, 2026 20:06
A client-supplied REMOTEID is not readable back — Tally overwrites the
attribute with its own value (IMPLEMENTATION_GUIDE.md §3.3a, fourth
property, verified). Nothing here uses it, but the doc on
unresolved_identity said only that the identity is retained "for later
reallocation" without naming the channel, and the obvious wrong choice
fails silently: an amount parked with its identity in a write-only field
is unreallocatable, and nothing about the write says so.

Says narration, and says why. Surfaced by the voucher-presence lane,
which had to correct three claims of its own after reading the
reference's §9.3 heading as the general case.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3a192c875b

ℹ️ 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".

Comment thread src-tauri/src/agent_catalog.rs Outdated
…facts

candidates can be empty because nothing resembles the name, because a
family resembles it and none is separable, or because the list was cut —
and those mean opposite things to whoever decides what to do next. The
disambiguators are reason, candidate_count and candidates_truncated, and
reading the empty vector alone is wrong in two cases out of three.

Recorded here rather than left to each consumer because it has already
been got wrong twice by different lanes: the preparation screen rendered
"0 possible ledgers are listed first" over a family of 120, and the
voucher-presence contract needed a paired test to stop its rule
collapsing into "no candidates means unknown". Same defect class this
ADR was written against — a refusal whose neighbouring value reads as an
answer.

Also records why this is a doc and not a type. An enum of Listed /
Truncated / Withheld / None is the stronger fix and the one P2 asks for,
but it is breaking, a stacked consumer already depends on
candidates_truncated as a predicate and holds the boundary with tests,
and forcing that rework mid-review trades an improvement for a
regression risk. Revisit once both have merged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@lamemustafa
lamemustafa force-pushed the claude/voucher-presence branch from 3a192c8 to f5bd780 Compare September 10, 2026 20:27

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fbe2934151

ℹ️ 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".

Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs Outdated
Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs Outdated
Comment thread src-tauri/src/agent_presence.rs
…isting

Five items in one round, because review reopens once either way and the
marginal cost of the rest once it is reopened is small.

Tally's master-name matching is measured, not guessable:
IMPLEMENTATION_GUIDE.md §3.3b found it case-insensitive AND
separator-insensitive — a hyphen matches a space — and otherwise exact.
The binder was stricter, which is not the safe direction it looks like:
it refused names Tally accepts, and `X - Y` is a common ledger
convention. A separate master_identity_key follows §3.3b and stops where
Tally stops; `AND` for `&`, a missing suffix word and a singular for a
plural still refuse. It is separate from comparison_key rather than a
widening of it, because that one is shared with voucher numbers and
voucher-type names and §3.3b says nothing about those. Measured live: 16
of 16 hyphenated masters now bind from the spelling Tally itself
accepts, where all 16 were near-misses before.

Candidates becomes None | Listed | Truncated | Withheld. An empty vector
was three different facts and a consumer reading is_empty() was wrong in
two of them, a shape already got wrong twice by different lanes. Taken
before merge because the contract has not shipped and this is the
cheapest it will ever be; the consumer who pays for it measured thirty
lines and reported the change improves its code. The MCP result gains an
explicit listing discriminator, since a model is the caller that would
read an empty array as "no such ledger exists"; the desktop DTO stays
flat, where the screen already distinguishes the cases and is tested.

Also: FallbackBinding says reallocate with a Journal and never Alter or
Cancel, which §9.7 measured as duplicating with the target untouched
while reporting success; the ADR records that identifier coverage is
bimodal by client (42%, 0%, 0%, 0%) so the rule is a first-pass check
and never a primary key; and BindingStatus says what a Bound does not
establish — not that the master still exists, not that the requested
name may be written, not that it is right in business terms, and no
authority at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
lamemustafa added a commit that referenced this pull request Sep 10, 2026
Codex reviewed #294 and I could not invalidate a single finding. The
four that mattered all failed in the silent direction this contract
exists to avoid.

One book voucher could satisfy two proposals. Each identity basis
enforced its own uniqueness and nothing stopped two proposals reaching
the same voucher by different bases — one by REMOTEID, another by a
manual number — so a consumer would exclude two source vouchers against
one book row. Every claimant of a contested voucher is now demoted.

A proposal's REMOTEID was discarded and could still be called absent.
The adapter sets every observed voucher's id to None because the read
profile does not fetch it, so a proposal carrying a REMOTEID had its
strongest key silently skipped and could still be reported absent on the
keys that happened to remain. The report-level remote_id_observed flag
did not stop that — a status field does not neutralise the verdict
printed beside it, which is the exact defect ADR 0017 cites elsewhere
and had reproduced. A window now declares RemoteIdEvidence, and NotRead
withholds absent structurally.

The catalogue was never corroborated after the voucher read. A ledger
renamed between the two reads would bind proposals to the old name while
the rows carry the new one, removing the only resemblance. Now re-read
and refused on drift, matching the selected-voucher read.

A number match contradicted by a different REMOTEID settled anyway.
Reported as a conflict now, per ADR 0016's identifier-versus-name rule.

Five more, none silent: candidates dropped by the response cap were
counted as book vouchers no proposal reached; the party diagnostic
tested every entry ledger while reporting the party field, so a real
disagreement went unreported; the two cross-input refusals ran after
three Tally reads despite the stated pre-read guarantee; nested schema
bounds and additionalProperties were advertised and not enforced; and
the tool description still carried the flat no-idempotency claim the ADR
had already corrected.

Nine regression tests, one per finding plus the contrasts that keep each
rule from drifting. 865 lib and 157 core green, clippy and fmt clean,
reseal run and gate passing.

Two findings are not addressed here because they are not code: no live
evidence, and window completeness resting on cardinality this read
cannot prove. Both are already stated as limits in ADR 0017 and both are
answered on their threads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cf421524d1

ℹ️ 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".

Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs
Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs Outdated
Comment thread docs/tally/compatibility/compatibility-surface.json Outdated
lamemustafa and others added 2 commits September 11, 2026 02:57
Three were the same shape recurring: an identifier built from something
that identifies a period rather than a party.

A token carrying letters now never yields a standalone numeric, whether
or not it qualified as a code — `Part A12345678` was reaching an
unrelated `Bank 12345678` through the one-letter gap the code test
rejects. Period labels are recognised by their numbers rather than their
words, which catches `SEPTEMBER2025` and `2025QUARTER1` that no cap on
the alphabetic run ever would: a month name can be any length, a year
cannot. And a fiscal range is excluded before its digits are fused,
since `2025-2026` strips to an eight-digit run no calendar reading
rejects.

Fallback assignment now checks catalog provenance, not just class: two
ledger catalogs are both Ledger, and a fallback drawn from the one the
report never saw names a master that was never a candidate. Candidate
collection selects by index and clones only what it retains, instead of
cloning every match before the cap and the budget discard most of it.

ADR 0016 quoted thresholds this module stopped using two rounds ago, and
it is the contract two surfaces integrate against. Synced — and a test
now reads the ADR and asserts it quotes the live constants, so the next
drift fails rather than waiting to be noticed. Verified against a
positive control: changing a constant without the document fails it. It
also caught a false positive of its own on first run, which was the
detector being too strict about `(10%)` rather than the ADR being wrong.

Live re-measure over 485 names, 2,330 cases: 434 of 434 listed rows
still contain the right master, median listed length 2, no wrong binds.
Identifier binds 11 -> 8 with bound and unbound totals unchanged: three
mutations that had bound to themselves through a leaked numeric now bind
by name instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ializable

Both reproduced by execution before fixing, and both re-verified against
the same reproduction after.

A token shared across a name family IS the family. Where the catalog is
large enough that the token stays under the common-token threshold — 30
rows among 330 is 9% — the shared-token pass re-offered exactly the rows
the prefix pass had withheld, restoring the arbitrary capped slice that
withholding exists to prevent. Measured: the case reported near_miss
with 25 of 30 listed and the intended master outside the slice; it now
reports no_discriminating_candidate with the family counted and none
listed. Weaker rules now skip the withheld set; decisive rules still
reach a family member on its own evidence, because the identifier or the
whole key is what separates that one from its siblings.

Candidates::Listed was a newtype variant carrying a sequence, and under
Serde's internally tagged representation a tag cannot be merged into a
sequence — so serializing the MOST COMMON unresolved result failed at
runtime while the other three variants succeeded. Now a struct variant.
No test caught it because none had ever serialized an Unresolved, only a
Bound; every variant now round-trips in a test.

Live re-measure unchanged: 434 of 434 listed rows contain the right
master, median listed length 2, no wrong binds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 39d6da2de5

ℹ️ 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".

Comment thread src-tauri/src/agent_presence.rs Outdated
Comment thread src-tauri/src/agent_catalog.rs Outdated
Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs
lamemustafa added a commit that referenced this pull request Sep 10, 2026
Codex reviewed #294 and I could not invalidate a single finding. The
four that mattered all failed in the silent direction this contract
exists to avoid.

One book voucher could satisfy two proposals. Each identity basis
enforced its own uniqueness and nothing stopped two proposals reaching
the same voucher by different bases — one by REMOTEID, another by a
manual number — so a consumer would exclude two source vouchers against
one book row. Every claimant of a contested voucher is now demoted.

A proposal's REMOTEID was discarded and could still be called absent.
The adapter sets every observed voucher's id to None because the read
profile does not fetch it, so a proposal carrying a REMOTEID had its
strongest key silently skipped and could still be reported absent on the
keys that happened to remain. The report-level remote_id_observed flag
did not stop that — a status field does not neutralise the verdict
printed beside it, which is the exact defect ADR 0017 cites elsewhere
and had reproduced. A window now declares RemoteIdEvidence, and NotRead
withholds absent structurally.

The catalogue was never corroborated after the voucher read. A ledger
renamed between the two reads would bind proposals to the old name while
the rows carry the new one, removing the only resemblance. Now re-read
and refused on drift, matching the selected-voucher read.

A number match contradicted by a different REMOTEID settled anyway.
Reported as a conflict now, per ADR 0016's identifier-versus-name rule.

Five more, none silent: candidates dropped by the response cap were
counted as book vouchers no proposal reached; the party diagnostic
tested every entry ledger while reporting the party field, so a real
disagreement went unreported; the two cross-input refusals ran after
three Tally reads despite the stated pre-read guarantee; nested schema
bounds and additionalProperties were advertised and not enforced; and
the tool description still carried the flat no-idempotency claim the ADR
had already corrected.

Nine regression tests, one per finding plus the contrasts that keep each
rule from drifting. 865 lib and 157 core green, clippy and fmt clean,
reseal run and gate passing.

Two findings are not addressed here because they are not code: no live
evidence, and window completeness resting on cardinality this read
cannot prove. Both are already stated as limits in ADR 0017 and both are
answered on their threads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ca673ee40b

ℹ️ 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".

Comment on lines +169 to +175
// The adapter has no source-side cardinality for nonempty
// windows. A later catalogue reread cannot change the fixed
// `Partial` state into a complete observation, so avoid the
// extra endpoint load and fail with the evidence already in
// hand. A future qualified nonempty path can continue to the
// paired-snapshot checks below.
return Err(PresenceError::WindowIncomplete

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Establish a live nonempty slice before landing the engine

For every nonempty voucher window, this branch unconditionally returns presence_window_incomplete, so the shipped surface still has no working end-to-end presence slice while this change adds the full decision engine and extensive simulator/unit coverage. Fresh evidence after the earlier documentation fix is that merely recording the qualification gap does not satisfy the repository rule prohibiting component growth beyond a live-proven slice; establish source-side completeness and live evidence first, or defer the unexercisable implementation.

AGENTS.md reference: AGENTS.md:L86-L90

Useful? React with 👍 / 👎.

Comment thread docs/adr/0017-voucher-presence-authority.md Outdated
Comment thread docs/adr/0017-voucher-presence-authority.md Outdated
Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ead47bf3aa

ℹ️ 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".

Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs Outdated
Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs
Comment thread docs/adr/0017-voucher-presence-authority.md Outdated
Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ce6e0c9b9

ℹ️ 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".

Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs Outdated
Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs Outdated
Comment thread src-tauri/crates/bridge-tally-core/src/book_presence.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:tally Tally integration

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant