Skip to main content

RFC Review: Mention User in CDP Notes

Companion review for rfc-notes-mention-user.md, produced by the rfc-reviewer skill. Lives beside the RFC; valid only for the RFC revision in reviewed_rfc_last_updated (2026-06-22).

Cycle R3 (delta re-review). The RFC's last_updated advanced 2026-06-18 → 2026-06-22 (date-based staleness now works — REV-12 fixed). This cycle reviews the 2026-06-22 re-verification, which brought four previously-absent repos into the workspace (notification-service, qontak-unified-component, hub/hub-chat, qontak-launchpad, plus mobile-qontak-chat) and closed the two external notification blockers that held R2 at HOLD.

Executive Summary

  • Overall Score: 8.5/10 (was 8.0 at R2 — +0.5)
  • Rating: Agentic-Ready
  • RFC Type: full-stack
  • Sub-Type: frontend:new-feature · backend:new-feature · mobile:enhancement
  • Assessment Confidence: High (all delivery layers now in-workspace and code-verified, including the notification service, web unified center, Launchpad, and both mobile apps — the R1/R2 Low-confidence external surface is gone)
  • Applied Caps/Gates: ACV lifted 6.5 → 8.5 (outbound Notification contract now VERIFIED against notification-service — the cap that held R2 below Agentic-Ready is released). No category < 5.0. 0 unresolved cross-layer contract mismatches. ROL not capped (BE-first deploy order specified). 8.5 (not higher) because one external design asset (OQ-14 Figma) still gates the single web-composer chunk, plus minor residuals (no FE e2e, no CWV/browser matrix, OQ-17 deep-link wiring).
  • Implementation Readiness Verdict: PROCEED — the notification contract is verified (POST /crm; payload pinned), organization_id is dissolved, 0 dangling decisions. 9 of 10 execution chunks are unblocked today; only chunk 7 (web composer) waits on the OQ-14 Figma asset, which is a missing design deliverable, not a spec ambiguity an agent would need to ask about.
  • Report Path: cdp/notes-mention-user/rfcs/rfc-notes-mention-user-review.md
  • RFC Author: CDP Squad | Reviewed: 2026-06-22 (cycle R3)

This R3 delta-review confirms the AUGFLOW-012 re-verification did the one thing R1/R2 could not: it grounded the external notification contract against real code. The dispatch endpoint is corrected to POST /api/v1/notifications/crm (the /chat endpoint rejects non-inbox types — verified notification_handler.go:225,313), the payload is pinned to verified fields (title now correctly required; skip_fcm removed because it does not exist; FCM fan-out is automatic for crm origin), and OQ-8 is dissolvedorganization_id is optional *uuid.UUID metadata and company_sso_id is itself a UUID, so no mapping is needed. The web rendering path is now fully grounded (Decision 13): the unified NotificationCenter in qontak-unified-component is hosted in hub/hub-chat, already renders the mention category, and qontak-customer-fe embeds via module federation. Mobile scope is resolved (OQ-18): mobile-qontak-crm only; mobile-qontak-chat has no CDP notes and is excluded. The remaining gate is OQ-14 (web Figma) — external and Design-owned — which blocks only the web-composer chunk. Net: the feature moves from HOLD to PROCEED, with one design-asset carve-out on a single chunk.


Quick Verdict

Why this RFC can be implemented agentically:

  • The outbound notification contract — the single drag that held ACV at 6.5 across R1/R2 — is verified, not assumed. §2.4 + Decision 10 now pin endpoint (/crm), auth (X-Api-Key), required fields (sso_id/title/description), taxonomy (notif_type="1", notif_category="2"), and the optional organization_id. An agent can write chunk 5's request body without guessing.
  • organization_id (OQ-8) is no longer a question: optional metadata, company_sso_id is a UUID. Decision 10's interface is complete.
  • 0 dangling decisions; Decision 12 has a default (Tiptap fallback) and Tiptap is now inventoried with a bundle budget (REV-11 fixed). Decision 13 grounds the web/mobile delivery surfaces.

Why this RFC will still cause agent guessing or rework:

  • Chunk 7 (web composer) is still gated on Figma (OQ-14 / REV-4). No design exists for the typeahead picker + chip; an agent cannot build pixel-correct UI from nothing. This is a missing deliverable, not an answerable clarification.
  • Web deep-link wiring (OQ-17 / REV-13, new): the unified-center click lands on /customers/{id}?tab=notes, but CustomerActivityV2.vue does not parse ?tab=notes and the Notes tab is gated behind isStaging. Without chunk 10, the web tap-through resolves to the customer page but not the Notes tab. Small, internal, scoped.

Findings Ledger (carry-forward)

