Skip to main content

RFC Review: Centralized Web Session — Hub FE Integration with mekari-session SDK

Executive Summary

  • Overall Score: 7.0/10
  • Rating: Strong
  • RFC Type: frontend
  • Sub-Type: enhancement
  • Assessment Confidence: High
  • Applied Caps/Gates: CNT capped <7.0 (no error-message catalog / fetching strategy for company-sync); FMC capped <7.0 (no error-message catalog). No hard overall cap triggered — no category <5.0, fewer than 3 categories <5.0.
  • Implementation Readiness Verdict: HOLD — Chunks 1–5 are agent-executable today behind the OFF toggle; Chunks 6–7 and full enablement blocked by OQ-1/OQ-2/OQ-3 (external/cross-squad) and OQ-4 (event-name contract).
  • Report Path: /Users/mekari/Documents/hub/rfc-review-report.md
  • RFC Author: Syafrizal Muhammad | Reviewed: 2026-06-28

This RFC is unusually well-grounded: nearly every claim cites a real file:line (verified in §2.0 Source Verification), decisions are captured as 6 ADRs with options/rationale/reversibility, and the rollout/rollback recipe is concrete and ordered. An AI agent could implement Chunks 1–5 (toggle gate, SDK boot mixin, logged_out/logged_in, switch_user re-auth, server_down fallback) from this document with very few questions. The biggest gap is that the SDK contract Hub consumes is not pinned down — the event name contradiction (logout vs logged_out, OQ-4) and absent payload schema/error catalog mean handler code will guess — and three critical upstream dependencies (SDK package, org flag, BE company endpoint) are unbuilt. The one thing that must change before full agentic execution: lock the SDK event contract (names + payload shape) and close OQ-1/OQ-2/OQ-3, or scope the first agent run explicitly to Chunks 1–5.


Quick Verdict

Why this RFC can be implemented agentically:

  • File-by-file scope is exact (§2.0 Repo Map + Existing Code Anchors + §4.C ordered chunks with acceptance criteria) — agent knows precisely where to write and what "done" means per chunk.
  • Decisions are closed and reuse existing primitives (doSignOut(), $auth.logout, middleware/login.js redirect) with ADRs — minimal architectural guessing.
  • Rollback is numbered and flag-first (§4.D); toggle-OFF = byte-for-byte current behaviour, so blast radius is contained.

Why this RFC will cause agent guessing or rework:

  • SDK event name + payload shape unresolved (OQ-4; §2.4 comment "logout vs logged_out") — handler branches are written against an unknown enum.
  • No error-message catalog and no UI failure states (toast copy, sign-out interstitial, re-auth failure) — agent invents copy / ships blank states.
  • Company-sync endpoint does not exist (OQ-1) and flag not in payload (OQ-2) — Chunk 6 is uncodeable; agent must not start it.

PRD → RFC Traceability Matrix

Source "PRD" = the Account & Launchpad source RFC (Confluence, linked in frontmatter). Hub RFC is the consuming-squad slice.

Standard format (source RFC exists)

PRD Element (source RFC section)RFC SectionCoverage
Overview / web + FE-app session bugs§1.1Full — Hub-scoped to web session, two concrete bugs named
Success Criteria§1.2Partial — Hub subset only; Session.refresh() activity cadence deferred (OQ-7)
Out of Scope§1.3Full — inherited + Hub-specific exclusions
Dependencies (SDK, Session Mgr, Redis, Kong)§1.6, §2.0, §2.10Full — marked upstream/READ-ONLY, ownership table
SDK usage / contract / events§2.4, §2.6Partial — Missing canonical event names + payload schema (OQ-4)
Local Storage msli§2.5, ADR-6, §4.C-5Full — write-on-login + read-on-server_down
Cookies _mekari_account§2.0, §2.5, §3Full — read-only via SDK iframe; CSP allow-list noted
OAuth2 Authorization Code Flow§2.6, ADR-3Full — mapped to existing sso-callback/login
Web Session (client_credentials) flown/aFull — explicitly rejected in ADR-3 with reversibility note
User Logout / Switch Account§2.6, §2.7Full — mapped to doSignOut/re-auth
Database Model (no change)§2.8Full — FE-only, no schema
HA & Security§3Partial — Hub-side CSP/referrer + obs; whitelist mechanics deferred (OQ-5/OQ-6)
Rollout Plan§4Full — Hub = step 5 consumer
current-company sync (RFC decision)§2.4, §5 OQ-1No verified PRD/BE driver — endpoint does not exist; flagged critical

