Conversation
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
ac2bd13 to
6fb8936
Compare
|
Ladon cannot review this PR until merge conflicts are resolved. |
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — additive seller reconciliation storage contracts (#1167 storage half).
Checked:
- No no-auto-approve team match; gated_paths false; high_risk false — no hard gates apply.
- Reviewer found no blocking or Medium findings across all 26 files.
- Verified via reviewer notes: multi-tenant isolation, credential/echo boundary (no credentials in ctx_metadata), append-only immutability, transaction atomicity, forward-compat on discriminated unions, and type-system import layering all clean.
- Semver signal correctly
feat:— the change is purely additive (new opt-in stores, managed evidence fields, a bundled migration, DB triggers, wire projections), no public-surface removals/renames/required-flips.
No blocking findings, no medium findings, no escalation triggers — falls through to row 9 (approve).
6fb8936 to
c44095e
Compare
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — reconciliation storage foundation (second pass).
No blocking findings. No medium findings. Reviewer verified memory/PG store parity, the fail-closed transition contract, scoped independent feeds, official-wins revision selection, and an additive public surface carrying the correct feat(reporting) semver signal. Full CI green (8,993 passed).
Gate checks:
- No no-auto-approve team match.
- gated_paths: false; review_decision: APPROVED.
- high_risk: false (no deleted/modified high-risk trigger).
- Prior decision was
approve— this pass is consistent, no new findings.
None of rows 1–8 fire (zero findings), so the outcome falls through to row 9: approve.
…e snapshot topology Independent adversarial review of the #1167A storage slice reproduced ten in-scope defects. Each is fixed with a regression that fails on the previous head. Buyer selector (`_reconcile._select_current`): - A unique official close masked a broken snapshot history. `current = official or leaves` never inspected snapshot topology, so two unsuperseded snapshot roots, or an A<->B supersession cycle, plus one official evaluated `definitive=True` with no reasons. Snapshot leaves are now counted on their own, and every leaf is walked back through its predecessors so a cycle's members stay unreachable and fail closed. - `native_commit` accepted a resource whose retained descriptor declared `immutability="immutable_location"` whenever the native reference and observation path matched. The seller already refused that; the buyer now mirrors the predicate. - `ReportingRevision` carries no obligation reference, so scanning semantic scope made two legitimate obligations that share a definition, profile, campaign set and period see each other's revisions and both go non-definitive. Candidates now exclude a revision only another obligation has materialized. Revisions nobody materialized -- an unmaterialized official included -- are never excluded, illegal fan-out is still reported, and an unresolvable owner still fails closed rather than guessing. Reference validators (`reporting.evidence`): - `resource_location` accepted Azure-SAS-shaped signed query material because no keyword appeared in it. Any `?name=` / `&name=` query-parameter pair is now refused in every reference field. - Unbounded credential substring matching rejected benign operational names such as `tokenized_inventory_daily`, `secretariat-report-v17` and `authorization_metrics_v2`. A keyword now condemns a value only when it stands alone as a word and introduces something after it. PostgreSQL evidence graph (`reporting_ledger_reconciliation.sql`): - `content_sha256` was only format-checked and the payload shape was open, so ordinary direct SQL -- with no trigger disabled -- retained a credential under an attacker-chosen digest and left that principal's whole feed unreadable. `reporting_canonical_json` now implements the `canonical_json_utf8_v1` profile in SQL, every insert recomputes its own fingerprint, and a closed key allowlist refuses any field outside the frozen record types. - No SQL predicate compared a successful materialization or an accepted receipt against the Core revision it named, so a coherent payload with a true fingerprint committed `row_count=999` and auto-created an accepted terminal head. `reporting_reconciliation_evidence` re-derives the predicate that decides money: revision row count, typed totals and canonical digest; binding format, readers, method, success status, verification path and retention floor; checksum-to-object binding; receipt profile, totals, profile-specific evidence and readable window; and an adjustment digest recomputed from the retained adjustment columns. - A filtered page succeeded while a payload-corrupt row sat inside the frozen boundary but outside the filter. The read integrity scan now covers the caller's whole retained graph, so any mismatch fails every page. Feed boundaries: - A caller's stale or hand-built boundary was reported as `REPORTING_HISTORY_CORRUPT`. Only a boundary this store opened can accuse itself; caller tokens now fail as `INVALID_CHECKPOINT`, indistinguishably from unknown, malformed, overlong, cross-principal, filter-mismatched and ahead tokens. Also adds the missing true concurrent-allocation probe: four order-independent records for one principal commit at once and must take dense unique sequences with a matching caller-local head. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Approve
Approve.
Delta pass on adversarial-hardening commit 3c405a2 (subsequent review; prior decision was approve with no findings). The reviewer examined all five source deltas — _reconcile._select_current cycle/fork detection, evidence.py credential-shape validators, delivery/delivery_pg boundary attribution, and the +262-line reconciliation SQL evidence graph — plus operational readiness and public-surface/layering. No Critical/High/Medium findings; changes are fail-closed and covered by new regression tests.
Gate checks:
- No no-auto-approve team match.
gated_paths: false(row 2 N/A);review_decision: APPROVEDregardless.high_risk: false— no (deleted)/(modified) escalation triggers (rows 3, 5 N/A).- No medium findings (rows 4, 8 N/A).
- Prior decision was
approve, notescalate(row 6 N/A).
None of rows 1–8 fire; falls through to row 9 → approve.
A cell declaring a metric `missing`/`delayed`/`unsupported` withdrew that metric's control total unconditionally, including when the declaring constituent staged no rows at all. `validate_monetary_content` requires the total for a monetary column every retained row carries, so the composition of #1173's per-metric evidence with #1175's obligation currency sealed an immutable publication the ledger then refused with `MONETARY_TOTAL_MISMATCH` on the replay of every retry -- permanently wedging the obligation for the answer per-metric evidence exists to give: a buy that delivered nothing and whose billing is pending. Distinguish the two shapes rather than exempting money from the withdrawal: - A withdrawal by a constituent that staged rows still removes the total. Those rows carry values the adapter disclaimed, so summing them would contradict its own evidence, and the payload stays staged byte-for-byte. - A withdrawal by a constituent that staged no rows reaches no sum and removes nothing. The checksum over the rows that were measured is retained. A row-empty batch still withdraws, so unavailability is never published as an observed zero. No total ever sums a disclaimed value. Monetary columns have no third option: dropping the total is refused by the ledger and keeping it would falsify the evidence. A withdrawn monetary cell whose own constituent's rows report that metric now raises before anything is staged or sealed. `ReportingDefinitionBinding.to_wire()` deliberately keeps the frozen unit declarations off the wire, so the frozen slice request cannot carry them; `InlineReportingSource(monetary_metrics=...)` lets a trusted caller declare #1171's custom `monetary_metric_units` columns, with `spend` always included. An undeclared custom money column keeps the non-monetary behavior. Manifest and control-total bytes for a result with no `cell_availability` are unchanged, byte-identical to the #1176 base. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Refs #1167
This adds the durable storage foundation for seller reporting reconciliation. Trusted publishers and future destination writers/receipt handlers can retain immutable destination bindings, materialization evidence, readability observations, and revision/adjustment receipts with the same state machine in memory and PostgreSQL.
The audit corrections reject evidence attached to a different frozen obligation, give reconciliation its own isolated incremental feed, and enforce the exact evidence graph in PostgreSQL. The buyer selects the obligation's unique current revision first: an official revision wins when present; otherwise the unique unsuperseded snapshot is current. An unmaterialized, pending, or failed current revision never falls back to an older materialized snapshot. Multiple current candidates fail closed.
A second independent adversarial review then reproduced ten further in-scope defects and fixed each with a regression that fails on the previous head. The database now recomputes every retained payload digest under a SQL implementation of
canonical_json_utf8_v1, refuses any payload field outside the frozen record types, and re-derives the financial acceptance predicate itself; the buyer selector judges snapshot topology independently of the official close; and the reference validators separate credential shapes from operational names that merely contain a credential word.Stack
conductor/reporting-obligation-currency-1171/ fix(reporting): freeze currency on reporting obligations #1175,ff584b6fbe3436e7afa4980a14e2e5e4004a06a3.3c405a21f978ed9d3208611bb4a7a8434a056933, treef81e8437de09ce341b3892bb17cc3edc069c661f.eac9b1b000c8faa56c19cc4cd9df83ba1ad688e9(storage foundation),89674803f128868e4c8069f4b22f97dccf5836ac(independent revision histories and receipt races).c44095e3b457c44931eacc8a0da21ddf442229fd(fix(reporting): enforce exact reconciliation graph and isolated feeds).3c405a21f978ed9d3208611bb4a7a8434a056933(fix(reporting): bind reconciliation evidence in the database and judge snapshot topology).Architecture and corrected findings
ReportingReconciliationFeedStoresupplies separate record kinds, typed snapshot/cursor/checkpoint tokens and filters. Each account/consumer has a dense append-only sequence/head. Evidence, its caller-visible sequence and its feed row commit together. CoreLedgerRecordKind,LedgerPage, change feed and required public store protocols remain unchanged.reporting_canonical_jsonimplements thecanonical_json_utf8_v1profile in SQL, so every insert recomputes its owncontent_sha256, and a closed key allowlist refuses any field outside the frozen record types. Ordinary direct SQL — satisfying every column constraint with no trigger disabled — can therefore no longer retain a credential or provider blob under a chosen digest, nor leave a principal's feed permanently unreadable.reporting_reconciliation_evidencere-derives the predicate that decides money: revision row count, typed totals and canonical digest; binding format, readers, method, success status, verification path and retention floor; checksum-to-object binding; receipt profile, totals, profile-specific evidence and readable window; and an adjustment digest recomputed from the retained adjustment columns. A retained payload that disagrees with its own fingerprint fails every read for that principal, including pages whose filter would have skipped the damaged row.ReportingRevisioncarries no obligation reference on the wire, so candidates exclude a revision only another obligation has materialized; revisions nobody materialized — an unmaterialized official included — are never excluded, illegal fan-out is still reported, and an unresolvable owner still fails closed. The buyer also requiresimmutability="native_version"fornative_commit, matching the seller.native_commitrequiresnative_versionimmutability, exact native references and the exact observation path. The complete method × profile × billing matrix is tested on both stores: dataset/warehouse bindings reject manifest-only verification, and billing requires canonical digests. Success also binds typed/unit-bearing expected totals, row count, format/readers, checksums and retention floors. Managed canonical evidence remains distinct from Core's unchanged digest contract.tokenized_inventory_daily,secretariat-report-v17andauthorization_metrics_v2persist unchanged whiletoken=…andBearer …do not. Independently, any?name=/&name=query-parameter pair is refused in every reference field, so presigned S3/GCS and Azure SAS material such asreport.csv?sv=…&sig=…never reaches storage even though no keyword appears in it.Exact retries return retained evidence and append nothing; changed retries conflict. Receipt IDs share one namespace, accepted leaves are terminal, and expiry/corruption does not erase acceptance. Adjustment acceptance binds the complete immutable adjustment digest and its official revision; revision and adjustment acceptance remain independent facts that completion must require together.
Migration
create_schema()applies the four bundled SQL resources in one transaction under the shared schema advisory lock. The reconciliation migration is itself atomic in autocommit mode and idempotent. Literal beta.15 and #1171 fixtures preserve existing rows, hashes, currency, leases, issue history and Core feed sequence numbers. The literal initial reconciliation upgrade also preserves records, receipt heads and legacy feed bytes/timestamps while projecting retained order into independent dense account/consumer sequences. Incomplete or inconsistent history is rejected atomically; unknown historical currency/digests/totals remain unknown. Start a new reconciliation snapshot/feed walk after upgrading; legacy Core tokens are not reconciliation tokens. Drain older writers before applying the migrations.Validation
Python 3.12.14, real PostgreSQL 16.14. The authoritative evidence below is from head
3c405a21f978ed9d3208611bb4a7a8434a056933, treef81e8437de09ce341b3892bb17cc3edc069c661f, with a clean checkout before and after every run. Earlierc44095e3evidence is superseded.make ci-localon the exact committed head: 9,076 passed, 34 skipped, 9 deselected, 1 expected failure; 85.14% coverage (80% required), pytest 1,075.97s. Lint, mypy over 1,359 source files,mypy --strictover 28 adopter fixtures with no typing suppressions, and generated-code validation all passed. All commit hooks passed, including Black, Ruff, mypy, Bandit and conventional subject validation.c44095e3. It now carries 495 reconciliation conformance regressions (boundaries 23, changes 62, migration 20, profiles 188, SQL 104, store 50, transitions 48) plus 52 buyer-selector cases — +83 new cases from 16 new test functions.src/. Replacing onlysrc/withc44095e3and re-running the new tests yields: signed-query rejection 20 failed/20 passed (the 20 passes are values the old keyword list already caught), benign-name acceptance 10 failed, caller-boundary classification 4 failed, closed-payload 3 failed, payload-digest 1 failed, outcome-evidence 6 failed, receipt-evidence 4 failed, adjustment-digest 1 failed, snapshot-topology 2 failed, identical-scope ownership 1 failed, buyernative_commitimmutability 1 failed.py.typed; no workspace artifacts are packaged. Isolatedpython -Icore import with an empty snapshot/feed/checkpoint probe passes in both installs without any PostgreSQL dependency (PG_AVAILABLE is False).[pg]passed a real PostgreSQL 16.14 exercise across two fresh Python processes: three idempotentcreate_schema()calls, then a five-record walk paged [1, 2] in the seed process and resumed [3, 4, 5] from the saved cursor in a second process, with the snapshot reading all five. The isolated schema was dropped and the checkout stayed unchanged.open_snapshotbyte-identical before and after higher-tier writes; inter-page writes deferred past the frozen upper bound; exclusivechanges_afterat head; replay appending no sequence; and unknown, malformed, overlong, cross-principal, filter-mismatched and ahead tokens all failing indistinguishably asINVALID_CHECKPOINT.reporting_iso_utcandreporting_canonical_jsonwere checked byte-for-byte againstiso()andcanonical_json_utf8_v1, including sub-millisecond, trailing-zero and non-UTC-offset timestamps, because the recomputed adjustment digest depends on that parity.e63c5aaa2635cfecf6411824d0a38c0d93270ec84a593c95b509f07efe08993b.6b89262c5e06fb4fcb6e0f8eb8f6fba1e9192ccdecc05e23be8a02382c80bea0.The focused command was:
The full gate used the configured real PostgreSQL URL and
make ci-local. Full CI on the committed head is the authoritative evidence.Remaining boundaries
These are trusted low-level storage contracts. Future handlers must derive account/consumer scope from authenticated transport, and future publishers/writers must compute expected evidence and verify actual destination content outside retained records. Core status remains Core-only. Higher-tier handlers, destination writers, capabilities, reconciliation health, bounded wire projections and #1168's atomic notification outbox remain completion work; #1167 stays open.
The reference stores load a consumer's retained graph for transition validation. Feed pages use indexed keyset reads with scoped integrity/count scans and the existing account transaction lock. The integrity scan now recomputes one canonical digest per retained record for the calling principal, and each insert recomputes its own, so both costs grow with a principal's history. Large-history throughput and production-sized migration lock duration remain unbenchmarked. Details are in the storage architecture.
Three review findings were deliberately not changed here, with reasons:
ledger_as_ofis not bound intosnapshot_id. Binding a clock-derived value would change the snapshot ID on every read and destroy the "foreign writes do not move my snapshot ID" property this slice is required to have.ledger_as_ofis echoed-only and affects no row selection.ReportingRevisionhas no obligation field and the response'sextextension is not yet a protocol-sanctioned alias model, so this PR narrows candidates by materialization ownership and otherwise fails closed. Two Core-only obligations that share a definition, profile, campaign set and period still read as ambiguous rather than guessing; resolving that needs the status projection planned for #1167C.A residual, unchanged boundary: no syntax check can decide whether an arbitrary opaque string is a secret. A high-entropy value with no keyword, scheme, userinfo, PEM header, AWS key shape, JWT shape or query-parameter pair still persists, and provenance remains the trusted adapter's responsibility. One cosmetic nit is also left alone: a ledger with both multiple officials and multiple snapshot leaves reports
AMBIGUOUS_REVISION_CHAINtwice, which changes no decision.