Skip to content

[Phase 9] Epic — Full CRM Depth & Relationship Graph #604

Open
0 of 5 issues completed
Open

Description

@cobmojo

Umbrella epic for Phase 9 — Full CRM Depth & Relationship Graph of the SiteStacker parity program. Full context: docs/prds/sitestacker-parity/phase-09-full-crm-depth-relationship-graph.md — the single source of truth (§A architecture rulings, §B deep modules, §C predecessor amendments, §D data model, §E contracts & wiring, §F ADRs, Testing Decisions, Evidence & Acceptance, Tracking Issues). All twenty children below cite that PRD; when an issue body and the PRD disagree, the PRD wins.

Mission

Modern SiteStacker parity — and the missions-CRM depth beyond it — for the operational heart of the platform: a real party model (people, churches, organizations, households), a real relationship graph (stored + derived edges with provenance), and a real record surface (one list engine, one record shell, one timeline) so that staff can open a person, church, organization, or household and see a complete operational record without jumping to donor-specific pages or spreadsheets — and no legacy donor grid, drawer, or detail routes remain. The north star (founder-ratified, PRD D3) is the best nonprofit CRM for Christian Missions organizations: support-raising, sending churches, staff assignment, and mobilization seams over generic-donor-CRM parity. This is a fresh-build replacement phase (D6: no users exist; no continuity ceremony), built directly on Asym Postgres (ADR-0001: Twenty retired), inheriting Phase 4 tenant-isolation plumbing and the Phase 7 party spine as amended 2026-07-06 (PRD §C2).

What to build

One coherent CRM depth layer — party spine → relationship graph → record shell → list engine → search → writes — replacing the legacy donor surface outright. The ten moving parts (each owned end-to-end by the children below):

  1. Party spine (§A1–A3). Thin parties supertype with shared-PK subtyping: persons.id = parties.id (same for household and org subtype rows); party_kind ∈ {person, household, org} with 'group' reserved-not-built; churches are org-kind parties with org_type='church'. donors.party_id ships NOT NULL with a composite tenant FK from the first migration; donors.party_type is never created; donors.missionary_id, donors.type, donors.organization, donors.spouse, the donors.notes scalar, and donor_activities die.
  2. Hybrid relationship graph (§A4–A6). crm_relationships is ONE canonical edge per relationship (never mirrored pairs), typed by a seeded crm_relationship_types catalog (24 launch types dispositioned in §D), time-bound and never hard-deleted. Derived edges (supports-from-ledger, household membership, org contacts) are security_invoker SQL views, never persisted, merged with stored edges in one UNION ALL surface carrying provenance and keyset pagination. "Supports" is the named, versioned supports_policy_v1.
  3. Derived roles, never authorization (§A7). Donor/missionary/board/volunteer/staff/partner are derived display roles from the ledger, records, and edges; authz.memberships stays the sole authorization source, enforced by a CI import gate plus a runtime negative test.
  4. One record shell for every kind (§A10–A12). A header contract (consent badges + per-address deliverability, lifecycle-status chip, duplicate-warning banner, portal/claim chip, owner chip reading assignment edges with N role-qualified assignees + ONE primary, tags, privacy indicator, reserved action-slot registry) + eight live tabs (Overview, Contact, Relationships, Giving incl. Commitments + the payment-instruments read panel, Notes, Activity, Tasks thin, Audit thin/permission-gated) + eight hidden registry sockets (Communications P6, Custom Fields P11, Files P29, Workflow coordination P34 + application/mobilization source P41, Events participation P37, Payment-Methods staff actions, External IDs / provider links P30, Donor Development / Portfolio P27) — rendered by one layout-agnostic component in both the peek drawer and the full page.
  5. One list engine, kind routes as pinned views (§A13–A14). /crm + /crm/people + /crm/churches + /crm/organizations + /crm/households + work queues /crm/relationships, /crm/duplicates, /crm/tasks — one engine. Saved views are first-class live-query rows (private-only at launch; team/everyone reserved); the filter schema is nested AND/OR from day one; the URL is the source of truth for view+filter+sort+cursor; pagination is keyset-only with rebuilt parameterized predicates (deleting escapeSearchValue and the comma-mutation); a server facet-options endpoint; semantic-table a11y with DataTableResponsive as the only grid primitive; Next.js 16 layout + Suspense + canonical detail pages with intercepting @drawer routes replacing the ?donor= state machine.
  6. Cmd-K global search (§A15). A pg_trgm GIN UNION ALL surface (parties display-name + donors email/phone), grouped by kind with recents, permission-bound to the same scope as the detail/list read models, replacing the hardcoded fake SearchDialog.
  7. Party-keyed engagement replaces donor-keyed outright (§A16, §D). crm_notes (Tiptap JSON + plain text; standard/restricted visibility), crm_activity_events (seeded taxonomy), flat tags on the party spine, and a 'party' task-link record type; the timeline is always composed at read from source-truth branches — never persisted.
  8. Plain Asym writes (§A17). Every mutation: authz capability check → zod validation → withTenant → ONE transaction containing (a) the row write, (b) a crm_audit_events row, (c) the crm_activity_events emission. No fail-closed provider gate, no provider-idempotency log, no crm_write_gates dependency — ADR-0001 §4 withdrew the gate and Phase 9 must not invent one.
  9. Governed CSV export (§A18). A minimal list CSV export through Phase 3 export governance (csvSafeCell + consent gate + audit), honoring field policies.
  10. Fresh-build discipline (D6, §E4). The legacy donor grid, drawer, ?donor= machinery, and monolithic detail service are deleted; the cursor envelope, virtualization, named-views storage, and the CRM Inline Operation Parity #270 operation shell are reused on merit; seeds regenerate correct-from-start including synthetic duplicate pairs, ≥1 merged tombstone per kind, and staff-assignment edges — else the dedupe queue, merge workbench, duplicate banner, and owner chips are undemonstrable in fresh environments.