Summary: 11 of 13 source-RFC elements fully covered, 3 partial (SDK contract, success-criteria cadence, HA whitelist), 0 silently dropped. 1 RFC decision (current-company sync) has no existing backend contract — correctly surfaced as OQ-1 rather than assumed.


Scorecard

Frontend Scorecard (11 categories)

CategoryScoreEvidence-Based Rationale
PRT — PRD Traceability8.0§1.4 maps every source-RFC section bidirectionally; §2.0 Source Verification table cross-checks claims to file:line. Docked for unresolved SDK-contract mapping (OQ-4).
TDC — Technical Decisions8.56 ADRs (§2.3) each with Context/Options/Decision/Rationale/Consequences/Reversibility. Decision index §1.6 → ADR cross-ref. Only soft gap: ADR-3 reversibility ("Medium") not fully costed.
CNT — Contract Specificity6.0SDK contract sketched (§2.4) but data.status enum contradictory (OQ-4) and full payload not typed; company-sync request/response absent (OQ-1); no error-message catalog → rubric caps CNT <7.0. msli/cookie contract (§2.5) is well-specified.
SCB — Scope Boundaries9.0§2.0 Repo Map + Existing Code Anchors name every file with read/modify/new tag; §2.9 Branch & Skip Catalog; §2.10 cross-squad boundary. Agent can produce file-by-file plan directly.
DEP — Dependencies8.0Every dep named with status (§2.4 tables, §1.6, OQ-3 for @mekari/sdk, OQ-1/2 for BE). Docked because 3 deps are unconfirmed/unbuilt (status honestly = blocked).
FMC — Failure Mode Coverage6.5server_down (§2.6 failure diagram, ADR-6 dual-branch), logged_out, switch_user covered with state machine §2.7. But no error-message catalog (rubric caps <7.0), no UI error/empty/partial states, re-auth failure path (sso-callback failure) untraced.
NFS — Non-Functional Specificity5.0§3.1 perf is qualitative ("must not block first paint"), no LCP/INP/bundle-delta numbers; no accessibility spec for switch_user toast; no browser-support matrix. Security is the strong sub-part (§3.2 table).
TPS — Test Plan Specificity7.0§1.7 + §4.C give per-chunk acceptance criteria as testable assertions ("toggle OFF ⇒ 0 SDK calls", "msli set to numeric timestamp"). Missing: a11y tests, named E2E scenarios, visual regression for toast.
ROL — Rollout & Rollback8.5§4.A compat, §4.C ordered chunks, §4.D numbered agent-executable rollback (flag-first), post-deploy signals defined. Docked: stop-condition thresholds qualitative ("≈0", "no regression").
OBS — Observability8.0§3.3 names exact emitters: RUM action centralized_session.<event>, Mixpanel v2 for switch_user/forced sign-out, alert on server_down spike. Tied to existing plugins/datadog-rum.ts. Docked: no alert threshold number.
CPA — Pattern Alignment9.0§2.0 Patterns-to-Follow table maps each concern to a reference file (plugins/hotjar.js boot, EventBus $on/$off + beforeDestroy leak rule, org-payload flag, doSignOut reuse). Explicitly avoids parallel postMessage listener (§3.2).

Decision Closure Assessment

Decision Index

#DecisionStatusCritical Gaps
1Load SDK from InitComponent.vue boot under layouts/hub.vueResolved
2npm @mekari/sdk import behind dynamic import()Partialpackage not published/accessible (OQ-3)
3Hub follows OAuth2 Authorization-Code flowResolvedreversibility cost to client_credentials only sketched
4Flag source = organization.feature_flag.centralized_sessionPartialflag not yet in org payload (OQ-2)
5Reuse existing auth primitives for event handlingResolved
6server_downmsli heuristic then sign-outResolved"token valid" check mechanism not pinned to a function
7SDK event contract (names + payload)Danglinglogout vs logged_out contradiction (OQ-4); payload schema absent

