RFC Review: Centralized Web Session — Launchpad SDK Integration
Executive Summary
- Overall Score:
7.5/10 - Rating:
Strong - RFC Type:
frontend - Sub-Type:
new-feature - Assessment Confidence:
High - Applied Caps/Gates:
none triggered(no category < 5.0; FMC ≥ 4.0; CPA ≥ 4.0 — but see DEP blockers below, which gate execution rather than score) - Implementation Readiness Verdict:
HOLD — chunks 2–6 blocked on OQ-1 (SDK package), OQ-2 (live Session Manager), OQ-3 (canonical iframe path). Chunk 1 + chunk 3 (mocked SDK) executable now. - Report Path:
/Users/mekari/Documents/qontak-launchpad-fe/rfc-review-report.md - RFC Author:
Syafrizal Muhammad| Reviewed:2026-06-28
This RFC is among the most agent-ready FE documents reviewable: every code anchor was independently verified against the repo (useAuthCookies.ts:5-7,30, authStore.ts:30,139,142, authenticated.global.ts:80,109, ssoCallback.ts:6, nuxt.config.ts:89-95,131-149 — all accurate), scope is file-precise, the execution plan is ordered with per-chunk acceptance criteria, and patterns are mirrored to real files. The biggest strength is grounding + scope discipline; the biggest gap is that the three load-bearing dependencies (@mekari/sdk, the Session Manager iframe, the canonical /sm/* path) do not yet exist or are ambiguous, so an agent can implement only the feature flag (chunk 1) and mocked-SDK specs (chunk 3) before stalling. The one thing that must change before full agentic execution: resolve OQ-1/OQ-2/OQ-3 so chunks 2–6 can run and be verified end-to-end.
Quick Verdict
Why this RFC can be implemented agentically:
- File-by-file scope, verified anchors, and an ordered chunk plan with acceptance criteria (§2.0, §4.C) — an agent can start chunk 1 with zero clarification.
- Pattern alignment is explicit and honest (§2.0 "Patterns to Follow"; flags that no in-repo iframe/
postMessagepattern exists rather than fabricating one). - Failure branches enumerated (
logged_out/switch_user/server_down+mslifallback, §2.4/§2.9) with a state machine (§2.8).
Why this RFC will cause agent guessing or rework:
- The SDK contract is consumed but unavailable (OQ-1/OQ-2): an agent stubbing it must invent the
postMessagepayload shape and origin behavior. - Two unresolved values gate testable acceptance: refresh throttle window (OQ-6) and company-sync endpoint (OQ-4/ADR-5).
- Iframe path ambiguity (OQ-3) means CSP and
server_downtiming cannot be wired deterministically.
PRD → RFC Traceability Matrix
Standard format (PRD exists — Confluence PT/48962437340, not fetchable at review time; assessed via §1.4 self-declared coverage)
| PRD Element | RFC Section | Coverage |
|---|---|---|
| Overview / issue (stale session, no SSO-logout observation) | §1.1 | Full |
| SC1 — SDK reports status across products | §1.2, §2.4 | Full (FE reacts to 4 events) |
| SC2 — 2h idle expiry → logout | §1.2, §2.4 logged_out | Full |
| SC3 — activity refreshes session | §1.2, §4.C ch.5 | Partial — throttle interval TBD (OQ-6) |
| SC4 — 1 account → multiple sessions | §1.2, §1.6 | Full (correctly scoped n/a — BE-owned) |
| Out of scope | §1.3 | Full |
| Dependencies (SDK, Session Manager, Redis) | §2.2, §5 OQ-1/2 | Partial — declared as external, unconfirmed |
| How to use SDK / events | §2.4 | Full |
Local storage msli / _mekari_account | §2.6 | Full |
| FE Web Session Flow | §1.4 | Mapped n/a — Launchpad is OAuth2 (ADR-3) |
| OAuth2 code flow | §2.7 | Full |
| User logout / switch account | §2.7, §2.9 | Full |
current_company endpoint | ADR-5 / OQ-4 | Partial — reuse vs PRD endpoint unresolved |
| Database model (no change) | §2.3 | Full |
| HA & Security | §3 | Full (FE-side) |
| Rollout plan | §4 | Full (step 4; step 5 deferred) |
Summary: ~14 of 16 PRD elements fully covered, 3 partial (SC3 throttle, dependency confirmation, current_company), 0 silently missing. No RFC decision lacks a PRD driver (no scope creep). PRD body not independently fetched → PRT scored on the RFC's self-declared §1.4 matrix, which is structurally complete; confidence on forward-coverage accuracy is medium for that reason.
Scorecard
Frontend Scorecard (11 categories)
| Category | Score | Evidence-Based Rationale |
|---|---|---|
| PRT — PRD Traceability | 7.5 | §1.4 maps every PRD section bidirectionally; 3 partials (OQ-6, OQ-4, dep confirmation). PRD body not fetchable → forward-coverage taken on trust. |
| TDC — Technical Decisions | 7.5 | 6 ADRs in §1.5 each name a chosen option grounded in real files. ADR-5 conditional on OQ-4; throttle deferred to OQ-6 → not fully closed. |
| CNT — Contract Specificity | 7.0 | SDK event table (§2.4), state surface (§2.5), storage keys (§2.6) specified. But SDK postMessage payload shape only "user_sso_id"; throttle window TBD (OQ-6); data-fetching for company reuses /users/me (verified authStore.ts:80-88). |
| SCB — Scope Boundaries | 9.0 | Repo map + named new/modified files (§2.0), explicit non-goals (§1.3), deferred cross-repo work tagged (§1.6). Agent can produce file-by-file plan directly. |
| DEP — Dependencies | 6.0 | All deps named with status, but 3 critical (@mekari/sdk confirmed absent in package.json:63-77; Session Manager; iframe path) are blocked/ambiguous (OQ-1/2/3). Honest, but unconfirmed status gates execution. |
| FMC — Failure Mode Coverage | 7.0 | server_down backoff + msli<2h fallback (§2.7, §2.9); logged_out/switch_user paths; race noted (OQ-7). Error-message catalog only partial — 2 toast strings, no full SDK-error→copy/i18n map. |
| NFS — Non-Functional Specificity | 6.5 | CSP/frame-ancestors/token/OWASP table (§3). Perf budget correctly BE-owned. a11y of toasts unaddressed; hardcoded copy not i18n keys. |
| TPS — Test Plan Specificity | 8.0 | Per-chunk acceptance, TDD red-first, named branch assertions (§4.C ch.3 asserts all 4 SDK events; ch.4 asserts cookie+msli clear; ch.5 fake-timer throttle). |
| ROL — Rollout & Rollback | 8.5 | Flag contract default-off (§4.A, verified surface nuxt.config.ts:148), ordered rollback recipe (§4.D), stages + go/no-go (§4.E), config contract present. |
| OBS — Observability | 7.0 | mixpanel event counts + server_down rate alert (§3). Exact event names/properties not enumerated. |
| CPA — Pattern Alignment | 9.0 | Mirrors real patterns: Pinia setup-store (authStore.ts:30), plugin init (pixel.ts), logout redirect (authenticated.global.ts:109). Honestly flags no in-repo iframe pattern. |
No score caps triggered. Verdict is HOLD on dependency/decision blockers, not on a capped score.
Decision Closure Assessment
Decision Index
| # | Decision | Status | Critical Gaps |
|---|---|---|---|
| 1 | Feature-flag mechanism (ADR-1) | Resolved | none |
| 2 | Where SDK loads (ADR-2) | Partial | blocked on OQ-1 (package not installable) |
| 3 | Which PRD flow applies (ADR-3) | Resolved | none — OAuth2 code flow verified ssoCallback.ts:6 |
| 4 | current_user source (ADR-4) | Partial | stale-token edge (OQ-7) |
| 5 | Current-company sync (ADR-5) | Dangling | endpoint choice unresolved (OQ-4) |
| 6 | Event→action routing (ADR-6) | Resolved | none |
| 7 | Refresh throttle window | Dangling | interval "TBD" (OQ-6) |
| 8 | Iframe canonical path | Dangling | /sm/current vs /sessionmanager/current (OQ-3) |
Aggregate: 3 Resolved, 2 Partial, 3 Dangling (of 8).
Decision: 1 — Feature-flag mechanism (ADR-1)
Status: Resolved
- What was decided: New
centralized_sessionkey inconfigs/*.json→runtimeConfig.public, defaultfalse. - Alternatives considered: Env-var-only / remote config rejected — "no env-var-only or remote-config system exists" (§4.A). Grounded.
- Grounding:
nuxt.config.ts:148env spread (verified —...CONFIGENVIRONMENT.env);configs/{local,development,production}.json. - Interface:
runtimeConfig.public.centralized_session: boolean = false. Fully specified. - Failure handling: Flag off → SDK not loaded, zero behavior change (chunk 1 acceptance).
- Challenge: Scale n/a; reversibility trivial (flip + redeploy); no conflicts; agent-implementable now.
Decision: 2 — Where SDK loads (ADR-2)
Status: Partial
- What was decided: New
~/plugins/centralized-session.tsregistered afterauth.ts(verified ordernuxt.config.ts:89-95). - Alternatives: Init in middleware vs plugin — plugin chosen per
pixel.ts/mixpanel.tsprecedent. Grounded. - Grounding: Strong; mirrors
plugins/pixel.ts. - Interface:
new Session({ current_user })— but the SDK module is unavailable, so its constructor/return type cannot be typed. - Failure handling: Flag-gated; load-failure of
/sm/sdk.jsnot explicitly handled at plugin level (onlyserver_downevent path covered). - Challenge: Agent would guess the SDK import surface and a script-load-failure fallback. Blocked on OQ-1.
Decision: 5 — Current-company sync (ADR-5)
Status: Dangling
- What was decided: Reuse
/users/me(authStore.fetchAuthLaunchpad) — verified atauthStore.ts:80-88— unless OQ-4 forces PRD's/v1.1/users/me/current_company. - Alternatives: PRD endpoint named but not chosen.
- Interface: If reuse, fully specified; if new endpoint, request/response unspecified.
- Gaps: Branch unresolved → agent cannot know which call to wire for the
logged_incompany-resync step. - Suggested resolution: Default to
/users/mereuse (already returnscompany_id,authStore.ts:88); only adoptcurrent_companyif SSO requires a fresh server-side switch confirmation. Confirm with SSO BE. - Open question: OQ-4.
Decision: 7 — Refresh throttle window
Status: Dangling
- What was decided:
session.refresh()"throttled" — interval TBD (OQ-6). - Gaps: Chunk 5 acceptance ("≤ once per throttle window") is unassertable without a number.
- Suggested resolution: Pick a concrete window (e.g. 60s leading-edge throttle) so fake-timer specs are deterministic; align with the 2h server idle TTL so refresh < TTL.
- Open question: OQ-6.
Decision: 8 — Iframe canonical path
Status: Dangling
- What was decided: Not decided — PRD gives
/sm/currentand/sessionmanager/current. - Gaps: CSP
frame-ancestors,server_downretry timing, and source-verification all hinge on this. - Suggested resolution: Confirm canonical path with SSO/Account BE before chunk 2; pin it in §2.2 and the CSP whitelist.
- Open question: OQ-3.
(ADR-3, ADR-4, ADR-6 assessed Resolved/Partial as indexed; all grounded in verified anchors.)
UI State Audit
This RFC adds session-lifecycle logic, not data-driven view components. UI surface = 2 toasts + redirects.
| Component/Surface | Loading | Empty | Error | Partial | Success | Assessment |
|---|---|---|---|---|---|---|
"User has changed" notice (switch_user) | n/a | n/a | defined (re-auth fail → redirect) | n/a | defined (toast + home) | 2/2 applicable |
Session-expired toast/redirect (logged_out/server_down) | n/a | n/a | defined (sign-out) | defined (msli degraded) | defined | 3/3 applicable |
Summary: No multi-state data components introduced. Session surfaces have their applicable states defined. Gap: exact toast copy is hardcoded, not i18n — see Accessibility/NFS.
Performance Budget Check
| Metric | Target | Baseline | Source | Assessment |
|---|---|---|---|---|
/sm/current p95 | <100ms | — | PRD §3 | BE-owned — correctly out of FE scope (§3) |
| SDK script weight | not stated | — | — | Minor gap: @mekari/sdk bundle/load-cost delta not estimated (it's an external <script>, but blocking-vs-async load not specified) |
No FE Core Web Vitals budget — acceptable for a session-logic feature, but SDK script load strategy (async/defer) should be pinned.
Accessibility Review
| Aspect | Specified? | Details | Assessment |
|---|---|---|---|
| Toast announcement (aria-live) | no | reuses @mekari/pixel3 toast.notify | rely on Pixel3 defaults — acceptable, unverified |
| Focus after redirect | no | full-page redirect on logout/switch | acceptable (browser nav resets focus) |
| i18n of copy | no | "user has changed" hardcoded (§2.7) | gap — should be i18n key per AGENTS/i18n convention |
| Motion/contrast | n/a | no new visual surfaces | n/a |
Pattern Alignment Check
| Pattern | RFC Approach | Assessment |
|---|---|---|
| State management (Pinia setup store) | follows authStore.ts:30 | Aligned |
| 3rd-party SDK init | follows plugins/pixel.ts/mixpanel.ts | Aligned |
| Logout redirect | reuses authenticated.global.ts:109 (verified) | Aligned |
| Cookie access | reuses useAuthCookies.ts:9-30 (verified) | Aligned |
iframe/postMessage | none in repo — explicitly flagged, SDK-encapsulated | Honest; no parallel system introduced |
| Analytics | extends mixpanel.ts event counts | Preserved/extended |
Data Flow Trace
Flow: SSO logout detected on Launchpad
SDK emits event {status:'logged_out'}
→ plugins/centralized-session.ts → useCentralizedSession handler
→ reuse authenticated.global.ts:109 redirect → window.location.replace(CHATPANEL_URL/logout)
→ (no /users/me call; terminal redirect)
Gap: none — terminal path is grounded.
Flow: switch_user
SDK event {status:'switch_user'}
→ authStore.resetAuth() (authStore.ts:142, verified) + clear token cookies + remove msli
→ redirect SSO /auth?...response_type=code (ssoCallback.ts:6 shape, verified)
→ sso-callback → token exchange → /users/me resync (authStore.ts:80)
→ toast.notify("user has changed") + navigate home
Gaps: (1) order of resetAuth vs cookie clear vs msli removal asserted in ch.4 — good; (2) the current_user passed to SDK may still be the old launchpad.sso_id (OQ-7) — verify comparison still yields switch_user.
Agentic Readiness Deep-Dive
Vague Word / Deferred-Value Audit
| # | Word/Phrase | Location | Impact | Concrete Replacement |
|---|---|---|---|---|
| 1 | "throttled (interval TBD)" | §1.2 SC3, §4.C ch.5 | agent picks arbitrary window; spec non-deterministic | name a window, e.g. 60s leading-edge |
| 2 | "reuse /users/me unless OQ-4 forces new endpoint" | ADR-5 | agent guesses which call | decide endpoint pre-chunk-3 |
| 3 | "cache max 5s" (iframe render) | §2.7 | BE-owned but FE retry timing depends on it | confirm with OQ-3 path |
Total deferred-value blockers in spec sections: 3 (all tracked as OQs — good hygiene, still blocking).
Dangling Alternatives
| # | Alternatives | Location | Impact |
|---|---|---|---|
| 1 | /sm/current vs /sessionmanager/current | §2.7 / OQ-3 | CSP + retry timing unimplementable |
| 2 | /users/me vs /v1.1/users/me/current_company | ADR-5 / OQ-4 | wrong endpoint wired |
Total dangling alternatives: 2.
Task Decomposition Assessment
| Chunk | Acceptance Criteria | Assessment |
|---|---|---|
| 1 Feature flag | runtimeConfig.public.centralized_session===false; flag-off renders unchanged | verifiable — executable now |
| 2 SDK loader | SDK instantiated once when flag on | verifiable but blocked OQ-1 |
| 3 Event router | spec asserts all 4 events (mocked) | verifiable — executable now (mock) |
| 4 switch/logout wiring | cookies + msli cleared before redirect | verifiable |
| 5 refresh + msli | refresh() ≤1/window; msli<2h fallback | vague until OQ-6 |
| 6 pilot enable | metrics emit; server_down < threshold | verifiable post-deploy |
Strengths
- Verified grounding (§2.0 + Source Verification table): every cited anchor checked accurate against repo (
authStore.ts:139,142,authenticated.global.ts:80,109,useAuthCookies.ts:5-7,30,nuxt.config.ts:89-95,148). An agent can trust the map. - Scope discipline (§1.3, §1.6, §2.0 repo map): in/out scope file-precise; cross-repo (CRM/Hub/HubChat) explicitly deferred — no surprise blast radius.
- Rollout/rollback (§4.A–§4.E): default-off flag, ordered rollback recipe, FE-only safety (no DB coupling), pre-merge command gates from real
package.json.
Biggest Gaps
- DEP / OQ-1+OQ-2 (§5):
@mekari/sdkconfirmed absent (package.json:63-77) and no Session Manager in any FE repo → chunks 2–6 cannot run or integration-test. This is the execution blocker. - OQ-3 iframe path ambiguity (§2.7): two candidate paths; CSP
frame-ancestorsandserver_downretry timing depend on the answer. - OQ-6 throttle + OQ-4 endpoint (§1.6, ADR-5): two unresolved values make chunk 5 acceptance unassertable and chunk-3/4 company resync ambiguous.
Priority Actions
- OQ-1 / OQ-2 — Obtain published
@mekari/sdkname+version+CDN URL and a staging/sm/current. Until then cap work at chunk 1 + chunk 3 (mocked SDK behind a typed interface). Unblocks chunks 2,4,5,6. - OQ-3 — Confirm canonical iframe path; pin it in §2.2 and the CSP whitelist so
server_downtiming andframe-ancestorsare deterministic. - OQ-6 — Set a concrete refresh throttle window so chunk 5 fake-timer acceptance is testable.
- OQ-4 / ADR-5 — Decide
/users/mereuse vscurrent_company; default to reuse (authStore.ts:88already returnscompany_id) unless SSO mandates otherwise. Add an i18n key for "user has changed" instead of a hardcoded string.
Implementation Readiness Checklist
Unblocked (agent can proceed)
- PRD → RFC traceability matrix (§1.4)
- Pattern alignment verified (§2.0)
- Rollout plan with flag + rollback (§4)
- Task decomposition with per-chunk acceptance (§4.C)
- Chunk 1 (flag) + Chunk 3 specs (mocked SDK) fully specified
- All UI/redirect failure surfaces defined (§2.4/§2.9)
Blocked (must fix first)
- OQ-1 —
@mekari/sdkpackage availability (blocks chunks 2,4,5) - OQ-2 — live/staging Session Manager
/sm/currentfor integration test - OQ-3 — canonical iframe path (blocks CSP +
server_downtiming) - OQ-6 — refresh throttle window (blocks chunk 5 acceptance)
- OQ-4 — company-sync endpoint choice (blocks chunk 3/4 resync)
Verdict: Fix 5 blockers first — FE specification is strong; execution gated on external/contract resolution. Chunks 1 & 3 (mocked) may proceed in parallel.
Task Manifest
| Order | Chunk | Files to Create/Modify | Acceptance Criteria | Dependencies |
|---|---|---|---|---|
| 1 | Feature flag | configs/{local,development,production}.json, nuxt.config.ts | runtimeConfig.public.centralized_session===false; flag-off path unchanged (spec) | None |
| 2 | Event router (mocked) | app/common/composables/useCentralizedSession.ts + .spec.ts | spec asserts all 4 events route correctly against a mocked SDK interface | Chunk 1 |
| 3 | SDK loader plugin | app/plugins/centralized-session.ts, nuxt.config.ts:89-95 | SDK instantiated once with launchpad.sso_id when flag on; not loaded when off | Chunk 1; OQ-1 |
| 4 | switch/logout wiring | useCentralizedSession.ts, reuse authStore.resetAuth (authStore.ts:142), authenticated.global.ts:109 | spec: token cookies + msli cleared before /auth redirect | Chunk 2 |
| 5 | refresh + msli fallback | useCentralizedSession.ts | refresh() ≤1/window (fake timers); msli<2h+valid token→logged_in else sign-out | Chunk 2; OQ-6 |
| 6 | Pilot enable | configs/production.json | post-deploy SDK metrics emit; server_down < threshold | Chunks 2–5; OQ-2/OQ-3 |
RFC specifies this decomposition (§4.C) — manifest verified against it; anchors confirmed real.
Dangling Decisions Log
| # | Decision | Location | Owner | Deadline |
|---|---|---|---|---|
| 1 | Canonical iframe path /sm/current vs /sessionmanager/current | §2.7 / OQ-3 | SSO/Account BE | pre-chunk-2 |
| 2 | Company-sync endpoint (/users/me vs current_company) | ADR-5 / OQ-4 | AL + SSO BE | pre-chunk-3 |
| 3 | session.refresh() throttle window | §1.6 / OQ-6 | AL squad | pre-chunk-5 |
| 4 | SDK event.origin enforcement guarantee | §3 / OQ-5 | SSO BE / infosec | pre-chunk-3 |
Open Questions
| # | Question | Category | Severity |
|---|---|---|---|
| 1 | Published @mekari/sdk package name/version + CDN URL availability? | DEP | Blocking |
| 2 | Staging /sm/current endpoint to integration-test the postMessage contract? | DEP | Blocking |
| 3 | Canonical iframe path? | CNT | Blocking |
| 4 | Reuse /users/me or adopt /v1.1/users/me/current_company? | TDC | Important |
| 5 | Does SDK enforce event.origin? | SAS/NFS | Important |
| 6 | session.refresh() throttle interval? | CNT | Important |
| 7 | Stale-token current_user still yields switch_user? | FMC | Nice-to-have |
Evidence Notes
app/common/composables/useAuthCookies.ts:5-7,30— confirmed token keys +launchpad.sso_id; validates ADR-4current_usersource. Lifted CNT/SCB.app/middleware/authenticated.global.ts:80,109— confirmed OAuth2 redirect shape + logout redirect target; validates ADR-3 and thelogged_outreuse path. Lifted TDC/FMC.app/common/store/authStore.ts:30,88,139,142— confirmed Pinia setup store,/users/mecompany source,resetAuth; validates ADR-5/ADR-6 + CPA.package.json:63-77— confirmed@mekari/sdkabsent; the central DEP blocker, capped DEP at 6.0 and drove the HOLD verdict.nuxt.config.ts:89-95,148— confirmed plugin order +runtimeConfigenv spread; validates ADR-1/ADR-2. Lifted ROL.- PRD (Confluence PT/48962437340) — not fetchable at review; PRT scored on §1.4 self-declared matrix → confidence on forward coverage is medium, overall confidence High on FE-side claims.