Skip to main content

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/postMessage pattern exists rather than fabricating one).
  • Failure branches enumerated (logged_out/switch_user/server_down + msli fallback, §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 postMessage payload 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_down timing 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 ElementRFC SectionCoverage
Overview / issue (stale session, no SSO-logout observation)§1.1Full
SC1 — SDK reports status across products§1.2, §2.4Full (FE reacts to 4 events)
SC2 — 2h idle expiry → logout§1.2, §2.4 logged_outFull
SC3 — activity refreshes session§1.2, §4.C ch.5Partial — throttle interval TBD (OQ-6)
SC4 — 1 account → multiple sessions§1.2, §1.6Full (correctly scoped n/a — BE-owned)
Out of scope§1.3Full
Dependencies (SDK, Session Manager, Redis)§2.2, §5 OQ-1/2Partial — declared as external, unconfirmed
How to use SDK / events§2.4Full
Local storage msli / _mekari_account§2.6Full
FE Web Session Flow§1.4Mapped n/a — Launchpad is OAuth2 (ADR-3)
OAuth2 code flow§2.7Full
User logout / switch account§2.7, §2.9Full
current_company endpointADR-5 / OQ-4Partial — reuse vs PRD endpoint unresolved
Database model (no change)§2.3Full
HA & Security§3Full (FE-side)
Rollout plan§4Full (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)

CategoryScoreEvidence-Based Rationale
PRT — PRD Traceability7.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 Decisions7.56 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 Specificity7.0SDK 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 Boundaries9.0Repo 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 — Dependencies6.0All 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 Coverage7.0server_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 Specificity6.5CSP/frame-ancestors/token/OWASP table (§3). Perf budget correctly BE-owned. a11y of toasts unaddressed; hardcoded copy not i18n keys.
TPS — Test Plan Specificity8.0Per-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 & Rollback8.5Flag 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 — Observability7.0mixpanel event counts + server_down rate alert (§3). Exact event names/properties not enumerated.
CPA — Pattern Alignment9.0Mirrors 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

#DecisionStatusCritical Gaps
1Feature-flag mechanism (ADR-1)Resolvednone
2Where SDK loads (ADR-2)Partialblocked on OQ-1 (package not installable)
3Which PRD flow applies (ADR-3)Resolvednone — OAuth2 code flow verified ssoCallback.ts:6
4current_user source (ADR-4)Partialstale-token edge (OQ-7)
5Current-company sync (ADR-5)Danglingendpoint choice unresolved (OQ-4)
6Event→action routing (ADR-6)Resolvednone
7Refresh throttle windowDanglinginterval "TBD" (OQ-6)
8Iframe canonical pathDangling/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_session key in configs/*.jsonruntimeConfig.public, default false.
  • Alternatives considered: Env-var-only / remote config rejected — "no env-var-only or remote-config system exists" (§4.A). Grounded.
  • Grounding: nuxt.config.ts:148 env 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.ts registered after auth.ts (verified order nuxt.config.ts:89-95).
  • Alternatives: Init in middleware vs plugin — plugin chosen per pixel.ts/mixpanel.ts precedent. 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.js not explicitly handled at plugin level (only server_down event 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 at authStore.ts:80-88unless 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_in company-resync step.
  • Suggested resolution: Default to /users/me reuse (already returns company_id, authStore.ts:88); only adopt current_company if 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/current and /sessionmanager/current.
  • Gaps: CSP frame-ancestors, server_down retry 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/SurfaceLoadingEmptyErrorPartialSuccessAssessment
"User has changed" notice (switch_user)n/an/adefined (re-auth fail → redirect)n/adefined (toast + home)2/2 applicable
Session-expired toast/redirect (logged_out/server_down)n/an/adefined (sign-out)defined (msli degraded)defined3/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

MetricTargetBaselineSourceAssessment
/sm/current p95<100msPRD §3BE-owned — correctly out of FE scope (§3)
SDK script weightnot statedMinor 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

AspectSpecified?DetailsAssessment
Toast announcement (aria-live)noreuses @mekari/pixel3 toast.notifyrely on Pixel3 defaults — acceptable, unverified
Focus after redirectnofull-page redirect on logout/switchacceptable (browser nav resets focus)
i18n of copyno"user has changed" hardcoded (§2.7)gap — should be i18n key per AGENTS/i18n convention
Motion/contrastn/ano new visual surfacesn/a

Pattern Alignment Check

PatternRFC ApproachAssessment
State management (Pinia setup store)follows authStore.ts:30Aligned
3rd-party SDK initfollows plugins/pixel.ts/mixpanel.tsAligned
Logout redirectreuses authenticated.global.ts:109 (verified)Aligned
Cookie accessreuses useAuthCookies.ts:9-30 (verified)Aligned
iframe/postMessagenone in repo — explicitly flagged, SDK-encapsulatedHonest; no parallel system introduced
Analyticsextends mixpanel.ts event countsPreserved/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/PhraseLocationImpactConcrete Replacement
1"throttled (interval TBD)"§1.2 SC3, §4.C ch.5agent picks arbitrary window; spec non-deterministicname a window, e.g. 60s leading-edge
2"reuse /users/me unless OQ-4 forces new endpoint"ADR-5agent guesses which calldecide endpoint pre-chunk-3
3"cache max 5s" (iframe render)§2.7BE-owned but FE retry timing depends on itconfirm with OQ-3 path

Total deferred-value blockers in spec sections: 3 (all tracked as OQs — good hygiene, still blocking).

Dangling Alternatives

#AlternativesLocationImpact
1/sm/current vs /sessionmanager/current§2.7 / OQ-3CSP + retry timing unimplementable
2/users/me vs /v1.1/users/me/current_companyADR-5 / OQ-4wrong endpoint wired

Total dangling alternatives: 2.

Task Decomposition Assessment

ChunkAcceptance CriteriaAssessment
1 Feature flagruntimeConfig.public.centralized_session===false; flag-off renders unchangedverifiable — executable now
2 SDK loaderSDK instantiated once when flag onverifiable but blocked OQ-1
3 Event routerspec asserts all 4 events (mocked)verifiable — executable now (mock)
4 switch/logout wiringcookies + msli cleared before redirectverifiable
5 refresh + mslirefresh() ≤1/window; msli<2h fallbackvague until OQ-6
6 pilot enablemetrics emit; server_down < thresholdverifiable 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/sdk confirmed 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-ancestors and server_down retry 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

  1. OQ-1 / OQ-2 — Obtain published @mekari/sdk name+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.
  2. OQ-3 — Confirm canonical iframe path; pin it in §2.2 and the CSP whitelist so server_down timing and frame-ancestors are deterministic.
  3. OQ-6 — Set a concrete refresh throttle window so chunk 5 fake-timer acceptance is testable.
  4. OQ-4 / ADR-5 — Decide /users/me reuse vs current_company; default to reuse (authStore.ts:88 already returns company_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/sdk package availability (blocks chunks 2,4,5)
  • OQ-2 — live/staging Session Manager /sm/current for integration test
  • OQ-3 — canonical iframe path (blocks CSP + server_down timing)
  • 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

OrderChunkFiles to Create/ModifyAcceptance CriteriaDependencies
1Feature flagconfigs/{local,development,production}.json, nuxt.config.tsruntimeConfig.public.centralized_session===false; flag-off path unchanged (spec)None
2Event router (mocked)app/common/composables/useCentralizedSession.ts + .spec.tsspec asserts all 4 events route correctly against a mocked SDK interfaceChunk 1
3SDK loader pluginapp/plugins/centralized-session.ts, nuxt.config.ts:89-95SDK instantiated once with launchpad.sso_id when flag on; not loaded when offChunk 1; OQ-1
4switch/logout wiringuseCentralizedSession.ts, reuse authStore.resetAuth (authStore.ts:142), authenticated.global.ts:109spec: token cookies + msli cleared before /auth redirectChunk 2
5refresh + msli fallbackuseCentralizedSession.tsrefresh() ≤1/window (fake timers); msli<2h+valid token→logged_in else sign-outChunk 2; OQ-6
6Pilot enableconfigs/production.jsonpost-deploy SDK metrics emit; server_down < thresholdChunks 2–5; OQ-2/OQ-3

RFC specifies this decomposition (§4.C) — manifest verified against it; anchors confirmed real.


Dangling Decisions Log

#DecisionLocationOwnerDeadline
1Canonical iframe path /sm/current vs /sessionmanager/current§2.7 / OQ-3SSO/Account BEpre-chunk-2
2Company-sync endpoint (/users/me vs current_company)ADR-5 / OQ-4AL + SSO BEpre-chunk-3
3session.refresh() throttle window§1.6 / OQ-6AL squadpre-chunk-5
4SDK event.origin enforcement guarantee§3 / OQ-5SSO BE / infosecpre-chunk-3

Open Questions

#QuestionCategorySeverity
1Published @mekari/sdk package name/version + CDN URL availability?DEPBlocking
2Staging /sm/current endpoint to integration-test the postMessage contract?DEPBlocking
3Canonical iframe path?CNTBlocking
4Reuse /users/me or adopt /v1.1/users/me/current_company?TDCImportant
5Does SDK enforce event.origin?SAS/NFSImportant
6session.refresh() throttle interval?CNTImportant
7Stale-token current_user still yields switch_user?FMCNice-to-have

Evidence Notes

  • app/common/composables/useAuthCookies.ts:5-7,30 — confirmed token keys + launchpad.sso_id; validates ADR-4 current_user source. Lifted CNT/SCB.
  • app/middleware/authenticated.global.ts:80,109 — confirmed OAuth2 redirect shape + logout redirect target; validates ADR-3 and the logged_out reuse path. Lifted TDC/FMC.
  • app/common/store/authStore.ts:30,88,139,142 — confirmed Pinia setup store, /users/me company source, resetAuth; validates ADR-5/ADR-6 + CPA.
  • package.json:63-77 — confirmed @mekari/sdk absent; the central DEP blocker, capped DEP at 6.0 and drove the HOLD verdict.
  • nuxt.config.ts:89-95,148 — confirmed plugin order + runtimeConfig env 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.