fix: allow conserved multi-match reconciliation approvals - #29
fix: allow conserved multi-match reconciliation approvals#29seonghobae wants to merge 493 commits into
Conversation
📝 WalkthroughWalkthrough이 변경은 조정 도메인에 실행 명령 증거, 은행 잔액 증거, 다중 승인 매치 보존, 승인 스냅샷 잠금 수리, 구조화된 close-review 증거, 권위 있는 close-package 재검증, 관련 HTTP/API 공개면, 마이그레이션 로더, 문서, 회귀 테스트를 추가합니다. Changes조정 통제
Estimated code review effort: 5 (Critical) | ~120 minutes Merge Risk: 🟠 High · up to This PR changes authoritative close-evidence construction and adds temporary branch-writing workflows. At the current head, close evidence can be assembled from inconsistent database snapshots, journal evidence is not explicitly constrained to the owning accounting book, and repository-controlled code runs with a persisted repository-write credential; these correctness and security risks must be fixed and verified in the reviewed commit before merge. Sequence Diagram(s)sequenceDiagram
participant Client
participant HTTP as http_api
participant Run as reconciliation_run
participant DB as PostgreSQL
Client->>HTTP: POST /reconciliation-runs
HTTP->>Run: accept_reconciliation_run(...)
Run->>DB: validate command and resolve statement binding
Run->>DB: insert reconciliation_run + reconciliation_run_command + evidence
DB-->>Run: persisted run document
Run-->>HTTP: evaluating run
HTTP-->>Client: response
sequenceDiagram
participant Builder as close_package builder
participant DB as PostgreSQL
participant Review as close_review projection
participant Package as close_package
Builder->>DB: load run, command, artifact, matches, exceptions
DB-->>Builder: database-owned evidence
Builder->>Review: validate scope, cutoff, reviewed matches, bridge
Builder->>Package: build canonical payload
Package-->>Builder: sha256 digest
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 56.87% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 517 functions across 61 files. (3 skipped: 3 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
seonghobae
left a comment
There was a problem hiding this comment.
Current-head CI failure is source-real but occurs before the new conservation guards run. In Accounting Foundation CI run 33073685926 / job 98522218358, both test_two_independent_matches_can_be_approved_in_one_run and test_approved_matches_cannot_overconsume_one_statement fail when the second approved reconciliation_match is inserted because PostgreSQL still has reconciliation_match_approved_single from migration 0014. The exact checkout is 52416a10227bf25ea43d16eff4cee25256536b3a.
The first causal boundary is the foundation migration loader split, not the 0015 conservation SQL itself: tests/test_postgres_posting.py intentionally initializes the real PostgreSQL catalog through accounting_information_platform.persistence.apply_foundation_migration, and that canonical chain still stops after 0014_reconciliation_candidate_allocation.sql. This PR changed migration_install.apply_foundation_migration into a wrapper that applies 0015 afterward, so public-wrapper callers and the durable foundation path now install different schemas.
Narrow repair: keep one authoritative install chain. Extend persistence.apply_foundation_migration through append-only migration 0015 and keep migration_install.py as the public alias/thin boundary rather than a second migration executor. Do not make the PostgreSQL test import the wrapper merely to turn it green; that would leave internal/operational callers able to stop at 0014. Strengthen test_foundation_install_manifest_contract.py so missing 0015 fails closed on the canonical loader.
The same current head also leaves canonical install metadata stale: docs/OPERABILITY.md still says/apply-through 0014, and scripts/validate_repository.py::REQUIRED_FILES does not require 0015. Update OPERABILITY/ARCHITECTURE/required-file contracts together with the loader, then rerun the failed PostgreSQL boundary before full 100% coverage/package/SBOM/provenance gates. Keep this PR Draft until that exact-head proof exists.
seonghobae
left a comment
There was a problem hiding this comment.
Current-head review on dccc6737abf043962aea132e56b112626a89eeb9: the canonical-loader/install-contract defect is repaired and repository-owned exact-head CI is green, but migration 0015 still scopes allocation conservation to a single reconciliation_run_id. Both reconciliation_allocation_conservation_guard() and reconciliation_match_approval_conservation_guard() derive capacity and consumed amount with WHERE ... reconciliation_run_id = NEW.reconciliation_run_id, and the advisory-lock key also includes the run id. A second non-superseded run can therefore approve the same immutable statement_entry_reference or journal_reference again without seeing consumption from the first run.
That violates Issue #8's source-consumption invariant: a consumed statement-entry amount or journal amount cannot be consumed by a second active approved match; only rejected/superseded evidence may release consumption through explicit state transition. The current PostgreSQL tests cover two matches inside one run but do not exercise cross-run reuse.
Please add a real-PostgreSQL RED regression with two reconciliation runs in the same tenant/book/bank-account scope that reference the same statement entry (and separately the same journal), approve the first allocation, then prove the second active approval fails closed. The narrow repair should conserve/lock by immutable source identity across active runs (while explicitly excluding released/superseded allocations), not merely remove reconciliation_run_id from one query without preserving tenant/scope safety. Keep exact Decimal/numeric conservation, append-only history, and no automatic posting authority. Re-run the failed PostgreSQL boundary first, then the exact-head 100% coverage/repository/package/security gates.
seonghobae
left a comment
There was a problem hiding this comment.
Current exact-head RED is now observed on b1e1b1fd3fbec26ecd822a23b6d4bd6077cf6665 in Accounting Foundation CI run 33087013727 (job 98569306261). PostgreSQL 18.4 ran 472 tests; the cross-run source-conservation/release tests now pass, but the suite fails exactly four append-only evidence contracts in tests/test_reconciliation_append_only_evidence_red.py: recorded candidate source identity can still be UPDATEd; an unmatched recorded candidate can still be DELETEd; and statement/journal allocations can still be UPDATEd or DELETEd after their match is superseded.
The current migration explains the boundary: reconciliation_candidate_capacity_guard allows UPDATE and has no DELETE immutability guard, while reconciliation_approved_allocation_immutability_guard rejects allocation mutation only when the current match status is approved; once status becomes superseded, historical allocation rows become mutable/deletable. That defeats the intended model where status transition releases capacity without rewriting retained evidence.
Narrow GREEN: make recorded reconciliation_candidate rows append-only after INSERT, and make statement/journal allocation rows immutable regardless of later match status. Keep release semantics entirely in reconciliation_match.match_status_code, so only approved rows count toward active capacity while superseded/rejected rows remain durable historical evidence. Preserve tenant/scope FKs, exact numeric conservation, cross-run advisory locking, and the no-posting authority boundary. Do not weaken the RED tests or obtain green by deleting historical rows.
After the narrow DB-owned repair, rerun the four focused PostgreSQL tests first, then the full real-PostgreSQL suite and current-head 100% owned statement/branch coverage, repository/docstring/compiler contracts, exact-head SAST/security/live-base OSV, reproducible package and SHA-bound SBOM/provenance. Keep Draft until those exact-head gates and the ruleset-required Strix path pass and an independent qualifying approval exists.
seonghobae
left a comment
There was a problem hiding this comment.
Current-head causal boundary after exact job-log inspection (Accounting Foundation CI 33122367392 / job 98692151054): exact checkout of 9ce884b85a856aba13da741e604e3a398973b302 and PostgreSQL 18.4 succeeded; 479 tests ran and exactly one repository-contract test failed: test_unreleased_changelog_records_balanced_approval_invariant. The database-owned approval-balance tests themselves pass, including equal non-empty allocation totals, missing-side rejection, and unequal-total rejection. The current [Unreleased] migration-0015 bullet therefore understates an already-enforced invariant rather than exposing a migration defect.
Narrow repair only: strengthen that existing CHANGELOG bullet so the same entry explicitly states (1) non-empty statement allocations, (2) non-empty journal allocations, and (3) approved statement/journal allocation totals are exactly equal. Preserve append-only evidence, cross-run conservation, no-posting/reversal/close/policy authority, and released history. Then rerun the full exact-head PostgreSQL/100%-coverage/repository/package/SBOM/provenance gates; do not transfer predecessor evidence. Keep Draft while Accounting Foundation CI is red, Strix is pending, and qualifying approval remains absent.
seonghobae
left a comment
There was a problem hiding this comment.
Current-head accounting-control review on c0af868004dabde4a99205271dc77d502abfa9d9: the sequential late-allocation RED is fixed, but the freeze is not concurrency-safe yet.
reconciliation_allocation_conservation_guard() reads the match status with a plain SELECT and rejects inserts only when the visible status is not proposed. It does not lock the reconciliation_match row. reconciliation_match_approval_conservation_guard() concurrently totals currently visible allocations before allowing proposed -> approved, but it likewise has no synchronization with a concurrent allocation INSERT on that match.
A real PostgreSQL interleaving can therefore violate the reviewed snapshot: transaction A begins proposed -> approved and sees balanced committed allocations; transaction B, from a snapshot that still sees the match as proposed, inserts an additional allocation before A commits; A can approve without seeing B's uncommitted row and both transactions can later commit, leaving an approved match whose allocation population changed across the approval boundary. The new sequential tests do not exercise this race.
Please add a two-connection PostgreSQL RED that coordinates the transactions explicitly (barrier/event, not timing sleeps): start with a proposed match and balanced allocations, begin approval in one connection while a second connection attempts a further statement allocation and separately a further journal allocation. Prove that exactly one serialization order wins and that no committed state can contain an approved match with allocation rows added after the reviewed snapshot. Also assert the final approved statement/journal totals remain equal.
Narrow GREEN: serialize allocation INSERT and terminal review on the same match identity at the database boundary, e.g. acquire a row-level lock on the parent reconciliation_match before accepting a proposed-state allocation (with the approval UPDATE naturally taking the conflicting row lock), or an equivalent DB-owned lock discipline. Preserve the existing cross-run source advisory locks, append-only evidence, terminal-state freeze, exact numeric conservation, and no-posting/reversal/close authority. Re-run the focused concurrency RED first, then real PostgreSQL/full 100%-coverage/repository/SAST/security/OSV/package/SBOM/provenance gates on one unchanged head.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Scheduled review-feedback autofix for this PR head.
|
|
Child-state correction: live lifecycle successor #43 is now exact |
|
Exact-stack correction for 2026-09-08: the PR body’s |
Merge exact hosted-GREEN lifecycle descendant into the reconciliation root without rewriting ancestry. Preserve PostgreSQL-owned command/status/outbox, TimeZone-independent digest, shared command identity, RLS and accounting authority boundaries.
|
Dependent-stack state correction; root source is unchanged. #47 is no longer Current #47 Accounting Foundation run |
|
Stack correction from fresh #47 review: current child is |
|
Exact child update: |
|
Scheduled review-feedback autofix for this PR head.
|
|
Central CodeQL owner-path correction — keep the AIP root unchanged. Required root CodeQL run Fresh owner-path evidence now advances the RCA beyond the earlier 403-only diagnosis. Canonical combined repair The live #2040 owner review has already classified the exact cause: the protected pre-cutover handler publishes the legacy unbound
|
|
Fresh central-owner recheck, 2026-09-09 KST: root required CodeQL |
Buyer outcome
Harden authoritative bank reconciliation and close evidence while preserving AIS as the accounting system of record. Bank statements remain immutable non-posting evidence; reconciliation cannot post/reverse journals, close periods, or alter accounting policy.
Exact current state — 2026-09-09
12e1a4bb9d8de5e4b6c72e2893107b07c515416b;develop@239008c4edc7d305c97704c5102b593c6622b36f;34173498502is GREEN on real PostgreSQL, complete owned statement/branch coverage, repository contracts, compile/import and reproducible package; same-head dependency diff/security/SAST are GREEN;34173498486remains terminal RED at the central current-generation receipt/dispatch publication boundary owned by.github, not from an AIP source-analysis finding;APPROVEDreview exists.Integrated lifecycle authority
The root contains the supported tenant-scoped idempotent
reconcile_reconciliation_run()path. It admits the database lifecycle session lease before a freshREPEATABLE READauthority transaction, derives review controls and the exact book-to-bank bridge from PostgreSQL-owned evidence, persists database-owned transition identity/hash, and atomically commits transition command,reconciledstatus and the matching transactional-outbox event. Direct SQL status mutation is not an alternative authority path.Dependent maker-checker lane
Current child is
#47@263e4ffb79607dccee715686d0aa451981c2eeb8, targeting this exact root, open/Ready/mergeable. Root migrations remain0019/0020/0021; #47 owns0022..0030.#53remains dependency-blocked and must not be restacked until #47 is normally integrated and the resulting parent head is reverified.Fresh review on predecessor #47 found the P1 stale-session-lease continuity defect. The ordinary TDD lineage is
4e301153...real PostgreSQL attack RED →24c8ca34...transactional lease invalidation plus exact-leaseSELECT ... FOR UPDATEfreshness repair →55c0d65f.../ current263e4ff...code-current ADR/doctoring. FORCE RLS, command/status/outbox invariants, maker-checker separation and PUBLIC helper revocations remain intact.Exact #47 Accounting Foundation
34237450022is terminal GREEN on unchanged263e4ff...: Accounting102098735789ran 1,315 real-PostgreSQL tests / 0 failures / 0 errors, with 6,211/6,211 statements and 2,330/2,330 branches, repository contracts, compile/import and reproducible package/evidence GREEN. Security102098735619, SAST102098735914, dependency diff102098735946are GREEN; integrated-head attestations are correctly skipped while stacked. Retained artifacts are behavior10061043416(sha256:efd4e6f9...24e09), coverage10061189106(sha256:c33ae1a3...ac81), package/evidence10061193521(sha256:2bfd0539...2de7), dependency diff10060933382(sha256:6ba6073d...751f). The P1 thread is resolved from hosted PostgreSQL evidence. Fresh CodeRabbit exact-head review5586974629found no current actionable defect in the requested lifecycle/accounting-authority/security boundaries but explicitly is not an approval.Effective dependent stack is
#29@12e1a4b... -> #47@263e4ff... -> #53. #43 remains merged history.Separate DDD architecture lane
#41 is exact
3bdbeec6cc35e5b111739499c562f988f5fcaa18, targeting this exact root, open/Ready/mergeable after the stale physical-owner-row repair reacquired complete exact-head evidence. The TDD lineageb2fd20fb...rejects ownership rows for absent files/directories,5e8f8a06...removes deletedreconciliation_completion.pyfrom current physical ownership, and3bdbeec...makes the #41-owned doctoring/regression contract code-current without moving runtime authority.Accounting Foundation
34239818457is terminal GREEN on unchanged3bdbeec...: Accounting102106807702passed behavior/repository, complete owned statement/branch coverage, strict denominator, repository contracts, compile/import and reproducible package/evidence; Security102106807452, SAST102106807652and dependency diff102106807711are GREEN. Retained evidence is behavior10061809339(sha256:90f4b2cb...573cc), coverage10061915751(sha256:0775fd1c...46995), package/evidence10061921252(sha256:377fe47e...3d0a6) and dependency diff10061846663(sha256:4433ebe9...75d2d). Integrated-head attestations are skipped because the stacked head is not protected integration evidence. No qualifying independentAPPROVEDreview exists.All current #41 review threads are resolved. The ADR 0059 shared
CHANGELOG.md/docs/doctoring/STANDARD_TRACEABILITY.mdfinding was not silently waived: it is explicitly transferred to canonical single-writer#37@bdf076466b1cde0e7ae6247f44309fac153ae4c4. #41 remains Proposed; after protected #41 integration, #37 must rebuild on that exact authority and record ADR 0059 / Context Map / UL provenance before #37 can merge.Central CodeQL owner path
The historical root required run
34173498486is retained as exact evidence but is not rerun as a substitute for a newer central workflow definition. Protected central authority is.github/main@7fd571dbcdbae6acf29d8f4ee704d7ba6297e4dbafter #2028.The current canonical combined central repair is
.github#2040@6706c231ab06a3c91c43fdb5b989cfcd79fff593, Ready/mergeable on that protected base. Its exact-head Runtime Quality34251822381, Security34251822390, SAST34251822314, and Python Security34251822251are terminal GREEN, while required CodeQL34251822255is terminal RED. Actions receiver102154521648and Python receiver102154523061fail current-head verdict enforcement, then coordinator102154736341fails before dispatch withCodeQL coordinator could not bind every pending language to an exact failed job.The canonical central owner has already classified that current P1 as a bootstrap evidence/settlement incompatibility: protected pre-cutover
main@7fd571...publishes legacy unboundcodeql-dispatch/<language>evidence/title, while the repaired producer correctly requires base/source-bound evidence; repeated per-language wake has fragmented one required run across attempts. That must be repaired centrally without accepting legacy head-only evidence or weakening exact repository/PR/base/head/language/required-run/SARIF provenance..github#1902remains Draft/mutable, so AIP does not copy either mutable central branch or create a competing leaf workflow fix.No historical AIP CodeQL rerun, synthetic status, no-op leaf commit, Draft/Ready churn, leaf PAT, gate weakening, or manual evidence substitution is an accepted recovery path. Root integration waits for ordinary central repair and a genuinely fresh required-workflow generation.
Accounting authority
Authority-bearing close construction derives locked run status/scope/currency/cutoff, retained statement artifact and balances, admitted statement entries, posted cash-journal population, complete approved matches/approvals/allocations/exceptions, deterministic population identities and exact Decimal book-to-bank bridge from PostgreSQL-owned evidence. Caller-shaped balances, populations, state labels, tenant identities and bridge amounts are not authority. Billing remains authoritative only for usage/rating/invoice/payment/refund/dispute/provider-settlement commercial truth and interacts through released/versioned event/API/ACL boundaries; cross-service SQL and mutable sibling dependency are prohibited.
Merge / release boundary
Keep this root Draft. Required central root CodeQL terminal evidence and qualifying root review are absent. #47 has exact-head hosted GREEN and fresh clean current-head review but still lacks qualifying independent approval before normal integration. #41 has exact-head hosted GREEN and all threads resolved, but still lacks qualifying independent approval; its shared-record requirement is transferred to #37's post-integration rebuild rather than duplicated here. Do not self-approve, bypass, force-push, destructively rebase, synthesize status, restack #53 early, duplicate #37-owned shared documentation, tag or release.