Aggregate: 4 Resolved, 2 Partial, 1 Dangling


Decision: 7 — SDK event contract (names + payload shape)

Status: Dangling

What was decided

NOT EXPLICITLY RESOLVED. §2.4 code comment: "source RFC sample uses data.status === 'logout' while the events section lists logged_out — reconcile with A&L (OQ-4) before coding." Handlers in §2.6/§2.7 assume the enum { logged_in, logged_out, switch_user, server_down }.

Alternatives considered

NO ALTERNATIVES DOCUMENTED — this is a contract to be confirmed, not a choice.

Grounding in existing code

Grounded in the consuming mixin path assets/mixins/session/centralizedSession.js (new) and SDK import @mekari/sdk, but the source-of-truth enum lives in the A&L SDK, not in Hub.

Interface specification

Incomplete. Missing: exact status string values, full payload fields beyond current_user, error object shape passed to session.on('event', (data, error) => …), and Session.refresh() throttle (OQ-7).

Failure handling

The (data, error) callback's error branch is never specified — what does Hub do when the SDK surfaces an error rather than a status?

Challenge results

  • Scale: N/A (event-driven, one iframe per shell).
  • Reversibility: High — string-map change once contract lands.
  • Consistency: Conflicts with itself in §2.4 (the cited contradiction).
  • Agent implementability: No — agent must choose logout or logged_out and will guess, producing a handler that silently never fires.

Gaps and suggestions

Missing: canonical status enum; payload schema; error-callback contract; refresh throttle. Suggested resolution: Pin a SESSION_EVENTS const in the mixin mapping the four states; write handlers against that const so a single edit reconciles the name once A&L confirms. Add a default/unknown-status branch that logs to RUM and no-ops (fail-safe, not fail-logout). Open questions for the author: OQ-4 — confirm exact status strings + payload + error shape with A&L before Chunk 3.


Decision: 2 — npm @mekari/sdk import behind dynamic import()

Status: Partial

What was decided

ADR-2: npm import (option a), lazy via dynamic import() inside the toggle guard; CDN URL retained as env only for CSP allow-listing.

Alternatives considered

(b) runtime <script src> like plugins/hotjar.js; (c) both. Rejected with reasons (versioned, tree-shakeable, Jest-mockable).

Grounding in existing code

Grounded: plugins/hotjar.js:1-3, nuxt.config.js:84/96/250, package.json:34-60 (SDK absent → confirmed new).

Interface specification

Import shape import { Session } from '@mekari/sdk' given; constructor new Session({ current_user }) given. Version pin not stated.

Failure handling

Dynamic-import failure (registry/network) not handled — what renders if import() rejects? Should be a silent no-op behind the toggle.

Challenge results

  • Scale: fine.
  • Reversibility: High.
  • Consistency: consistent with lazy-load discipline.
  • Agent implementability: Blocked — package not published/accessible (OQ-3). Agent can scaffold mock + tests but cannot install.

Gaps and suggestions

Missing: published package + registry access; version pin; import-failure branch. Suggested resolution: Gate Chunk 2 on OQ-3; in the meantime build against a local Jest mock of Session; wrap import() in try/catch → RUM warn → treat as toggle-OFF. Open questions: OQ-3 — publish date + registry scope.


Decision: 6 — server_down fallback

Status: Resolved

What was decided

ADR-6: if msli exists AND now - msli < 2h AND qontak._token.hub valid → stay (treat logged_in); else sign out. Diagram §2.6 "Failure path".

Alternatives considered

(a) immediate sign-out; (b) msli-then-sign-out; (c) ignore. (b) chosen, tied to source-RFC "Retry-backoff fallback".

Grounding in existing code

msli helper pattern utils/general.js:104; token cookie hubAuthScheme.js:152.

Interface specification

Mostly specified; "token valid" not bound to a concrete function — is it cookie presence or an expiry decode?

Failure handling

This decision is the failure handling; both branches covered + tested (§4.C-5).

Challenge results

  • Reversibility: High.
  • Consistency: consistent with state machine §2.7 Degraded state.
  • Agent implementability: Yes, except "token valid" check needs a named function.

Gaps and suggestions