Stable, never-renumbered ids. R1 minted REV-1…REV-10; R2 added REV-11/REV-12; R3 reconciles all and mints REV-13/REV-14. "Was this captured before?" = the id exists; "did we improve?" = open → fixed below.

IDSeverityFinding (one line)RFC locationStatusFirst seenResolved inEvidence / fix
REV-1blockerNotification Service contract UNVERIFIED — notif_type/notif_category/event_type + web parity not confirmed; dispatch payload cannot be finalized§2.4 outbound dep call; Decision 10; OQ-1fixedR1R3notification-service now in workspace. Verified: endpoint POST /api/v1/notifications/crm (route.go:57-58; /chat rejects non-type-3, notification_handler.go:225,313); payload sso_id/title/description required, notif_type="1"/notif_category="2" (notification_handler.go:67-83, notification_attributes.go:122-141); web center renders mention (typeAndCategory.ts:40-43). OQ-1 narrowed to a non-blocking product choice (reuse vs new category).
REV-2blockerorganization_id mapping unresolved — company_sso_id (string) → company UUID undefined§1 Assumptions #4; §2.4; OQ-8fixed (dissolved)R1R3Verified organization_id is optional *uuid.UUID metadata, not the recipient (notification_handler.go:67-83); company_sso_id is itself a UUID (Launchpad models.go:15-27). No mapping or extra lookup needed. OQ-8 resolved.
REV-3major→minorWeb editor mention-trigger feasibility — Decision 12 was danglingDecision 12; §4.D ch.7; OQ-7fixed (decision) / spike-half openR1R2Decision 12 Partial with pre-committed Tiptap fallback; the spike (pixel3-native vs Tiptap) is an optimization, not a blocker. Unchanged at R3.
REV-4blockerWeb Figma frames for typeahead picker + chip do not exist§1 Design References; OQ-14openR1OQ-14 still Open; Design-owned, external to code repos. Gates only §4.D ch.7. Now the sole remaining blocker.
REV-5majorCompany-active-user validation method on IUserService unverified§2.0 contracts; §4.D ch.4a; OQ-13fixedR1R2GetActiveUsersBySsoIds wrapper spec'd; chunk 4a. R3 note: Launchpad now in workspace confirms the underlying GET /private/users?statuses[]=active&sso_ids[]=&company_sso_id= primitive (user_handler.go:459).
REV-6majorOQ-9 (private-note + mention) left TBD§3.A.1; §3.D; OQ-9fixedR1R2No privacy model exists; notify normally. Unchanged.
REV-7majorIdempotency key included note_updated_at → dedup broke under concurrent PUTs§2.D; §2.E; §2.FfixedR1R2Key note_id + recipient_sso_id, timestamp-free + mandatory. Unchanged.
REV-8minorIdempotency key "recommended" not mandated; DLQ retention unstated§2.D; §2.FfixedR1R2Mandated; retry/DLQ grounded to worker_service.go/backoff.go. Unchanged.
REV-9minorWeb FE on deprecated note path; new fields must appear on both route groups§2.4 note; OQ-15fixedR1R2Both groups share one handler. Unchanged.
REV-10minormention_count semantics unpinned; FE i18n keys TBD§3 Monitoring; §3.C; OQ-16fixedR1R2mention_count = newly-added. Unchanged.
REV-11minorTiptap fallback dependency not inventoried; no bundle budgetDecision 12; §1 Dependencies; §2.J; §3 PerffixedR2R3Verified present: @tiptap/vue-3 + @tiptap/extension-mention row in §1 Dependencies (l.205); bundle budget (~50–80KB gzipped, lazy-load) in §3 Performance.
REV-12minorlast_updated not bumped despite material commits — staleness undetectable by dateFrontmatter / Metadata tablefixedR2R3last_updated bumped to 2026-06-22; Metadata row annotated with the re-verification. Date-based staleness now works.
REV-13minorWeb deep-link does not land on the Notes tab — ?tab=notes not parsed + Notes tab gated behind isStagingDecision 13; §2.G; OQ-17; §4.D ch.10openR3New. Two small qontak-customer-fe changes (parse query param; un-gate tab). No change to qontak-unified-component or hosts. RFC documents it (OQ-17) and gives it chunk 10. Internal, scoped, non-blocking for BE/mobile.
REV-14lowAuto-FCM means contact name (description="On contact {Name}") surfaces on a mobile lock-screen push, no skip_fcm opt-out§3.D; Decision 9/10open (pending infosec ack)R3New. RFC §3.D now calls this out explicitly and offers a one-line mitigation (generic description). Legal basis unchanged (recipient is an authorized same-company viewer). Needs explicit infosec acceptance before AGREED — folds into the existing infosec gate, does not block build.

