Skip to content

AL-2104 — [P28-F14] Follow the same reported gift through recording and handling #2104

Open

Description

@cobmojo

AL-2104 — [P28-F14] Follow the same reported gift through recording and handling

Approved implementation ticket. Plan key: P28-F14. Work only after its real blockers and exact qualification gates are satisfied. Publication does not establish implementation, qualification or activation.

Parent

Phase 28 implementation specification #2064. Canonical specification commit e93e2e9958f32380e28ec0e005d7ef3ee24f7e7a; documentation PR #2065. The full published spec and comments were verified against the canonical source. Preserve the parent’s body, title, labels, state and comments; new native child/dependency relationships are metadata only.

What to build

Maya and Finance see the same intake progress into exact recorded money while unresolved physical handling and duplicate/already-recorded resolution remain truthful and independently owned.

Source of truth and acceptance ownership

  • Finance Stewardship: part 1 · part 2
  • Common safeguards and proof: part 1 · part 2
  • Owner reconciliation and later-decision precedence: part 1
  • Evidence and existing seam limits: part 1

Primary requirement references: REQ28-F31. Stories: US28-F31.

The checked criteria below are this ticket’s primary trace ownership, not a relaxation of the full normative contract. For a requirement split across tickets, this ticket delivers its named behavior and enforces applicable invariants immediately; the other ticket’s full scenario is not claimed complete early. All source-specific state/control, numeric-boundary, owner, monitoring and exclusion sections in the linked volume remain binding. Preserve settled D1–D50 and C01–C20; do not reopen them or import contradictory vendor conventions.

Acceptance criteria

  • AC28-F31-01 — Recording the gift resolves exactly covered recording work while Handling still needed remains if applicable; completing transfer first does not post money.
  • AC28-F31-02 — Exactly reconciling an already-posted check links the result and preserves both report origins without creating another contribution, commitment application or receipt.
  • AC28-F31-03 — Return, loss, dispute, rejection, duplicate or mistake retains attributable history and precise remaining work; reversal or NSF does not resurrect physical possession.

Implementation decisions and slice boundaries

  • Finance continuation must not re-enter/repost/reissue receipt; split/move/carryforward preserves lineage.
  • Same amount/name/date/check number is insufficient; independent reports keep origins until source-qualified resolution.
  • No universal one-report/one-header cardinality; financial recording does not certify transfer/deposit/clearance; reversal does not recreate possession.

Binding normative decisions

REQ28-F31 — Continue the same intake and keep handling separate

Story US28-F31: As finance staff, I want to continue the same reported item,
so that Maya does not re-enter it and recording money does not hide unfinished
physical handling.

Decision basis: D33 C12–C16; P27 F61–F67.

Finance SHALL continue or exactly reconcile the same Phase 15 intake through its
qualified batch/commit path. Lineage SHALL survive splits, moves, carry-forward,
corrections and linking an already-posted result without reposting or reissuing
receipts. Similar amount, name, date or check number is not exact reconciliation.
Independent reports retain separate origins until owner-qualified duplicate
resolution; neither universal one-report/one-header cardinality nor a general
matching engine is presumed. Financial recording, reported possession/transfer,
deposit, clearance, commitment and fulfillment SHALL remain separate. Each
accepted report SHALL have qualified next responsibility or an owned unresolved
exception using existing source work, not a guessed staff audience.

Testing, migration and operating obligations

Every reached layer belongs to this slice: actual source persistence and upgrade behavior, canonical command/query, permitted UI, and executed proof. Reuse native owners, shared Base UI/base-maia components and the established architecture. No parallel CRM, Tasks, consent, sender, permission or retention engine.

Before affected activation, prove current trusted tenant/actor/subject/role and fine-grained purpose/field authority, effective old-row/new-row RLS and grants, same-tenant relationships, applicable uniqueness/history/delete constraints, and every reached view/RPC/storage/service path. Caller identifiers and metadata are never authority. Source revision and privacy checks precede disclosure and are repeated after ordering waits where required.

