Repository navigation
feat(db): add dormant protected first-capture intent (migration 0014) - #16
Merged
Merged
Conversation
Adds the P-C prerequisite for R2: one atomic, dormant SQL operation that accepts the first capture intent for a payment backed by a completed immutable P-B MANUAL authorization. - request, manifest and acceptance tables with unique first intent per payment and per merchant/operation/key; exact replay is read-only - shared exclusive APPLICATION/CAPTURE_INTENT permit; exact six-group child images (payment, attempt, outbox, authority, audit, acceptance) commit atomically with a stable capture:attemptUUID command - SECURITY DEFINER code pins search_path, current_user and session_user; NOLOGIN owner roles; PUBLIC revoked; no login or runtime role granted - normalized actual-owner projection used at prepare and in immediate and deferred reverse closure, so extra earlier owned audits or outbox rows (including uppercase UUID variants) are rejected - lock order preserved: request key, account SHARE, advisory, scope, payment, attempts by id - migrations 0000-0013 unchanged; 0014 is a forward migration with an appended journal entry; historical F01 assertions unchanged No capture journal, lot or fee accounting, no runtime grant and no scope activation. Activation prerequisite: the relay and handler must require a provider_command_authorities row for provider.capture.requested (see PR). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Adds migration
0014_first_capture_intent(P-C of the R2 prerequisites): one atomic, dormant PostgreSQL operation that records the first capture intent for a payment backed by a completed, immutable P-B MANUAL authorization. The intent request, manifest and acceptance, the stablecapture:attemptUUIDprovider command (payment, attempt, outbox, command authority, audit) and the revision bump commit together or not at all. No capture journal, lot or fee accounting, no runtime or login-role grant, no scope activation. B2-A, B2-B and B2-C are not started.Review guide
Read carefully (T2 areas):
apps/api/drizzle/0014_first_capture_intent.sql(578 lines): request wrapper and replay (first_capture_intent_request), theprepare/apply/acceptentry points, the normalized actual-owner projection (first_capture_intent_record_owners, used at prepare and in immediate and deferred reverse closure),check_first_capture_intent(six exact child groups), the issuance guard (SECURITY INVOKER; binds current_user, transaction, backend pid and session_user), the dispatch rewrites of existing 0009/0010/0013 guard functions (pg_get_functiondef overlay; fails closed on anchor mismatch), NOLOGIN roles and PUBLIC revocations.apps/api/drizzle/meta/_journal.json(appended idx 14,whengreater than 0013) andapps/api/src/database/schema.ts(mapping only).Skim: harness and spec count bumps from 14 to 15 migrations and
staged(14)pinning of P-B tests; four doc paragraphs;first-capture-intent-harness.tsfixtures (fake test key, test-DB-only grants).Least confident about: (1) full-table, non-sargable owner-resolution scans of
outbox_eventsandaudit_logswhile the scope lock is held (correctness fine, cost unmeasured); (2) thexmin::textversuspg_current_xact_id()::textnot-equal idiom (copied from 0009; fails open after xid epoch rollover, bookkeeping columns only); (3) behaviour on PostgreSQL versions other than 17 (pg_input_is_validneeds 16 or later; only PG17 was run).Tests removed, weakened or skipped: none removed or weakened.
settlement-f01.pending.spec.tsis unmodified (its six F01 assertions still fail as expected withRUN_F01_PENDING=1). Edits to existing specs are migration-count bumps and an added 0014 phase. The review-signals guard warns about two weak matchers (expect(later.error).toBeDefined()) in the NEWfirst-capture-intent.spec.tsrace tests; they are paired with row-count assertions but do not pin an error message (reviewer follow-up CR7-F3). The six skips are the unchanged env-gated F01 suite.Task size
T2. Invariants touched: idempotency (request unique per merchant, operation and key; payload, hash and principal compared; replay is read-only); local intent, outbox, audit and replay response commit atomically; provider command key stable (
capture:attemptUUID); ledger and evidence immutability and uniqueness unchanged (new tables underprotect_financial_evidence; no journal or lot is written); captured is not settled is not paid out (capture accounting stays blocked: receipt AWAITING_APPLICATION, D02 BLOCKING_REVIEW). Transaction boundary: one READ COMMITTED transaction holding one exclusive APPLICATION/CAPTURE_INTENT permit; deferred constraint triggers re-check the six groups and exactly one revision +1 at commit; a prepare-only commit cannot succeed.Checks
Local runs on commit
fcdc190(rebased on origin/main f623df0), Docker PostgreSQL 17, mocked Stripe and broker boundaries. Local runs are not hosted CI.pnpm test:integration:dockerRUN_F01_PENDING=1settlement-f01.pending.spec.ts, run on the identical tree before commitpnpm lint,pnpm build,pnpm typecheckpnpm testpnpm test:verificationnode scripts/check-migration-history.mjs origin/main HEADnode scripts/check-pr-review-signals.mjs origin/main HEADnode scripts/sync-agent-skills.mjs --check,node scripts/check-engineering-workflow.mjs, diff --checkUnverified boundaries
Hosted CI; Node 22; PostgreSQL versions other than 17; live Stripe and multicapture entitlement; RabbitMQ and Redis runtime; production-size table performance; any real role-grant topology outside the migrations. No runtime path invokes the new SQL.
Review
T2 workflow
engineering-workflow, taskr2-prerequisite-p-c-implementation(HIGH_RISK), ledger at HUMAN_APPROVAL; correction budget 2 of 2 used and not reset./security-reviewcommand was run through the Claude Code Skill tool on the COMMITTED diff against origin/main (17 files, 1330 insertions) and reported no finding at or above 0.7 confidence; a supplemental custom-prompt security review of the same content returned SECURITY_APPROVED. Limitations: both are static reads by sub-tasks (no SQL executed); the built-in run did not read migrations 0001-0006, 0008, 0011, 0012 (P-B proof validators) or the RabbitMQ consumer; it was not theclaudeCLI binary, whose earlier attempts failed on credit or egress approval; an earlier built-in run saw an empty diff (uncommitted work) and is superseded. Treat security coverage as adequate for the dormant state, not as a proof for activation.Activation prerequisites (security; not P-C scope; none exploitable while no scope is enrolled and no login role holds EXECUTE)
provider_command_authorities(activation-blocking): the unchanged relay (outbox-relay.service.ts) and handler (payment-command-handler.service.ts) execute any PENDINGprovider.capture.requestedrow from its payload without readingprovider_command_authorities, provider mode or provider account, and the outbox ownership guard does not resolveproviderPaymentId. Before any scope is enrolled or the CAPTURE contract installed: require a matching authority row in the relay and handler (or deny such outbox rows in SQL without one), prove the TEST or live account binding, and add a negative test for a raw outbox row with unrelated UUIDs and a realproviderPaymentId.actor_idthroughGET /audit-logs; redact or replace before activation.payment_method_tokenis copied into immutable manifests (same pattern as 0013); decide retention and deletion handling before activation.Docs and temporary files
Owning docs updated:
docs/payment-lifecycle.md,docs/database-design.md,docs/concurrency.md,docs/webhooks-and-idempotency.md. Approved decision records: the P-A and P-B payment-proof and MANUAL authorization contracts were read for T2; no new decision record. Temporary files kept (ignored, until merge or rejection) under.tmp/agent-workflows/r2-prerequisite-p-c-implementation/(state, plan, immutable reviewer and security reports, verification files, final-checks files, handoff) and.tmp/r2-prerequisite-p-c-implementation/(pr.md and the commit message).🤖 Generated with Claude Code