Hard invariants (bind every child)

  • Tenant isolation: composite (tenant_id, id) keys, ENABLE+FORCE RLS, withTenant tenant-guard, cross-tenant negative CI tier — inherited from Phase 4, never re-invented.
  • Single canonical edge, never mirrored — mirrored relationship pairs are a forbidden topology.
  • Derived kinds are structurally blocked from the stored catalog (CHECK + API rejection + test); relationship types are never hard-coded in an app page (catalog edited via migrations/seed only until the Phase 11/12 catalog UI).
  • Derived roles are display-only, never authorization (CI dependency gate: the authz package cannot import the roles view; plus a runtime negative test).
  • Provenance-scoped permissions: giving-derived edges and giving context inherit FINANCE visibility; anonymity is respected; both negative-tested.
  • Member-care exclusion: member_care_private_notes stays RLS-revoked; the composed timeline references no member_care% relation (CI assertion); only the permission-gated Overview indicator references care.
  • Staff NEVER key card data into any surface this platform renders — read-only Stripe PaymentMethods metadata only; updates complete via donor secure links (§A11; SAQ-A intact). No admin-app card form, ever.
  • Cultivation stage is NEVER a lifecycleStatus value; asks are never edges or custom fields (§A12 Phase 27 guardrails).

Children

Per the PRD's dependency notes, #606 carries status:blocked-on-Phase-4 and status:blocked-on-Phase-7-T4; #610 and #626 carry status:blocked-on-Phase-4. All other children are gated inside the phase via the blockers above.

Prerequisites & posture

Done when

Verbatim from the PRD: "Staff can open a person, church, organization, or household and see a complete operational record without jumping to donor-specific pages or spreadsheets" — and no legacy donor grid, drawer, or detail routes remain.