Missing: exact validity predicate for qontak._token.hub. Suggested resolution: Specify whether validity = cookie present vs JWT exp decode; reuse any existing hubAuthScheme expiry check. Open questions: none blocking.

(ADR-1, ADR-3, ADR-5 assessed Resolved — clear decision, alternatives rejected, grounded, reuse existing primitives; abbreviated per >=7.0 optimization.)


UI State Audit

Behavioural FE change with one user-visible surface (the switch_user toast) + sign-out/redirect transitions.

Component / SurfaceLoadingEmptyErrorPartialSuccessAssessment
"User has changed" toast (switch_user)n/an/amissingn/adefined (§1.5)1/2 relevant states — no error/timeout copy if re-auth redirect fails
Re-auth redirect interstitial (switch_user)missingn/amissingn/adefined (§2.6)what does the user see during $auth.logout→SSO redirect? blank?
server_down degraded staten/an/adefined (stay/sign-out, §2.6)defineddefined3/3 — strongest
Company-sync on logged_inmissingmissingmissingmissingpartial (§2.6)blocked by OQ-1; states undefined

Summary: server_down path is fully stated; the user-visible toast and re-auth transition lack error/loading copy. Company-sync states are undefined pending OQ-1.


Performance Budget Check

MetricTargetCurrent BaselineSourceAssessment
LCP / INP / CLSnot statednot statedmissing — §3.1 only says "must not block first paint"
Bundle size deltanot statedmissing — @mekari/sdk size impact not quantified (ADR-2 calls it tree-shakeable but no number)
iframe load impact"lazy after $auth.loggedIn"§3.1vague — qualitative only

NO NUMERIC PERFORMANCE BUDGET — acceptable-ish for an enhancement (perf is not the goal) but bundle-delta of a new SDK should be bounded. Agent cannot assert a regression gate.


Accessibility Review

AspectSpecified?DetailsAssessment
Keyboard navigation flownotoast/redirect onlymissing
Focus managementnoswitch_user toast + redirectmissing — focus after toast/redirect undefined
ARIA labelsnomissing
Heading hierarchyn/ano new pagen/a
Color contrastnouses Pixel toastimplicit (design system) but unstated
Motion sensitivityn/an/a
Screen reader behaviornotoast announcement (aria-live?)missing — forced sign-out should be announced

A11y is the weakest non-functional area. For an enhancement touching only a toast + redirects the surface is small, but the "your account has changed" toast must be screen-reader-announced (aria-live="polite") and the forced redirect should not strand focus.


Pattern Alignment Check

PatternRFC ApproachAssessment
Third-party SDK bootfollows plugins/hotjar.js + nuxt.config registrationaligned (§2.0)
State management / flagsfollows org-payload feature_flag getter, v-if not v-showaligned (ADR-4, AGENTS.md)
Decoupled eventsfollows EventBus $on/$off + beforeDestroy cleanupaligned, leak rule cited
Sign-outreuses doSignOut() — no re-implementaligned (ADR-5)
Re-authreuses middleware/login.js auth-code redirectaligned
postMessageintentionally NOT adding wildcard listener; keeps separate from sso-callback.js:23aligned + security-aware
AnalyticsMixpanel v2 mixin (not legacy)aligned (AGENTS.md)

No parallel-system risk detected; strongest dimension of the RFC.


Agentic Readiness Deep-Dive

Vague Word Audit

#Word/PhraseLocationImpactConcrete Replacement
1"must not block first paint"§3.1agent has no measurable gate"SDK import() deferred until after $auth.loggedIn; route render never awaits it (assert in test)"
2"throttled per X (TBD)"§2.4refresh cadence guessedresolve OQ-7; state interval ms
3"server_down rate ≈ 0"§4.Dno alert threshold">N events/5min for company → page on-call"
4"token still valid"ADR-6predicate ambiguousname the cookie/exp check function

Total vague words in spec sections: 4

Dangling Alternatives

#AlternativesLocationImpact
1data.status === 'logout' vs 'logged_out'§2.4 / OQ-4handler matches wrong string → silently never fires

Total dangling alternatives: 1

Task Decomposition Assessment