Implement semantic idempotency, required source history/receipt and durable secondary intent, truthful partial/unknown/lost-response recovery, context-switch/revocation fencing, temporal boundaries and correction lineage. Preserve each adopted default and exact Off/new-work/accepted-work/reenable behavior. Never infer consent, money, completion or actual interaction from a presentation action.

Test through existing Playwright whole-user journeys as the primary boundary; use canonical API Vitest tests, real PostgreSQL roles and at least two concurrent sessions, qualified Auth and existing provider-contract seams where the browser cannot prove the guarantee. Reuse existing account-frame/session-guard, task-recovery, roster/redaction, operation-handler, dispatch/restriction, field-policy SQL and replay proof patterns while retaining their demo/mock limits. No new test framework and no skipped mandatory fixture may count as a pass.

Use persistent truthful outcomes, clear context and next steps, keyboard/screen-reader/touch/list equivalents, narrow-screen reflow, localization, safe international data and honest interrupted-connection recovery. Inventory and fence reached legacy writers/readers, preserve lawful history and unresolved obligations, and qualify mixed versions, restore, retention and containment before activation. Adopt existing source-specific signals/thresholds/owners/responses and measured cohort budgets; do not invent a numeric SLO or defer correctness to monitoring.

Shared requirements Q01–Q27 apply where relevant to every ticket. R43, F41–F44, M41–M42 and C49 apply to their affected lanes. W05/W06 aggregate composed evidence; they are not permission to defer any slice’s safeguards.

Exact consumed contracts

Related source owners (references are not automatically blockers): #2021, #2026, #2027. Current source contract takes precedence over superseded issue wording.

Blocked by

A blocker’s closure is not runtime qualification: verify the exact consumed revision and executed proof. Keep independently qualified capabilities useful while an affected effect is unavailable. Integrate the approved specification into the implementation base; the open documentation PR is not evidence that these features exist.

Related approved slices

Short ticket keys in implementation notes resolve here. Published requirement, acceptance, story, decision and completeness codes retain their original meanings.

The full execution index and primary-acceptance ownership map are published with the Phase 28 release-qualification ticket. Every shared safeguard remains mandatory within its implementing slice.

