Repository navigation
PAY-002B2C3B — Persist immutable Stripe reconciliation evidence #206
Description
Officer impact: None. This child adds an unused server-only evidence record and tests. It changes no current checkout, payment, refund, page, or officer procedure.
Officer documentation: None — no public page, officer duty, provider account, deployment, or production behavior changes.
Deployment evidence: None at issue creation. Closure must report source, tests, merge, website, runmprc.com, Firebase, Stripe/provider, production data, and live behavior separately. A green workflow proves none of the outside surfaces.
Problem
PAY-002B2C3A #184 freezes a pure closed classifier. Its new_attempt_candidate result is deliberately not persistence, authorization, or permission to send. No durable record currently proves which exact attempt-1 evidence the trusted verifier classified, and C3C must never act on a caller-held transient classification.
The next boundary must append one immutable reconciliation evidence record and one deterministic audit partner without changing any earlier command, lease, provider plan, pre-send marker, or audit. It must not create or authorize provider attempt 2.
Atomic outcome
Add one unused recordInitialStripeReconciliationEvidence(input) journal API. It validates the exact B1/B2A/B2B/C1/C2 chain, passes the exact closed attempt-1 evidence object to the unchanged C3A classifier, and atomically creates one deterministic reconciliation record/audit pair only when C3A returns new_attempt_candidate.
flowchart TD
V["Trusted verifier produces exact closed attempt-1 evidence"] --> C{"Unchanged C3A result"}
C -- "existing_attempt_found or reconciliation_required" --> N["Write nothing; do not advance"]
C -- "new_attempt_candidate" --> J{"Exact B1 through C2 chain valid and bound?"}
J -- "No" --> F["Fail closed; write nothing"]
J -- "Yes" --> P["Atomically create one immutable evidence record and one audit"]
P --> R["Return fixed persisted-candidate result"]
R --> X["Still no send, attempt 2, or advancement authority"]
Text alternative: Only a complete candidate from the unchanged classifier and an exact existing command/plan/pre-send chain may create one immutable evidence and audit pair. Every other result writes nothing. The saved candidate still cannot send or create attempt 2.
Exact input boundary
The root is one exact plain data object containing:
- the existing B1 command identity fields:
db, environment, caller scope, command ID/type/schema, and canonical payload; - the existing C1 Stripe plan identity fields: account ID, mode, API version, fixed checkout-session-create operation/path, and canonical provider parameters;
- one exact
reconciliationEvidenceobject accepted by the unchanged C3A policy.
It accepts no caller timestamp, record path, document ID, provider object ID, Stripe idempotency key, hash/commitment, attempt number, lease/send flag, free text, URL, response, or attempt-2 field. This persistence step does not require or imply an active lease; C3C must obtain a fresh lease separately.
Invariants
functions/commerceProviderReconciliation.jsremains byte-identical.- Only exact C3A
new_attempt_candidatemay be persisted.existing_attempt_found,reconciliation_required, malformed evidence, and hostile input write nothing. - The transaction validates the existing command/registration audit, lifecycle/current audit history, exact attempt-1 provider plan/audit, and exact C2 pre-send record/audit before reading or writing C3B.
- C3B requires an existing structurally and chronologically valid C2 pair. Missing, orphaned, future, malformed, time-unknown, or conflicting C1/C2 evidence fails closed without repair.
- The new record is under attempt 1 at one deterministic path. Its audit uses one deterministic ID. Both are created in the same transaction after every required read.
- The record binds to command identity, provider attempt 1, the exact C1 plan commitment, a command-bound commitment to the exact C2 pair, the C3A policy version, all closed evidence enums, the fixed candidate classification/state, revision 1, and one trusted server timestamp.
- The audit binds to the same command, attempt, plan/send commitments, policy version, candidate classification, evidence revision, environment/mode/operation, and exact timestamp.
- No raw caller/command UUID, provider account/object ID, idempotency key, canonical parameters, PII, free text, response body, or secret is stored, returned, logged, or copied into an error.
- Exact retry is read-only and returns the same fixed frozen result. Any different valid evidence under the same deterministic identity is a conflict. An orphan in either direction is invalid and is never repaired.
- Transaction retry reuses one prepared timestamp and one deterministic pair. Commit failure leaves neither partner. A forged transaction return is rejected.
- Every B1/B2A/B2B/C1/C2 record and audit remains byte-for-byte unchanged.
- The fixed result contains no provider object, evidence values, ID/hash/path, send/execute/replay/advance flag, attempt 2, or business transition.
- No Stripe SDK/network call, endpoint/index import, logger, random value, environment read, production record, migration, provider setting, or deployment is added.
- Browser access remains denied by the existing nested
checkoutRequestsandauditEventsrules; add explicit read/write/list/collection-group denial proof without changingfirestore.rules.
Acceptance criteria
- The unchanged C3A classifier is the only classification authority and is imported only by the still-unused journal library.
- Exact candidate evidence creates one deterministic immutable record/audit pair atomically after all foundation reads.
- The saved record/audit are bound to the exact C1 plan and exact C2 send evidence without storing raw sensitive values.
- Exact retry is read-only; one changed evidence enum conflicts and never overwrites.
- Existing-attempt, reconciliation-required, invalid, incomplete, hostile, and accessor/proxy evidence writes nothing and exposes one fixed non-identifying error boundary.
- Missing/orphan/future/malformed/conflicting C3B partners and any invalid C1/C2 foundation fail closed without repair.
- Transaction retry, commit failure, forged return, and lost-response retry preserve atomicity and immutability.
- Twenty-four identical emulator calls create exactly one pair; conflicting candidates race to one immutable winner.
- Static tests prove no attempt-2 path/document, provider/network/logger/random/runtime/index adoption, or mutable earlier evidence.
- Browser anonymous/member/admin read, create, update, delete, list, and collection-group access remains denied.
- Focused mock and emulator suites, full Functions/lint, Rules, frontend/SPA/build, and workflow/artifact checks pass on Node 20/Java 17.
- Security, concurrency/test, and documentation/dependency reviews approve the exact diff.
- Source, tests, merge, Firebase deployment, Stripe/provider state, website publication, and production behavior are reported separately.
Exact owned paths
functions/commerceCommandJournal.jsfunctions/commerceCommandJournal.test.jsfunctions/commerceCommandJournal.emulator.test.jsfunctions/commerceProviderReconciliation.test.jstests/firestore-rules/shop-and-locked.test.jsSTRIPE_COMMERCE_DESIGN.mdSYSTEM_DESIGN.mdSECURITY.mdIMPLEMENTATION_PLAN.mdGITHUB_ISSUES.mdGITHUB_ISSUE_SLICES.md
Dependencies and coordination
- C2 #182 merged as
95be260d08377b7e931dd3526da91b59c5fbf755. - C3A #184 merged as
5c7d2d160237465b367c4dcc799cefcfab1e4e9b. - Hosted journal gate #167 merged as
e1f88720. - PAY-005A2/PAY-005A2 — Validate Stripe refund results before recording success #204 is released; exact current base is
0dbac066a49837e31af72c9821c8044d287035fc. - No open PR, ready issue, duplicate C3B ticket, or conflicting claim existed at publication.
- DATA-001B — Inventory personal data and approve a minimization/retention matrix #110/BACKEND-001 — Audit the Firebase cutover and approve legacy data disposition #113 do not block additive unused synthetic persistence; their policy/inventory decisions still block production data, retention, migration, and live adoption.
Explicitly out of scope
- Editing
functions/commerceProviderReconciliation.jsor changing the C3A matrix. - Provider verification or trusting how evidence was gathered; inputs remain synthetic in tests.
- C3C fresh lease, business-transition authorization, attempt-2 plan/key/document, provider request, checkout/webhook/runtime adoption, response replay, or repair.
firestore.rules, Functions index/endpoints/webhooks/refunds, workflow, package/lock, provider configuration, production data, migration, deployment, and officer procedures.
Claim protocol
Creation is not a claim. Before editing, assign the issue, post CLAIMED by <canonical agent> at <UTC>; branch <branch>, re-read live comments, and keep every excluded path untouched.
Activity
daliu commentedon Jul 14, 2026
CLAIMED by /root at 2026-07-14T02:21:14Z; branch
codex/issue-206-reconciliation-evidence.Exact base:
0dbac066a49837e31af72c9821c8044d287035fc.Exact ownership is the 11 paths in the issue body. The unchanged C3A source, Firestore Rules source, runtime/index/endpoints/webhooks/refunds, workflow, package/lock, provider, production-data, migration, deployment, and officer paths are excluded.
Invariant: persistence is not authorization. This child may save only an exact attempt-1
new_attempt_candidateplus its deterministic audit. It cannot create attempt 2, send to Stripe, advance business state, or prove provider/live behavior.daliu commentedon Jul 14, 2026
IN-SCOPE CONCURRENCY ADDENDUM — 2026-07-14T02:25Z
Design review found one required ordering gate: C3B must not freeze a candidate while C2 can still permit attempt 1. Candidate persistence therefore requires the one prepared trusted timestamp to be at or after C2's stored 23-hour automatic-retry deadline (C2 already stops at equality) and the current leased lifecycle to be expired. A valid candidate before either cutoff writes nothing and returns the unchanged frozen C3A
new_attempt_candidate / requires_persistence_and_authorizationresult. A later exact call can persist it after the gates.This adds no new path or authority. It closes a race between a still-sendable attempt 1 and immutable reconciliation evidence. C3B still makes no provider call and C3C must separately obtain a fresh lease and authorization. Tests will cover deadline-minus-1ns, equality, active takeover, expiry equality, and terminal lifecycle denial.
daliu commentedon Jul 14, 2026
EXTRACTION FINISHED by /root at 2026-07-14T03:11:54Z.
Ready PR: #207
Exact source commit:
e6eb363632f89f4af333507bb1c20891d6847ac6Exact pre-commit diff SHA-256:
56c56e32dc60b164fe23b5490e15a7f7e2ba328d7da40ef79456f92ec91809f7Scope: exactly the 11 claimed paths; working tree clean.
Independent security, code, concurrency/test, and dependency/documentation reviews approved the exact diff with no findings. Local Node 20 / Java 17 proof: focused 253/253, real reconciliation emulator 38/38, Functions lint + 1198 passed / 39 skipped, Rules 357/357, frontend 198/198, SPA 11/11, frontend lint baseline, production diagnostic build, exact path/exclusion/secret/link/diff checks all pass.
Status is in review. No website,
runmprc.com, Firebase, Stripe/provider, production-data, migration, or live-behavior change has been made or claimed. Merge waits for exact-head hosted checks.daliu commentedon Jul 14, 2026
COMPLETED by /root at 2026-07-14T03:15:53Z.
e6eb363632f89f4af333507bb1c20891d6847ac6.1730977eca65dc7df59bb7f195bbc44afd7e5895.Deployment/state truth:
runmprc.comproduction behavior: not changed or claimed; this exact merge has no GitHub deployment record and no web asset/runtime adoption.The exact 11-path claim is released.