Ledger summary: 3 open (1 blocker REV-4 external; 1 minor REV-13 internal; 1 low REV-14 infosec-ack), 4 transitions to fixed this cycle (REV-1, REV-2, REV-11, REV-12). Counting by id across all cycles: of REV-1…REV-14 → 11 fixed (REV-1/2/3-decision/5/6/7/8/9/10/11/12), 3 open (REV-4 external blocker, REV-13 minor internal, REV-14 low). The only true blocker left is external/Design (REV-4 / OQ-14). Still-open findings are promoted to the RFC §5 Open-Questions table (OQ-14 carried; OQ-17 added this cycle; REV-14 captured in §3.D).


PRD → RFC Traceability Matrix

Forward — PRD element → RFC coverage (R3 deltas marked):

PRD ElementRFC SectionCoverage
S01 Mention teammate (Web) — AC-1/2/3, ERR-1§1.C, §2.A, §2.B, §2.4, §2.3, §4.D ch.4b/6/7/10Full (ch.7 UI still Figma-gated)
S02 Mention teammate (Mobile) — AC-1/2/3, ERR-1§1.C, Decision 8, §2.4 dual-parse, §4.D ch.5/8Full
S03 Mentioned user notified — AC-1/2/2b/3, ERR-1/2§2.2, §2.F, §3.A, Decision 4/9/10/13, §4.D ch.5/9Full (upgraded from Partial — the web Notification Center path and dispatch payload are now verified, OQ-1 closed)
S04 Only valid company users mentionable — AC-1, ERR-1/2§2.4 validation, Decision 5, §2.0 contract, §4.D ch.4a/4bFull
S05-NEG cross-company / non-user guard — NEG-1/2§4 flag gate, §2.4 company-scopeFull
PRD §6 Constraints (10k limit, SSO identity, server sanitize, DOMPurify, async, latency)§2.3, §3.B, Decisions 1/3/6, §2.A, Decision 2/§2.F, §3 PerfFull
PRD §6 Constraint: plan gating Growth+Enterprise§1 Assumptions #5Partial — assumption only; flag-gated (acceptable)
PRD §8 #1 typeahead list§2.4, §2.B, Decision 11Full (Launchpad ?query= search now verified, OQ-6 closed)
PRD §8 #3 dispatch payload§2.4 dependency-call table, Decision 10Full (upgraded from Partial — payload verified against notification-service)
PRD §8 #4 mobile tap-throughDecision 9, §2.F.1 step 6Full
PRD §10 Rollout (3 stages)§4 Rollout StrategyFull
PRD §11 Observability (5 events)§3 MonitoringFull
PRD §12 Success Metrics§1 Success CriteriaFull
PRD §13 Dependencies§1 Dependencies, §2.F.1Full (Tiptap inventoried — REV-11 fixed)
PRD §14 Decisions D-1…D-10§2 Decisions 1–13 + §1.B mappingFull
PRD §15 OQ-1…OQ-12§5 (carried + OQ-13…OQ-18)Full

Reverse — RFC artifact → PRD driver: unchanged; every §1.B/§2.I artifact traces to a PRD AC. The new artifacts this cycle (Decision 13 web/mobile scoping; chunk 10 deep-link wiring) trace to S03/AC-2/AC-2b. No scope creep.

Summary: 18 of 20 PRD elements Full (S03 + dispatch payload upgraded Partial→Full; §13 dependency gap closed), 2 Partial (plan-gating = flag-gated, acceptable; ch.7 UI Figma-gated under S01), 0 Missing. Bidirectional traceability remains exemplary.


Scorecard

Full-Stack Scorecard (18 categories)