ChunkAcceptance CriteriaAssessment
1 Toggle plumbinggetter returns false when flag absentverifiable
2 SDK boot + mixinON+sso_id ⇒ Session constructed; OFF ⇒ 0 callsverifiable (blocked OQ-3)
3 Boot wire + logged_out/inlogged_out ⇒ 1 emit; logged_in ⇒ msli numericverifiable (event name pends OQ-4)
4 switch_userlogout + redirect URL + msli removed + toastverifiable
5 server_downboth branches testedverifiable
6 company syncsync action dispatched, sets companyblocked OQ-1 — not startable
7 observabilityeach event emits mocked RUM actionverifiable

Decomposition is explicit, ordered, and dependency-aware (§4.C order rationale) — a standout strength.


Strengths

  • Anti-hallucination rigor (§2.0 Source Verification): every structural claim mapped to a file:line and the SDK-absence + 0-grep facts verified — an agent inherits a trustworthy map, not assumptions.
  • Closed, reuse-first decisions (§2.3 ADRs + ADR-5): event handling routes through existing doSignOut()/$auth.logout/middleware/login redirect, minimizing new surface and leak risk; EventBus cleanup rule honored.
  • Executable rollout/rollback (§4.C/§4.D): ordered chunks with per-chunk acceptance criteria and a numbered flag-first rollback recipe; toggle-OFF = byte-for-byte current behaviour bounds blast radius.

Biggest Gaps

  • SDK event contract unresolved (§2.4, OQ-4): logout vs logged_out contradiction + no payload/error schema → handler code (Chunks 3–5) is written against an unknown enum; agent guesses and may ship a never-firing branch.
  • Three critical upstream blockers (OQ-1 BE endpoint, OQ-2 org flag, OQ-3 SDK package): without OQ-2 the feature is permanently inert; without OQ-3 nothing imports; without OQ-1 Chunk 6 is uncodeable. RFC honestly flags these but they gate real execution.
  • Missing error-message catalog + a11y/perf numbers (§3.1/§3.2): no user-facing copy mapping for forced sign-out/switch, no toast screen-reader behaviour, no bundle-delta/LCP budget → caps CNT & FMC and leaves agent inventing copy and skipping a11y.

Priority Actions

  1. OQ-4 / Decision 7 — Confirm canonical SDK status enum + payload + error shape with A&L; encode as a SESSION_EVENTS const with a default→RUM-no-op branch. Unblocks correct handlers for Chunks 3–5.
  2. §3.2 + FMC — Add an error-message catalog: for switch_user toast and forced sign-out, give exact i18n keys/strings and aria-live behaviour; specify re-auth-failure path (sso-callback failure → sign-out). Lifts CNT/FMC above 7.0.
  3. OQ-1 / OQ-2 / OQ-3 — Get written commitments + dates: BE current-company contract, centralized_session in org payload, @mekari/sdk publish + registry access. Until then scope the first agent run to Chunks 1–5 only and mark Chunk 6 "do not start".
  4. §3.1 NFS — Quantify bundle-size delta budget for @mekari/sdk and add a browser-support line; even an enhancement should bound the new dep's weight.

Implementation Readiness Checklist

Unblocked (agent can proceed — Chunks 1–5 behind OFF toggle)

  • Source-RFC → RFC traceability matrix (§1.4)
  • Most technical decisions resolved with alternatives rejected (§2.3, except Decision 7)
  • Pattern alignment verified (§2.0 Patterns table)
  • Rollout plan + numbered rollback (§4.C/§4.D)
  • Observability metrics named (§3.3)
  • Task decomposition with acceptance criteria per chunk (§4.C)
  • Scope boundaries file-by-file (§2.0)
  • All interfaces fully specified — SDK event enum/payload missing (OQ-4)
  • All UI states defined — toast/re-auth error states missing
  • Performance budget quantified — missing
  • Accessibility requirements specified — missing
  • Browser support matrix — missing
  • Configuration contract — MEKARI_SESSION_SDK_URL env named; centralized_session flag default documented (safe-OFF) but flag not yet provisioned (OQ-2)
  • Zero vague words in spec sections — 4 remain