Acceptance criteria

  • All twenty-three children ([Phase 9] Docs foundation: party-topology ADR, OpenSpec delta, glossary + matrix discipline #605–[Phase 9] Assemble the permanent negative/safety tier + structural CI gates + e2e a11y #627) are closed.
  • The done-when holds end-to-end in a seeded demo environment: every kind opens to the full A10 record shell (header + eight live tabs) from both the list drawer and the canonical detail page.
  • No legacy donor grid, drawer, ?donor= state machine, or monolithic detail-service routes/symbols remain (D6 structural CI gate).
  • The permanent negative/safety tier passes: cross-tenant isolation; derived-roles-never-authorize (import gate + runtime test); frozen roles-view shape; derived kinds rejected from the stored catalog; no member_care% relation in the timeline + member_care_private_notes RLS-revoked; restricted notes invisible via Notes tab and timeline; the four cross-surface leak tests (household grants zero portal access; no party notes/activity in missionary projections; no broad CRM enumeration from the missionary workspace; no CRM internals in donor-portal projections); FINANCE visibility + anonymity on giving-derived edges; search never leaks unreadable fields and excludes member-care/finance-provenance data; CSV export honors field policies; , % ( ) round-trip; stable keyset pagination over duplicate timestamps; merge re-point dedupe/tombstone/replayability.
  • Structural CI gates pass: authz-import gate, roles-view schema-shape test, no-new-inline-contact-columns migration lint, every /crm* route registered in the e2e a11y spec, D6 legacy-deletion gate.
  • No provider gate, provider-idempotency log, or crm_write_gates dependency exists anywhere in the phase (negative check; ADR-0001 §4).
  • Seeds demonstrably include synthetic duplicate pairs, ≥1 merged tombstone per kind, and staff-assignment edges (E4) — dedupe queue, duplicate banner, and owner chips are populated in a fresh environment.
  • The F1 ADR is authored; the §C1/§C2 amendment one-liners land on the Phase 4/Phase 7 PRDs; the C6 congruence touches land (phase-map row-9 soft-Phase-8 qualification, parity-matrix Phase 9 row, OpenSpec delta); the glossary pass lands in CONTEXT.md.
  • The evidence file (Phase 0 Built/Live/Confirmed discipline, under docs/ops/phase-evidence/) records migrations applied, the full test suite incl. the negative tier, route/API checks, the seeded-demo walkthrough, and the explicit list of what Phase 9 did not build (the sockets and reserved seams).

Depends on

Related (do not build here)

🤖 Generated with Claude Code

Activity

  1. cobmojo commented on Jul 6, 2026

    @cobmojo
    ContributorAuthor

    Roadmap v2 renumbering applied (2026-07-07). The program adopted Roadmap v2 (docs/prds/sitestacker-parity/roadmap.md on PR #465): 41 phases (0–40), with every phase number above 9 renumbered. The bodies of this epic and children #605–#618, #621, #624, #625 were updated in place to the new numbering (129 token edits). Phases 0–9 are unchanged.

    Key moves relevant to this epic's sockets and guardrails: Custom Fields 10→11, Full Permissions 11→12, Ledger 12→13, Offline Batch 13→15, Pledges 15→16, PDF Templates 16→18, Statements 17→19, Donor Dashboard 22→25, System Messages 24→17, Files 25→29, Mailchimp 26→32, Events/Groups 28→37, Imports 29→30, Reporting 30→33, Workflow Engine 31→34, Donor Development 33→27. New phases: 10 (Sensitive-Data Safety), 21 (Field Accounts), 26 (Support Hub), 31 (Platform API), 38 (Member Care Ops), 39 (Field-First UX), 40 (Data Stewardship/AI).

    Full v1→v2 mapping table: docs/prds/sitestacker-parity/roadmap.md#renumbering-map-v1--v2. Comments above this one predate the renumbering and use v1 numbers — decode via the map.

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

    type:docsDocumentation only

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions