Repository navigation
AL-2001: Share a new eligible partnership update with its exact audience (P27-C01) #2001
Description
AL-2001: Share a new eligible partnership update with its exact audience
Approved implementation ticket. Plan key: P27-C01. Labels: type:feature, status:blocked, complexity:hard. Publication does not authorize implementation before its documented gates are satisfied.
Parent
Native child of the unchanged Phase 27 specification. Parent hierarchy is not a blocking edge.
Source of truth
Documentation PR: #1993. Exact specification commit: b5a65b3f2da880fcbfa4b3cae189d216d06542be. Inspected implementation baseline: 71b81fae2ce1f9b615bcfee8606cd2c978a70e71. Publishing this contract does not establish implementation, merge, qualification or activation.
- docs/prds/sitestacker-parity/phase-27-donor-development/context.md (REQ27-C01, REQ27-C02, REQ27-C03, REQ27-C04, REQ27-C05, REQ27-C06)
- docs/prds/sitestacker-parity/phase-27-donor-development/proof.md (Q01–Q16)
- docs/prds/sitestacker-parity/phase-27-donor-development/operations.md
- docs/prds/sitestacker-parity/phase-27-donor-development/owner-reconciliation.md
- openspec/changes/add-donor-development/specs/donor-development/spec.md
- packages/api/src/admin/crm/notes/service.ts:1
- packages/api/src/missionary-portal/donor.ts:119
- tests/unit/packages/api/crm-notes.test.ts:34
- tests/unit/packages/api/missionary-portal/donor-mutations.test.ts:74
- [Phase 9] Build the activity timeline: crm_activity_events + composed UNION read #617
- [Phase 9] Enforce the permission/projection chokepoint + cross-surface leak negative tests #622
- [Phase 3] Change-control engine: field_policy_change_requests + widen() + surface lattice + baseline guard #498
- REQ27-C01 — Voluntary missionary contribution and independent obligations
- REQ27-C02 — New eligible shared content and truthful private alternatives
- REQ27-C03 — Exact ongoing audience and historical access
- REQ27-C04 — Qualified source, attribution and mutation invariants
- REQ27-C05 — Atomic shared-update acceptance and current-authority recovery
- REQ27-C06 — Calm composition and context-bound drafts
- REQ27-Q07 — Operational follow-through without coercion
Stories: US27-C01, US27-C02, US27-C03, US27-C04, US27-C05, US27-C06, US27-Q07. Primary acceptance ownership: 23 exact source cases reproduced below.
Applicable ADRs:
- docs/adr/0001-asym-postgres-owns-crm-truth-twenty-retired.md
- docs/adr/0211-distinct-human-approval-for-projection-widening.md
- docs/adr/0183-source-owned-work-projects-into-one-shared-tasks-hub.md
What to build
Implement the qualified P9-owned update/encounter source and bounded P12 create/read/history/correct/restrict capability consumption as one vertical behavior, with P27 development meaning. Render a clearly shared composer and saved entry in the existing staff context and narrow missionary projection. Keep a qualified private-note destination separate and truthfully labeled. General updates require no invented encounter; two contributions about one actual encounter retain separate words and one typed occurrence. Enforce exact tenant, partnership, source, fields, ongoing audience and independently granted earlier-history authority at commands, direct database paths and current result recovery. Save state/history/audit/receipt and required local intent atomically, derive attribution server-side, and preserve context-bound drafts after slow/lost results without persistent sensitive offline drafts.
Context and contracts
Existing admin notes service is native but uses general standard/restricted notes; it is not D26 shared history. The missionary activity command still inserts donor_activities after donor ownership checks and stamps now. Current memory/mocked unit tests do not prove transactional audience/history authorization. Existing P9 timeline #617 and projection #622 are OPEN; their old bodies do not themselves supply D26 eligibility. This slice extends the owner source; it must not build a second diary or convert private notes.
Current authority, tenant/field/history-safe read and write behavior, atomic source history/receipt/required intent, truthful lost-response recovery and applicable two-session concurrency proof accompany this slice. Base UI/base-maia/Zinc, keyboard/screen-reader/mobile/zoom behavior and slow/failing network recovery accompany every reached screen. These are in-slice delivery obligations under REQ27-Q01–Q14, not deferred to final qualification. Terminal Q tickets prove composition and release evidence.
Scope and non-goals
- Migrating private/imported/care messages into shared history
- Full Phase28 workspace, personal journal, new ACL/team engine, attachments or all-collaborator broadcasting
- Exchange-recency calculation, ratings, compulsory follow-up or coaching
Acceptance criteria
- AC27-C01-01: A missionary can decline to write and continue ordinary permitted use without penalty, required stage/task, activity report or coaching enrollment; staff cannot configure such coercion through this lane.
- AC27-C01-02: Saving or reading an update leaves managed-work admission, pause/closure, source obligations and staff status unchanged; a gift handoff cannot be completed by a note.
- AC27-C01-03: An independently selected personal follow-up is created once through its source. Its failure or cancellation neither fails nor duplicates an already accepted update.
- AC27-C02-01: Existing private/imported/internal/care/location-sensitive/financial fixtures remain protected after activation, import, refresh and shared-history migration.
- AC27-C02-02: The private destination discloses its actual reader scope, and its absence leaves a clear unavailable/cancel option with no shared save.
- AC27-C02-03: A newly authored eligible update can be saved directly in its qualified shared context without a repeated sharing checkbox or supervisory approval.
- AC27-C03-01: Two ministries sharing one church and two tenants cannot see or write each other’s updates or protected audience metadata.
- AC27-C03-02: A new assignee gains no earlier history from assignment alone; an independently history-authorized collaborator sees only the qualified earlier slice with the audience explanation.
- AC27-C03-03: Revocation before acceptance blocks the write; revocation after acceptance blocks subsequent unauthorized body/result disclosure while preserving accepted history. Both serialized orders are demonstrated.
- AC27-C03-04: Users can identify the exact partnership and permitted audience before saving; a changed or unavailable audience does not silently broaden the destination or disclose restricted names.
- AC27-C04-01: Staff and missionary projections resolve the same permitted source update; two accounts of one meeting preserve distinct authorship and a single encounter, without copied editable transcripts.
- AC27-C04-02: Caller-forged tenant/actor/author and cross-tenant/context/source references are rejected without mutation or protected diagnostic disclosure.
- AC27-C04-03: Actual database roles cannot retarget origin or author through direct mutation, view, RPC or service paths; authorized source correction remains attributable, and destructive relationship removal does not erase history.
- AC27-C04-04: Concurrent edit or policy narrowing is revalidated at acceptance; stale intent cannot overwrite accepted history or publish beyond current authority.
- AC27-C05-01: Failure of required audit/history/outcome persistence leaves no accepted update; an identical retry accepts one effect, and changed payload/context under that identity conflicts.
- AC27-C05-02: A lost reply recovers the accepted update once; a retry after read revocation returns no protected stored body or event-existence detail.
- AC27-C05-03: An accepted update with delayed projection remains truthfully accepted/pending, and replay repairs the permitted derivative once without another update, encounter, notification or permission grant.
- AC27-C05-04: Save-versus-revoke/narrow and competing edits are exercised in both applicable orders using actual source/database boundaries.
- AC27-C06-01: An ordinary eligible update takes one deliberate save; the user can identify audience and correction/follow-through options without another form or approval.
- AC27-C06-02: A draft for A cannot submit into B after a record/role switch; a late A response cannot erase B’s draft or text entered after A’s submission.
- AC27-C06-03: Slow/lost saves, session loss and unavailable audience/history show distinct states and permit safe recovery without false Saved feedback or duplicate content.
- AC27-C06-04: No automatic read receipt, all-collaborator broadcast, pressure to acknowledge, offline publication or newly exposed donor/public field follows from saving.
Primary source cases are reproduced below so the implementer can distinguish one slice's first-demonstration ownership from later composed regression proof. An AC whose multi-step scenario crosses a later producer must have that producer as a genuine blocker or its primary acceptance transferred explicitly; a mocked internal owner does not satisfy it.
AC27-C01-01
A missionary can decline to write and continue ordinary permitted use without penalty, required stage/task, activity report or coaching enrollment; staff cannot configure such coercion through this lane.
AC27-C01-02
Saving or reading an update leaves managed-work admission, pause/closure, source obligations and staff status unchanged; a gift handoff cannot be completed by a note.
AC27-C01-03
An independently selected personal follow-up is created once through its source. Its failure or cancellation neither fails nor duplicates an already accepted update.
AC27-C02-01
Existing private/imported/internal/care/location-sensitive/financial fixtures remain protected after activation, import, refresh and shared-history migration.
AC27-C02-02
The private destination discloses its actual reader scope, and its absence leaves a clear unavailable/cancel option with no shared save.
AC27-C02-03
A newly authored eligible update can be saved directly in its qualified shared context without a repeated sharing checkbox or supervisory approval.
AC27-C03-01
Two ministries sharing one church and two tenants cannot see or write each other’s updates or protected audience metadata.
AC27-C03-02
A new assignee gains no earlier history from assignment alone; an independently history-authorized collaborator sees only the qualified earlier slice with the audience explanation.
AC27-C03-03
Revocation before acceptance blocks the write; revocation after acceptance blocks subsequent unauthorized body/result disclosure while preserving accepted history. Both serialized orders are demonstrated.
AC27-C03-04
Users can identify the exact partnership and permitted audience before saving; a changed or unavailable audience does not silently broaden the destination or disclose restricted names.
AC27-C04-01
Staff and missionary projections resolve the same permitted source update; two accounts of one meeting preserve distinct authorship and a single encounter, without copied editable transcripts.
AC27-C04-02
Caller-forged tenant/actor/author and cross-tenant/context/source references are rejected without mutation or protected diagnostic disclosure.
AC27-C04-03
Actual database roles cannot retarget origin or author through direct mutation, view, RPC or service paths; authorized source correction remains attributable, and destructive relationship removal does not erase history.
AC27-C04-04
Concurrent edit or policy narrowing is revalidated at acceptance; stale intent cannot overwrite accepted history or publish beyond current authority.
AC27-C05-01
Failure of required audit/history/outcome persistence leaves no accepted update; an identical retry accepts one effect, and changed payload/context under that identity conflicts.
AC27-C05-02
A lost reply recovers the accepted update once; a retry after read revocation returns no protected stored body or event-existence detail.
AC27-C05-03
An accepted update with delayed projection remains truthfully accepted/pending, and replay repairs the permitted derivative once without another update, encounter, notification or permission grant.
AC27-C05-04
Save-versus-revoke/narrow and competing edits are exercised in both applicable orders using actual source/database boundaries.
AC27-C06-01
An ordinary eligible update takes one deliberate save; the user can identify audience and correction/follow-through options without another form or approval.
AC27-C06-02
A draft for A cannot submit into B after a record/role switch; a late A response cannot erase B’s draft or text entered after A’s submission.
AC27-C06-03
Slow/lost saves, session loss and unavailable audience/history show distinct states and permit safe recovery without false Saved feedback or duplicate content.
AC27-C06-04
No automatic read receipt, all-collaborator broadcast, pressure to acknowledge, offline publication or newly exposed donor/public field follows from saving.
AC27-Q07-02
- GIVEN a missionary adds an eligible voluntary update without an active staff enrollment.
- WHEN the update is accepted.
- THEN no enrollment, compulsory task, quota, coaching obligation or performance penalty is created.
Testing and verification
Use the confirmed authenticated Mission Control → real owner command → disposable Supabase/Postgres → fresh read/reload seam, with real separate staff/missionary/finance identities in two tenants and no demo auth, fake internal store, missing-prerequisite skip or production credentials. Supplement with actual-role SQL/RPC denial and controlled separate-session races over the same backend; mock only external I/O. Required fixture setup must fail preflight rather than skip. Demonstrate one-save authored entry, new collaborator without earlier grant, two ministries sharing one church, cross-tenant forged keys, readonly recovery, audit failure, same-key changed text, save/revoke both orders and late A response while editing B. Exercise real missionary projection with actual missionary actor, not a staff impersonation. Include keyboard/focus/mobile ordinary and error flow.
Verified existing commands (future implementation checks, not a claim that this new behavior is already tested):
bun run test:unitbun run verify:data-boundarybun run lintbun run typecheckbun run ci:preflightbun run test:e2e:strictbun run test:a11y:strict
Verified current origin/develop script names; these are required implementation verification commands, not passes from this documentation task. Strict browser scripts must use the qualified real-auth fixture; existing default demo bypass is not sufficient.
Completion evidence must link the implementation commit and exact focused test commands/results to the listed story/REQ/AC IDs; show before/after user or source outcomes, real-role denial evidence and each relevant race/recovery result. Extend existing stable seams using red-green-refactor; no production test endpoint, alternate task engine or copied in-memory owner substitutes.
Operational controls
Implement the existing signal, threshold, owner and response for MON27-121, MON27-123, MON27-124. Read the exact rows in the commit-pinned operations.md/monitors.json; consolidate identical signals without losing their meanings. Q02 exercises deployment ownership/runbooks after the producer is delivered.
Blocked by
- AL-1998: [Phase 9] Manage one stable Ministry Assignment and its separate participant memberships (P27-S01) #1998: Canonical tenant/Party/identified MinistryAssignment and qualified P9 context source identity; no admission into managed cultivation is required to write a voluntary eligible update Why: Updates must attach to the same exact partnership without enrolling managed work merely to write.
Non-issue prerequisites
-
Integrate docs(AL-1992): publish Phase 27 specification and implementation handoff #1993 governing specification into the implementation target before dispatch. This PR is currently unmerged; do not mark this ticket ready because its body is complete.
-
id: SPEC-INTEGRATION; type: non-issue; contract: Governing Phase27 specification commit b5a65b3 must be integrated into the implementation target, or an explicitly approved integration branch must contain that exact revision. Push/PR creation does not itself prove merge or runtime readiness; parent AL-1992 — Phase 27 Donor Development & Portfolio Management specification #1992 is unchanged. Concrete breakdown approval remains required before implementation-issue publication.; source_revision: b5a65b3; source_url: https://github.com/Asymmetric-al/core/tree/b5a65b3f2da880fcbfa4b3cae189d216d06542be/docs/prds/sitestacker-parity/phase-27-donor-development
-
id: QUAL-C-CURRENT-AUTHORITY; type: non-issue-source-component-qualification; contract: Qualify only the actual consumed components: [Phase 12] Capability spine core: branded Capability + runtime-verifiable EffectiveAccess + one resolver (PDP) + retire the incumbent + names-never-authorize gates + DRT reference model + D22 non-prod isolation #667 current capability resolver, [Phase 12] Tenant-axis substrate: tenant-in-the-brand + unified RLS source + delete OR is_super_admin() + positive RLS-isolation gate + kill defaults + cross-tenant-edge invariants #668 tenant context, [Phase 12] Principal foundation: four principal classes + active-assignment primitive + fire-time re-resolution + profiles.role inert-for-authz #669 active human/NHI principal and assignment context, [Phase 12] Revocation + grant-state: single monotonic tenant epoch + governance_version cache + assignment-bound grant contract + advisory-locked grant-state function + last-admin/last-clearance guards #670 current revocation/grant state, [Phase 12] Module / entity / field permissions (CRUD, per-field on the Phase-11 catalog, edit-implies-view lattice, manager-rollup opt-in + fail-closed cycle, draft/unpublished, self-scope, merge/dedupe re-derivation) #675 source/entity/field action and disclosure rules, [Phase 12] Metering choke + set-based floor predicate + tiered audit-before-serve + transactional outbox + hash-chained read-audit + the R7 uniformity oracle #677 required audit, and [Phase 12] Purpose/consent axis (resolve with purpose bound to surface + consent AND-floor + contactability) + residency axis (region veto + placement build-invariant + no-default-for-restricted) #680 exact source-processing purpose. Reads, writes, metadata/counts, cache/export and accepted-result recovery must recheck current actor/tenant/source/field/history authority. Component evidence, not whole-issue or whole-Phase12 closure, is required; no role-label/mock substitute or duplicate authorization foundation.; source_issues: [667, 668, 669, 670, 675, 677, 680]; source_refs: ['docs/prds/sitestacker-parity/phase-12-full-role-permission-configuration.md', 'docs/prds/sitestacker-parity/phase-27-donor-development/proof.md']
-
id: QUAL-C-REAL-SLICE-PROOF; type: non-issue-release-qualification; contract: Exercise the reached behavior with real authenticated staff/missionary/finance identities against disposable Supabase, actual-role SQL/RPC denial and separate-session concurrency, real source commands/results and browser/accessibility checks. Missing setup fails; demo bypass, memory stores, skipped tests and document validators do not prove runtime acceptance. Source-specific measured operating budgets and operator assignments must qualify before activation.
-
id: QUAL-C01-SHARED-PROJECTION; type: non-issue-source-component-qualification; contract: Before exposing the new narrow missionary shared-history projection, qualify the exact eligible source fields/create/read/history/correction/restriction audience under the existing P9 [Phase 9] Build the activity timeline: crm_activity_events + composed UNION read #617 source and [Phase 9] Enforce the permission/projection chokepoint + cross-surface leak negative tests #622 projection chokepoint, current P3 [Phase 3] Projection resolver (fail-closed, subtract-only) + promote portal SELECTs + parity guard #493 resolver and [Phase 3] Change-control engine: field_policy_change_requests + widen() + surface lattice + baseline guard #498 ADR0211 widening engine. New projection-policy widening requires its distinct-human source control; ordinary eligible saves do not. Reconcile the older [Phase 9] Build the activity timeline: crm_activity_events + composed UNION read #617 demo-deletion and [Phase 9] Enforce the permission/projection chokepoint + cross-surface leak negative tests #622 blanket activity-exclusion wording with D26 owner amendments; never backfill private content. Full unrelated timeline/projection/change-engine issue closure is not a direct blocker.; source_issues: [617, 622, 493, 498]; source_refs: ['docs/prds/sitestacker-parity/phase-09-full-crm-depth-relationship-graph.md', 'docs/prds/sitestacker-parity/phase-03-minimum-permission-role-scoped-projection-foundation.md', 'docs/adr/0211-distinct-human-approval-for-projection-widening.md']
-
Recheck current source component revisions and qualification at implementation start. Existing issue closure alone is not a runtime certificate. Missing qualified inputs gate only the consumed lane, never authorize a substitute source or broader permissions.
Implementation constraints
- Use canonical packages/api business commands, thin app routes, approved packages/database collections/hooks and existing native Postgres truth; Twenty is retired.
- Preserve exact shared Base UI/base-maia, Zinc semantic tokens and current Bun/Turborepo/Next.js/TanStack architecture. This is not permission to create a component or data framework.
- Apply current Q01–Q06/Q09–Q11/Q13/Q14 to every reached path in this behavior; source writes, permissions, failure recovery and accessible UI are part of this slice, not deferred to a later general safety ticket.
- Follow substantive-work TDD under docs/ai/skills/tdd/SKILL.md, Vitest instructions, current scoped AGENTS, database migration/RLS workflow and docs/ai/rules/testing.md. Existing mock/demo tests are prior art, not production-shaped acceptance.
- No donor/public checkout field, default highest-giver order, mandatory missionary reporting or claim of access from assignment, church membership, source ID or appeal link.
- Completing this ticket does not authorize rollout: specification integration and exact consumed owner, cohort, measured envelope and composed pilot gates remain explicit.
- No compulsory save-time sharing checkbox or supervisor approval after source/policy qualification. No write/read implies contact, staff status, permission, stage, enrollment or completed source obligation.
- The source allows no ordinary retargeting of original tenant/context/encounter/author even if both old and new contexts are writable; controlled source correction is distinct.
Read AGENTS.md, the nearest scoped instructions, testing rules, document authority and OpenSpec rules. The explicitly invoked to-tickets skill governs this ticket; do not invoke implementation as part of ticket publication. For implementation, inspect current manifests and installed framework guidance rather than freezing this baseline's versions into new code.
Fresh-context scope: focused_spec_words: 1317; estimated_total_read_tokens: 9000; basis: Planning estimate, not a measured model limit: focused normative clauses plus source-owner excerpts, current modules, tests and commands. The ticket does not require loading the entire phase.. Read focused source and tests for this behavior; retained historical review records are provenance, not mandatory bulk context. If a consumed source is missing, report its exact contract and keep the affected behavior gated; do not quietly expand the ticket to implement a whole earlier phase.
Delivery boundary
This ticket proves its behavior and mapped acceptance on its qualified inputs. It does not close the parent, mark unrelated requirements complete, satisfy another owner's unresolved decision, activate an unqualified cohort, or authorize merging the documentation PR. E7/C-01 remain explicit where reached. All retained implementation task boxes stay unchecked during planning.
Activity
[-]P27-C01: Share a new eligible partnership update with its exact audience[/-][+]AL-2001: Share a new eligible partnership update with its exact audience (P27-C01)[/+]