Blocked (must fix first)

  • OQ-3 — @mekari/sdk published + registry access (blocks Chunk 2 install)
  • OQ-2 — centralized_session in org payload (feature inert otherwise)
  • OQ-1 — Hub BE current-company endpoint contract (blocks Chunk 6)
  • OQ-4 — canonical SDK event names/payload (blocks correct Chunks 3–5 handlers)

Verdict: Fix 4 blockers first — Chunks 1–5 implementable today against mocks; full enablement and Chunk 6 gated.


Task Manifest

Verifying the RFC's own §4.C decomposition. It is sound; reproduced with reviewer dependency annotations.

OrderChunkFiles to Create/ModifyAcceptance CriteriaDependencies
1Toggle plumbingstore/organization.js (confirm getter), mixin computedgetter returns false when centralized_session absentNone
2SDK boot plugin + mixin (TDD)new plugins/mekari-session.js, assets/mixins/session/centralizedSession.js + __tests__/centralizedSession.spec.js; modify package.json, nuxt.config.js:84/250toggle ON+sso_id ⇒ Session constructed with current_user; OFF ⇒ 0 callsOQ-3 (install); build vs Jest mock until then
3Wire boot + logged_out/logged_inmodify components/layouts/main/InitComponent.vuelogged_out ⇒ 1× EventBus.$emit('user-sign-out'); logged_in ⇒ localStorage.msli numericChunk 2; OQ-4 (event names)
4switch_user re-authmixin handler; reuse middleware/login.js URL builder$auth.logout then SSO /auth?response_type=code…/sso-callback; msli removed; toastChunk 3; OQ-4
5server_down fallback + mslimixin handler + utils helper (cf. utils/general.js:104)now-msli<2h & valid token ⇒ no sign-out; else emit; both branches testedChunk 3; define token-valid predicate
6company synccommon/constants/endpoint.js (register key), store actionlogged_in ⇒ sync dispatched; sets companyBLOCKED OQ-1/OQ-2
7observabilitymixin RUM/Mixpanel callseach event emits mocked RUM actionChunks 3–5

RFC specifies this manifest; reviewer confirms order rationale (§4.C) is correct and dependency-safe.


Dangling Decisions Log

#DecisionLocationOwnerDeadline
1Canonical SDK event names + payload + error shape§2.4 / OQ-4A&Lbefore Chunk 3
2current-company BE contract (/me/current_company)§2.4 / OQ-1Hub BEbefore Chunk 6
3centralized_session org-payload flagADR-4 / OQ-2Hub BEbefore enablement
4@mekari/sdk publish + registry accessADR-2 / OQ-3A&Lbefore Chunk 2 install
5Session.refresh() throttle interval§2.4 / OQ-7A&Lbefore activity-refresh wiring

Open Questions

#QuestionCategorySeverity
1Confirmed BE current-company endpoint contract?PRT/CNTBlocking
2When does centralized_session enter org payload?ROL/DEPBlocking
3When is @mekari/sdk published + registry-accessible?DEPBlocking
4Canonical event status strings + payload + error object?CNT/FMCBlocking
5Hub prod domain(s) for SDK frame-ancestors/referrer whitelist?NFS (security)Important
6Hub current CSP source (header vs meta) + edit location for account.mekari.com?NFS (security)Important
7Session.refresh() throttle interval value?TDCNice-to-have
8Infosec approver name + Slack discussion thread (metadata [REQUIRED])?GovernanceImportant

Evidence Notes

  • §2.0 Source Verification table — strongest evidence; every claim tied to file:line incl. SDK-absence (package.json:34-60) and 0-grep for current_company. Raised PRT, SCB, CPA.
  • §2.3 ADRs — closed decisions with reversibility; raised TDC to 8.5; Decision 7 (event contract) absent here dragged TDC/CNT.
  • §2.4 SDK contract — the logout/logged_out self-contradiction + no payload schema + no error-message catalog capped CNT (6.0) and FMC (6.5).
  • §3 HA & Security — security sub-table solid; absence of a11y, perf numbers, browser matrix pinned NFS at 5.0.
  • §4.C/§4.D — per-chunk acceptance + numbered rollback; raised TPS, ROL, OBS.
  • §5 Open Questions + §7 gate — author self-declares "not ready: no" with 3 critical blockers; corroborates HOLD verdict rather than BLOCKED (Chunks 1–5 are genuinely executable).