#CategorySourceScoreEvidence-Based Rationale (Δ vs R2)
1PRT — PRD TraceabilityMerged9.0Unchanged. Forward+reverse matrices, composite AC ids, D-n→Decision-n map. S03 + payload upgraded Partial→Full; §13 gap closed (REV-11).
2TDC — Technical DecisionsMerged8.5+0.5. Decision 10 moved Partial→Resolved (payload verified, OQ-1/OQ-8 closed); Decision 13 added (web unified-center + MFE + mobile scoping, fully grounded). 13 ADR-format decisions; 0 dangling, 0 blocking-partial (Decision 12 spike-half is an optional optimization).
3CNT — Contract SpecificityFE7.5Unchanged. mention_count/i18n pinned; Tiptap inventoried. Still minus: picker props pending Figma (OQ-14) — the one FE contract not pinnable from code.
4SCB — Scope BoundariesFE8.5Unchanged. §2.I per-layer create/modify/NOT-touch; shared-module isolation; Decision 13 adds explicit mobile-qontak-chat exclusion + "no change to unified-component/hosts."
5DEP — DependenciesFE8.0+0.5. REV-11 fixed: Tiptap (@tiptap/*) now in §1 Dependency table + §2.J + §3 bundle budget. New downstream deps (notification-service, qontak-unified-component, Launchpad) all verified and tabled.
6NFS — Non-Functional SpecificityFE7.5+0.5. Bundle budget now present (~50–80KB gzipped, lazy-load, asserted in npm run build — REV-11). Still no CWV (LCP/INP/CLS) or browser matrix.
7TPS — Test Plan SpecificityFE7.5Unchanged. §4.C repo-sourced commands; §4.D chunks carry assertable AC (incl. new ch.10). Still no FE e2e (follow-up).
8DMS — Data Model & SchemaBE8.5Unchanged. Struct+bson delta, example doc, cardinality, PII, retention, optional index + .down.json.
9ACV — API Contract & VersioningBE8.5+2.0 — the headline. The outbound Notification contract, UNVERIFIED across R1/R2, is now verified against notification-service: endpoint /crm, auth, required/optional fields, taxonomy, organization_id semantics (§2.4, Decision 10; anchors route.go:57-58, notification_handler.go:67-83/225/313). Internal endpoints already complete; the persistent ACV drag is released.
10DIC — Data Integrity & ConsistencyBE8.5Unchanged. Timestamp-free mandatory idempotency key (REV-7); atomic single-doc writes; diff-notify.
11FMC — Failure Mode CoverageMerged8.5Unchanged. FE UI-state matrix + error catalogs; BE timeout/retry×3/DLQ/poison; cross-layer error shapes match. R3 adds the auto-FCM delivery path (no new failure mode — fan-out is platform-owned).
12CSS — Concurrency & ScalingBE7.5Unchanged. Collision map; timestamp-free key dedups across concurrent PUTs. Last-write-wins on body is an inherited limitation; no endpoint rate limit named.
13SAS — Security & AuthorizationBE8.5Unchanged. bluemonday allow-list w/ href-scheme rejection; UUID validation; company-scoped tenancy (verified); SSRF note (server-constructed click_action_url); Vault secrets; gosec. R3 adds §2.G rows confirming only server-constructed strings reach the notification body and the deep-link host is trusted.
14ROL — Rollout & RollbackMerged8.5Unchanged. BE-first order + cross-layer matrix; single logical flag; quantified stop conditions; deploy-order-aware rollback; additive field.
15OBS — ObservabilityMerged8.5Unchanged. 5 named events; mention_count pinned; OTel/Datadog into worker; SLO 99%; DLQ surfaced as metric. Minus: no trace into the external service (out of CDP control).
16SBC — Service Boundary & CouplingBE8.5Unchanged. §2.F.1 responsibility matrix; new NotificationClient mirrors verified heimdall pattern; async via IJobEnqueuer. Decision 13 cleanly bounds web rendering to the platform component (no CDP-local UI).
17CPA — Pattern AlignmentMerged8.5Unchanged. §2.0 Patterns cite exact in-repo files; MFE embed + unified-center import grounded to real anchors (TheNavbar.vue:79, hub-chat/nuxt.config.ts:526).
18CDG — Compliance & Data GovernanceBE8.0+0.5. §3.D now fully characterizes the egress, including the newly-verified auto-FCM lock-screen exposure of the contact name (REV-14) with a concrete mitigation; legal basis intact (recipient is an authorized same-company viewer). Infosec gate explicit. Residual: egressed body not recallable (D-6, accepted).

Resource & Cost Advisory (non-blocking)

  • §4.F: negligible compute/DB delta; ≤10 external calls/note + one batched company-user lookup; no new infra. Bundle delta for the Tiptap fallback is now budgeted (REV-11). Advisory only — no score/verdict impact.

Decision Closure Assessment

Decision Index

#DecisionStatusCritical Gaps
1Storage as mentioned_user_ids []string on Mongo docResolvednone
2Async dispatch via gocraft/work workerResolvednone
3Server-side bluemonday sanitizationResolvedneeds infosec sign-off (gate)
4Notify only newly-added mentions (diff)Resolvednone
5Invalid mention → drop-and-warnResolvednone
6Max 10 mentions/noteResolvedtunable const
7Mobile plain-text fallbackResolvednone
8Mobile emits sso_id; BE dual-parsesResolvednone
9Mobile tap-through via external_url (mobile-qontak-crm only)Resolvedconfirm host/URL w/ Mobile (operational)
10Reuse notification-service via new client — POST /crmResolved (was Partial)payload verified; OQ-1 narrowed to a non-blocking product choice (reuse vs new category)
11Typeahead = existing list, client-filteredResolvedLaunchpad ?query= now verified available (OQ-6)
12Web @-trigger in MpRichTextEditorPartialfallback (Tiptap) chosen + inventoried; spike only picks A vs C; Figma (OQ-14) gates the chunk
13Web rendering via unified center + MFE; mobile scoped to mobile-qontak-crmResolved (new)OQ-17 deep-link wiring (small, chunk 10)

Aggregate: 12 of 13 Resolved, 1 Partial (Decision 12 — editor-path optimization, not a blocker). R2 had 10 Resolved / 2 Partial / 0 Dangling. Decision 10 promoted Partial→Resolved; Decision 13 added Resolved. 0 dangling, 0 blocking-partial decisions remain.


Decision: 10 — Reuse notification-service via a new outbound client (POST /crm)

Status: Resolved (was Partial at R1/R2 — the headline R3 improvement)

What was decided

Option B — new NotificationClient mirroring QontakLaunchpadClient (heimdall), targeting POST /api/v1/notifications/crm with X-Api-Key. Transport/auth/timeout/logging decided and grounded (qontak_launchpad.go:38).

Interface specification — now complete

§2.4 + Decision 10 pin every field against verified code: sso_id (required, handler-enforced notification_handler.go:296-301), title/description (required), notif_type="1", notif_category="2", click_action/click_action_url, organization_id (optional *uuid.UUID). The R1/R2 TBD tags are gone. skip_fcm correctly removed (does not exist; FCM fan-out is automatic for crm origin per service.go:47-49).

Failure handling

Fully specified (§2.F): 10s timeout, retry×3 (1,4,16,64s), log on final failure, note unaffected, mandatory timestamp-free idempotency key.

Challenge results

  • Scale: ≤10 calls/note, async — holds at 10x.
  • Reversibility: remove client; job no-ops behind flag. Low cost.
  • Consistency: consistent with Decisions 2/9/13; the verified auto-FCM path means Decision 9's mobile delivery is reached without a flag.
  • Agent implementability: now yes for the full body — an agent can construct the /crm request from the RFC alone.

Gaps and suggestions

Missing: none blocking. Product choice (non-blocking): reuse the generic mention(2) category or add a CDP-specific category in notification-service for analytics/filtering (OQ-1, narrowed). Open questions: OQ-1 (narrowed, non-blocking).


Decision: 13 — Web rendering via unified Notification Center + MFE; mobile scoped to mobile-qontak-crm

Status: Resolved (new this cycle)

What was decided

Web mention notifications render in the unified NotificationCenter (qontak-unified-component) hosted in hub/hub-chat; qontak-customer-fe embeds via module federation (RemoteContactRouter) at /customers/**; mobile delivery is mobile-qontak-crm only (mobile-qontak-chat excluded).

Grounding in existing code

NotificationCenter.vue + mention category (typeAndCategory.ts:40-43); import hub-chat/.../TheNavbar.vue:79; CRM-origin click routing useNotificationCenter.ts:35-44; MFE expose qontak-customer-fe/nuxt.config.ts:92-111 + consume hub-chat/nuxt.config.ts:526, pages/customers/[...all].vue:69; mobile ownership crm_note/.../note_screen.dart, mention_toolbar_botton.dart; mobile-qontak-chat exclusion app.dart:85-113, contact_detail_screen.dart.

Interface specification

No change to qontak-unified-component or the hosts. The CDP-side contract is the notification payload (Decision 10) + the click_action_url deep-link.

Failure handling / challenge results

  • Agent implementability: yes for the BE emit + mobile delivery; the web tap-through needs chunk 10 (OQ-17) to land on the Notes tab.
  • Reversibility: additive; only the deep-link precision degrades if OQ-17 is left open.

Gaps and suggestions

Missing: OQ-17 wiring — parse ?tab=notes, un-gate the Notes tab in CustomerActivityV2.vue (chunk 10, small). Open questions: OQ-17 (REV-13).

Decisions 1–9, 11 remain Resolved (abbreviated per the ≥7.0 optimization). Decision 12 unchanged (Partial; editor-path optimization, Figma gates the chunk).


UI State Audit

ComponentLoadingEmptyErrorPartialSuccessAssessment
Web typeaheaddefineddefineddefinedn/adefined4/4
Web mention chip (render)n/adefineddefined (warn chip)defined (mix)defined4/4
Mobile pickerdefineddefineddefinedn/adefined4/4
Mobile note rendern/adefineddefined (never raw HTML)n/adefined3/3

Summary: All surfaces fully defined (§2.C). Unchanged from R2.


Performance Budget Check

MetricTargetSourceAssessment
Typeahead P95≤500ms§3adequate (escalation trigger defined)
Note write P95≤2sPRD §6adequate
Bundle size delta (Tiptap fallback)~50–80KB gzipped, lazy-load§3 (REV-11)now specified (was missing at R1/R2)
LCP / INP / CLSstill missing (minor)

REV-11 closes the bundle gap. CWV still absent — minor, not a blocker.


Accessibility Review

AspectSpecified?Assessment
Keyboard navigationyes (arrow/enter/escape, §3.E)adequate
Focus managementpartial (aria-activedescendant; return-focus on picker close not described)incomplete (minor)
ARIA labelsyes (role=listbox/option; chip role=link)adequate
Color contrastyes (≥4.5:1 token-driven)adequate
Motion sensitivitynominor
Screen readerpartial (listbox semantics; chip announces @Name)adequate

Unchanged from R1/R2.


Cross-Layer Contract Verification

Endpoint / ContractBE / sourceFE / Mobile expectedMatch?Gaps
POST/PUT …/notesnew fields, both route groups via shared handlerFE maps snake→camelYesclosing chunk §4.D ch.6
Company user list[{ sso_id, full_name?, status }] (Launchpad — avatar absent)picker needs sso_id + null-safe name + initials fallbackYesavatar-absence handled (Decision 11 note)
Mention anchor (web/mobile)data-user-id / href sso_idcomposer/mapper emit; DOMPurify ADD_ATTRPartialclosing chunks ch.6/8
Notification body (title/description)server-constructed strings only (never note HTML)unified center + One Notif V2 render as textYes (low risk)note HTML sanitized server-side; not placed in body
Web deep-link click_action_url/customers/{id}?tab=notes (trusted host)unified center → MFE → notes tabPartial (OQ-17)host/MFE must parse ?tab=notes + un-gate tab (ch.10)
Notification dispatch (outbound)POST /crm verified payloadn/a (platform)Yes (was the R1/R2 gap)OQ-1 product choice only

Mismatches found: 0 unresolved. The outbound-contract gap that was the standing ACV drag is closed; two new rows (notification body, deep-link) are added — the deep-link is the one Partial, with chunk 10 as its closer.


Cross-Layer Rollout Compatibility Matrix

ScenarioFEBEWorks?Notes
Pre-deployOldOldYesbaseline
Backend firstOldNew (flag OFF/dark)YesBE ignores anchors; flag OFF → no dispatch
Frontend firstNewOldNoavoided by chosen BE-first order
Both deployedNewNewYestarget
Backend rollbackNewOldNoroll back FE first or flag OFF; additive field data-safe
Frontend rollbackOldNewYesBE still parses stored anchors; chips degrade

Deploy order: Backend first. ROL not capped. Unchanged.


End-to-End Data Flow

Flow: Web — create note with a new mention (R3, grounded)

Agent picks @user (FE: UserStore.getUsers, client-filtered; Launchpad ?query= available)
→ NoteInput.vue inserts <a data-user-id data-mention>; DOMPurify ADD_ATTR keeps it
→ POST/PUT /iag/v1/contacts/{id}/notes [both route groups → same handler, REV-9]
→ ContactNotesService: mention.Parser → IUserService.GetActiveUsersBySsoIds (company-scoped, active)
→ bluemonday sanitize → drop invalid/self
→ repo insert/$set MongoDB (note + mentioned_user_ids)
→ IJobEnqueuer.EnqueueJob per new mention; analytics cdp_note_mention_added (newly-added)
→ 200 { mentioned_user_ids, dropped_mentions? }
→ FE CustomerStore reads new fields → NotesList renders chip
→ (async) worker → POST /api/v1/notifications/crm [VERIFIED payload: sso_id,title,description,notif_type=1,notif_category=2,click_action_url]
key note_id+recipient_sso_id (REV-7)
→ web: unified NotificationCenter (hub/hub-chat) renders mention → click → click_action_url → MFE RemoteContactRouter → /customers/{id}?tab=notes [needs OQ-17 ch.10 to select Notes tab]
→ mobile: auto-FCM (crm origin) → mobile-qontak-crm One Notif V2 → external_url tap-through

Gaps in flow: the worker→notification step is now fully specified (was the R1/R2 TBD). The only residual is the web deep-link landing precisely on the Notes tab (OQ-17 / chunk 10). Every other hop is grounded.


Strengths

  • The external contract is grounded, not assumed (ACV 6.5→8.5). The AUGFLOW-012 re-verification opened notification-service and pinned the dispatch endpoint and payload against real code — including the non-obvious correction that a CDP mention must use /crm, not /chat (the latter rejects non-inbox types, notification_handler.go:225,313). This is the single change that lifts the feature to Agentic-Ready.
  • Two standing blockers dissolved by verification, not by assertion. OQ-8 (organization_id) was answered by reading both the notification request struct (optional *uuid.UUID) and the Launchpad company model (sso_id is a UUID) — so no mapping exists to build. OQ-1's taxonomy is confirmed and the web center already renders mention.
  • The whole delivery topology is now first-party verified. Decision 13 grounds the web unified-center + MFE embed and scopes mobile to the one app that can display a CDP note (mobile-qontak-crm), explicitly excluding mobile-qontak-chat. No "external/UNVERIFIED" hand-waving remains in the notification path.

Biggest Gaps

  • Web Figma frames still pending (OQ-14 → REV-4, §1 Design References). No design for the typeahead picker + chip; gates §4.D ch.7. External/Design-owned — the sole remaining blocker, and it blocks only one chunk.
  • Web deep-link does not yet land on the Notes tab (OQ-17 → REV-13, Decision 13 / §2.G). ?tab=notes is not parsed and the Notes tab is gated behind isStaging; without chunk 10 the unified-center click reaches the customer page but not the notes view. Small, internal, scoped.
  • Auto-FCM PII on lock-screen (REV-14, §3.D). Contact name surfaces on a mobile push with no skip_fcm opt-out. Documented with a one-line mitigation; needs explicit infosec acceptance (folds into the existing gate). Low.

Priority Actions

  1. OQ-14 (§1 Design References / §4.D ch.7) — Create the two Figma frames (picker + chip). Design-owned; the only remaining blocker. Unblocks the web composer chunk. (Independent of the now-resolved editor decision.)
  2. OQ-17 / REV-13 (§4.D ch.10) — Wire the web deep-link: parse ?tab=notes in CustomerActivityV2.vue and un-gate the Notes tab from isStaging. Small qontak-customer-fe change; lands the web tap-through.
  3. REV-14 (§3.D / infosec gate) — Get explicit infosec acceptance that the contact name may appear on a mobile lock-screen push (or adopt the generic-description mitigation). Required before AGREED, not before build.
  4. OQ-1 (non-blocking product choice) — Decide with Notification/Platform whether to reuse the generic mention(2) category or add a CDP-specific one for analytics/filtering. Reuse is sufficient for v1.

Implementation Readiness Checklist

Unblocked (agent can proceed)

All types: PRD↔RFC traceability ✓; 0 dangling decisions ✓; failure modes + error catalogs ✓; config contract ✓; pattern alignment ✓; rollout + rollback ✓; observability ✓; task decomposition with AC (10 chunks) ✓; vague words near-zero ✓.

Frontend: UI states ✓; a11y specified (focus-return minor) ✓; bundle budget now present ✓. Pending: picker prop types (Figma OQ-14); CWV/browser matrix (minor).

Full-stack: cross-layer contract verified (partials have closing chunks) ✓; BE-first deploy order ✓; rollout matrix ✓; e2e flow ✓; flag coordination ✓.

Backend: schema at struct/bson precision ✓; internal + outbound contracts now complete ✓; idempotency key fixed ✓; concurrency map ✓; security/tenancy ✓; additive migration ✓; service boundary ✓; compliance (infosec gate set; REV-14 to ack) ✓.

Blocked (must fix first)

  • OQ-14 — web Figma frames (REV-4, Design-owned) — gates only chunk 7

Internal follow-ups (do not block BE/mobile)

  • OQ-17 — web deep-link to Notes tab (REV-13) — chunk 10
  • REV-14 — infosec ack of lock-screen contact name (before AGREED)

Verdict: PROCEED — 9 of 10 chunks unblocked (BE 1–3, 4a, 4b, 5 full, FE ch.6, Mobile 8–9, + ch.10 deep-link); only ch.7 (web composer) waits on the OQ-14 Figma asset.


Task Manifest

OrderChunkFilesAcceptance CriteriaDependencies
1BE field + optional indexrepository/contact_notes/base.go; db/migrations/0NN_*.jsonstruct compiles; bson round-trips; index applies+rolls backNone — unblocked
2mention.Parser (dual-form)service/mention/parser.go(+_test)both forms → same []sso_id; malformed dropped; >10 errorsChunk 1 — unblocked
3bluemonday policyservice/mention/policy.go; go.modgolden test: allow-list survives, scripts/javascript:/data:/style strippedNone — unblocked
4aIUserService.GetActiveUsersBySsoIdsservice/launchpad/user_service.go (+interface)given sso_ids+company → active members onlyNone — unblocked
4bWire parse+validate+sanitize+persist; drop-and-warn; diffcontact_notes_service.go; payload/*valid stored + dropped_mentions; diff notifies new; self filteredChunks 2,3,4a — unblocked
5NotificationClient (POST /crm, payload finalized) + MentionNotifyJobapi/notification_client.go; service/.../mention_notify_job.gorequest sends verified fields w/ X-Api-Key; enqueue per new recipient; 5xx→retry×3→log; key dedupsChunk 4b — fully unblocked (was payload-blocked)
6FE DOMPurify ADD_ATTR + read new fieldsNoteInput.vue:460; NotesList.vue:104; CustomerStore.tsdata-* survive sanitize; warn chip showsChunk 1 — unblocked
7FE typeahead + chip (pixel3-native or Tiptap fallback)MentionPicker/MpAutocomplete or Tiptap; NoteInput.vue; chip CSSlists users; insert anchor; chip rendersblocked on OQ-14 (Figma)
8Mobile CDP mapper emits sso_idmention_toolbar_botton.dart l.398-402href = .../users/{ssoId}/edit_user; list shows @NameChunk 2 — unblocked
9Mobile external_url route verify (mobile-qontak-crm only)notification_item_v2_mixin.dart, bottom_navigation_screen_mixin.dart:253-308 (verify-only)origin=external_url opens click_action_url; mobile-qontak-chat not targetedunblocked
10FE web deep-link to Notes tab (OQ-17)qontak-customer-fe: CustomerActivityV2.vue parse ?tab=notes; un-gate tab/customers/{id}?tab=notes auto-selects Notes tabunblocked (internal)

Dangling Decisions Log

#DecisionLocationOwnerDeadline
None. Decision 10 moved Partial→Resolved (payload verified); Decision 13 added Resolved; Decision 12 remains Partial on an editor-path optimization (not dangling).

Open Questions

#QuestionCategorySeverity
1Web Figma frames for picker + chip (REV-4 / OQ-14)CNT/NFSBlocking (external/Design, 1 chunk)
2Web deep-link: parse ?tab=notes + un-gate Notes tab (REV-13 / OQ-17)TDC/CNTImportant (internal, small)
3Infosec ack: contact name on mobile lock-screen push, no opt-out (REV-14)CDGImportant (before AGREED)
4Reuse mention(2) category vs add CDP-specific category (OQ-1, narrowed)ACV/productNice-to-have (non-blocking)

Evidence Notes

  • §2.4 + Decision 10 + OQ-1/OQ-8 — re-verified against in-workspace notification-service: /crm endpoint, required/optional fields, taxonomy, optional organization_id. Closed REV-1; dissolved REV-2 → ACV 6.5→8.5, TDC +0.5, S03/payload Partial→Full.
  • Decision 13 + §2.G — web unified-center + MFE + mobile scoping grounded to qontak-unified-component/hub/hub-chat/qontak-customer-fe/mobile-qontak-*. New Resolved decision; surfaced REV-13 (deep-link wiring) and REV-14 (lock-screen PII).
  • §1 Dependencies l.205 + §3 Performance — Tiptap (@tiptap/*) inventoried with bundle budget → closed REV-11, DEP +0.5, NFS +0.5.
  • Frontmatter last_updated: 2026-06-22 — bumped; date-based staleness now works → closed REV-12.
  • §3.D — auto-FCM lock-screen egress characterized with mitigation → CDG +0.5; REV-14 logged for infosec ack.
  • Launchpad user_handler.go:459GET /private/users?query=&statuses[]=active confirms OQ-6; avatar absent from list response (noted in Decision 11).

Review History

CycleDateReviewed RFC revision (last_updated / commit)ScoreVerdictFindings open → fixedNotes
R12026-06-182026-06-18 / 2d43cb27.5HOLD10 open (4B/4M/2m), 0 fixedBaseline. External-dep blockers + one internal correctness fix (REV-7 key).
R22026-06-182026-06-18 (unchanged) / 5883ba88.0HOLD7 fixed (REV-3 decision-half/5/6/7/8/9/10), 4 open (REV-1/2/4 external + REV-11 new), 2 new minor (REV-11/12)All internal findings closed with grounded verifications; 0 dangling decisions. Held <8.5 by the unverified external Notification contract (ACV 6.5).
R32026-06-222026-06-22 / working tree (base 5f1d4c6)8.5PROCEED4 fixed (REV-1/2/11/12), 3 open (REV-4 external blocker; REV-13 minor internal; REV-14 low), 2 new (REV-13/14)AUGFLOW-012 re-verification brought notification-service/qontak-unified-component/hub/hub-chat/qontak-launchpad/mobile-qontak-chat into the workspace. Notification contract verified (/crm, payload pinned); OQ-8 dissolved; OQ-6 closed; Decision 10 Partial→Resolved; Decision 13 added; mobile scoped to mobile-qontak-crm. ACV 6.5→8.5 releases the cap. Only OQ-14 (Figma) blocks, and only chunk 7. Score +0.5; rating Strong→Agentic-Ready; verdict HOLD→PROCEED.