Activity

  1. changed the title [-][P28-F14] Follow the same reported gift through recording and handling[/-] [+]AL-2104 — [P28-F14] Follow the same reported gift through recording and handling[/+] on Oct 19, 2026
  2. II-ricky-bobby-II commented on Oct 25, 2026

    @II-ricky-bobby-II
    Contributor

    Spec r1, Scope, Oct 24, 2026, pending Samson approval

    Status: r1 for #2104 (P28 F14), base develop 6ac594d. The issue body is the source of intent; this spec is the build contract. It's within both caps, so no split. F14 adds only status and navigation for a reported gift. Maya and finance see the same report move through recording, already-recorded or duplicate resolution, and physical handling, as separate facts from their owners. F14 adds no engine. Natively blocked by #2103, #2026 and #2027, which matches the body's "Blocked by" list. The body also consumes #2021, which isn't natively linked here but blocks both #2026 and #2027 natively. By slice name it needs F13-a and F13-b. All three owners (#2021, #2026, #2027) are open and unbuilt, so the production outcome adapter is unavailable (OQ1). Context read: #2064 (finance stewardship, common safeguards, owner reconciliation and evidence comments), the #2152 index comments, the #2103 r1 spec, the #2009, #2021, #2026 and #2027 bodies, and finance-stewardship.md (REQ28-F31 :800-828, F41–F44 :1081-1231).

    §0 What I checked

    §1 Intended behavior

    Maya reported John's envelope on Oct 19. Finance records it on Oct 21 as a 50.00 USD contribution. Maya's report now shows Recorded by finance and, on a separate line, Handling still needed until finance marks the envelope deposited. Finance, on that contribution, sees "Reported by Maya Chen, Oct 19" and can open the report. If finance instead links the report to a check already posted last week, both show the link and nothing new is recorded.

    1. Outcome port (OQ1). ReportedGiftOutcomeSource.outcome(intake_ref) returns two independent parts, each with its owner's revision:

      It also has reportsForContribution(contributionRef). Each owner implements its part; until then the only production adapter is unavailable, and F14 shows "Status not available yet".

    2. One composed status. reported_gift_status(report_ids) is a guarded read function. It returns F13's submission plus the port's two parts, filtered by current authority: the recorder, a holder of the current AL-2069 — [P28-W01] Enter and switch the exact authorized workspace context #2069 context for that subject, or finance:manage_contributions. Revoked context removes the read.

    3. Maya's view. F13-b's list gains a detail panel with a Recording line and a Handling line, each with its date and owner's wording. Exceptions show their reason and the remaining work ("Returned to donor on Oct 22; nothing left to do"). F14 never resolves or closes work itself; covered recording work closes in AL-2021: Continue the same reported gift through finance recording (P27-F22) #2021's owner.

    4. Finance view. The admin contribution detail sheet gains a "Reported origins" block from reportsForContribution: each origin's recorder and date, with a link to the report. Two independent reports linked to one contribution both appear.

    5. No matching. F14 never suggests a match, a duplicate or a link from a similar amount, name, date or check number.

    6. Separate facts. Recording doesn't mark handling done, and transfer or deposit doesn't show Recorded. A reversal or NSF shows on the Recording line only; the Handling line never returns to "In hand".

    Threat model

    Threat Control Criteria
    Recording hides unfinished handling Two independent parts A1
    Transfer shown as posted money Recording only from #2021's part A1
    Similar details treated as a match No suggestion code; owner links only A2, S1
    Linked report creates a second contribution Read-only; census A2
    Reversal resurrects possession Handling only from #2027's part A3
    Status leaks after context change Current authority in the read function B1

    Non-goals: finance recording, linking, duplicate resolution or handling commands; a matching engine; changes to money, receipts or fulfillment; notices.

    §2 In scope

    • The port, its unavailable production adapter and the test adapter.
    • The guarded status function.
    • Maya's detail panel and the finance "Reported origins" block.
    • Docs and tests.

    §3 Out of scope

    §4 Criteria

    Tags: [A] Acceptance-owned test, [B] Builder test, [S] static, [R] real PostgreSQL (#2250's step). The owners are F14's test adapter, driven through event sequences.

    • A1 [A][R] Record and handle separately (AC28-F31-01).

      • Record-before-transfer: Recording shows Recorded by finance and Handling shows Handling still needed.
      • Transfer-before-record: Handling shows Transferred and Recording stays Awaiting finance.
      • A census shows F14 wrote no contribution, receipt or work row.
    • A2 [A][R] Already posted (AC28-F31-02).

      • Linking a report to an already-posted check shows "Linked to an existing recorded gift" on the report, and the contribution's block shows both report origins.
      • Two reports with the same amount, name, date and check number, with no owner link, show as two separate reports with no suggestion.
      • A census shows no new contribution, commitment application or receipt.
    • A3 [A][R] Exceptions and corrections (AC28-F31-03).

      • Returned, lost, disputed, rejected, duplicate and mistake outcomes each keep their history and show the remaining work.
      • A split and a carry-forward keep the lineage to every resulting contribution.
      • A reversal and an NSF after deposit show on Recording only; Handling still shows Deposited.
    • B1 [B][R] Current authority. After Maya's AL-2069 — [P28-W01] Enter and switch the exact authorized workspace context #2069 context for the subject is revoked, she can still read her own reports as recorder, a context holder can't, and a finance user can.

    • S1 [S]

      • No F14 module writes donations, receipts, batches or fulfillment.
      • There's no similarity, matching or suggestion code.
      • The new function is in GUARDED_RPCS.
    • C1 [A] Journey. On a real stack with the test adapter, at a phone viewport for Maya and desktop for finance, keyboard-only and with screen-reader names checked:

      • Maya sees Recorded by finance and Handling still needed;
      • finance sees the report origin on the contribution and opens the report;
      • deposit clears the Handling line.

      Axe clean.

    Handback

    §5 Mapping

    Ticket requirement Criteria
    AC28-F31-01 recording resolves covered work; handling remains; transfer doesn't post A1, C1
    AC28-F31-02 already-posted link keeps both origins; nothing new created A2, C1
    AC28-F31-03 exceptions keep history; reversal doesn't resurrect possession A3

    §6 Declared edits

    File Change Owner
    supabase/migrations/<VERSION>_reported_gift_status.sql + rollback Guarded status read function only Builder
    packages/api/src/offline-reporting/outcomes/** (new) Port, unavailable adapter, test adapter, status service Builder
    packages/api/src/missionary-portal/offline-reports/route.ts (F13-a) Status detail read Builder
    packages/api/src/admin/contribution-operations/detail-read-model.ts Additive "Reported origins" field from the port Builder
    apps/missionary/app/donors/** (F13-b section) Detail panel Builder
    apps/admin/app/(app)/contributions/contribution-detail-sheet.tsx "Reported origins" block Builder
    packages/api/src/identity/tenant-guard.ts New GUARDED_RPCS entry only Builder
    docs/features/offline-reporting/reported-gift-status.md (new) Port contract and status meanings Builder
    tooling/eslint-config/suppressions.json, tooling/shadscan/findings.json Refresh only (§7) Builder
    tests/unit/acceptance/issue-2104/** A1–A3, S1 Acceptance
    tests/e2e/reported-gift-status.spec.ts (new) C1 Acceptance

    §7 Pins this change touches

    Pin Where In or out Criterion
    F13 submissions and routes <VERSION>_offline_receipt_reporting.sql (#2103) Out: read, unchanged B1
    Contribution detail read model packages/api/src/admin/contribution-operations/detail-read-model.ts In: one additive field A2
    Missionary navigation list tests/unit/apps/missionary/missionary-navigation-search.test.tsx:67-75 Out (no nav item) —
    Donor detail suppressions tooling/eslint-config/suppressions.json:1181-1188 Out: the donor detail isn't touched —
    Shadscan app floors scripts/verify/shadscan.mjs (ci.yml "Enforce complete Shadscan app floors and reviewed findings") In: no new unreviewed finding H4
    #2250 step and order issue-2250/static.acceptance.test.ts Out (no step) H1
    Workflow fingerprints issue-2155/…:257-280 (bc8d57a2be564d61), issue-2201/…:166-178 (1078832efaff84e6) Out under the OQ3 default —
    docs/ci.md byte pin issue-2250 prefix pin and #2304 Out: not edited —
    Phase 28 PRD archive verify:phase28-spec (ci.yml :91), 911 files under docs/features/missionary-workspace-depth/grill/** Out: none edited —

    §8 CI

    No new CI step and no docs/ci.md edit under the OQ3 default. Real-DB tests run in "Verify tenant isolation (real stack)" (bun x vitest run realstack.acceptance, env TENANT_GUARD_IT_ADMIN_DATABASE_URL). Everything else runs in existing ci.yml steps: bun run format:check, "Run affected lint on pull requests", "Run affected typecheck on pull requests", "Run unit tests", "Run acceptance tests", Shadscan. Playwright C1 runs locally and is recorded (H3).

    §9 Data, grants, person_id and donor spine

    • No new table. reported_gift_status(report_ids uuid[]) is a SECURITY DEFINER guarded function over F13's submissions that applies current authority; outcome parts come from the port.
    • Grants: the function is reachable only through the tenant guard; no anon/authenticated/service_role table privileges change. No RLS policy on an existing table is dropped or recreated.
    • person_id: F14 reads and writes neither profiles.person_id nor missionaries.person_id. No [Security] missionaries.person_id column-grant fix: anon cross-tenant read + select("*") routes (follow-up from #2256) #2268 block is needed.
    • Donor spine: needs Blake's sign-off; Samson relays it to Blake at merge. F14 writes nothing, but it adds a field to the contribution detail read model and shows recorded contributions on a report.

    §10 Size

    Part Code+docs Tests
    Migration and rollback ~60 —
    Port, adapters, status service, routes ~200 ~380 (real-stack and API)
    Maya's panel and finance block ~150 ~220 (component and Playwright)
    Docs ~50 —
    Total ~460 ~600

    Within both caps; no split.

    §11 Dependencies and migration

    §12 Open Questions (proposed defaults in bold)

    1. Owners unbuilt. AL-2021: Continue the same reported gift through finance recording (P27-F22) #2021, AL-2026: Resolve duplicate reports and existing recorded gifts by exact lineage (P27-F23) #2026 and AL-2027: Track physical handling and truthful exceptions independently of posting (P27-F24) #2027 are open, and AL-2021: Continue the same reported gift through finance recording (P27-F22) #2021 waits on AL-2009: Report an actual gift receipt with honest missing details (P27-F21) #2009 and [Phase 15] Escape valve — commit clean rows, carry exceptions #773. Default: F14 merges on the test adapter with the production outcome adapter unavailable, so reports show "Status not available yet" beyond Awaiting finance and the finance block stays empty. Wiring each owner's part, plus A1–A3 rerun against it, is a handback gate on that owner. No status is derived from donations, batches or similarity. This is a departure that narrows. The alternative is holding F14 until all three merge.
    2. Who reads a report's status. Default: the recorder always; a current AL-2069 — [P28-W01] Enter and switch the exact authorized workspace context #2069 context holder for the subject; and finance:manage_contributions. Flagged for Samson.
    3. Journey in CI. Default: no CI step; local run recorded. The alternative is a test-e2e step "Reported gift status journeys" with run text node scripts/run-with-ci-env.mjs -- node node_modules/@playwright/test/cli.js test -c playwright.missionary.config.ts tests/e2e/reported-gift-status.spec.ts --workers=1, which re-pins both fingerprints and byte-pins docs/ci.md (with docs/ci.md:35 and :366 still call test-e2e informational on develop; update with the #2180 byte pin in one change (follow-up to #2161) #2304).

    §13 Deploy

    • Production is behind develop. It's missing the 58 develop versions 20260515025814–20261024120000 (25 applied, newest 20260513173938). The dev project is 55 behind, with four 20261001* collisions and an extra 20260519233845. Develop's catalog is not assumed live anywhere.
    • Order: (1) the backlog, as its own deploy; (2) F13-a; (3) F14's migration; (4) the app release. Nothing beyond Awaiting finance shows while the adapter is unavailable. (5) Each owner's real part as it merges.
    • Rollback: the app first. The migration's rollback drops only the function.
    • Samson relays the deploy plan to Blake at merge.

    §14 Flags

  3. II-ricky-bobby-II commented on Oct 25, 2026

    @II-ricky-bobby-II
    Contributor

    Approval (Samson, factory lead, Oct 24, 2026, 9:54 PM PT), relayed by Scope:

    All five r1s are approved. Versions (prefix 20261026): … F14 20261026100000. Defaults accepted as departures: test adapter, production unavailable, handback gate Samson checks (F14: outcomes are unavailable until #2021, #2026 and #2027 exist). F14 OQ2: a report's status is readable only by the person who submitted it (their own reports only) and by staff holding finance:manage_contributions in that tenant. CI-step OQs closed: no.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ReadyBlake-approved for the agent factory to pick up (oldest first)complexity:hardHigh complexity or scopetype:featureNew functionality

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions