RFC Review: Centralized Web Session — Hub Chat v2 FE Integration
Executive Summary
- Overall Score:
6.5/10 - Rating:
Needs Work - RFC Type:
frontend - Sub-Type:
enhancement - Assessment Confidence:
High - Applied Caps/Gates:
FMC < 7.0 gate (no error-message catalog) holds FMC at 6.5; CNT < 7.0 gate (data/SDK contract incomplete) holds CNT at 6.5. No hard overall cap triggered — overall set by judgment: two [critical] blockers prevent end-to-end agent execution. - Implementation Readiness Verdict:
HOLD — blockers Q1 (cross-domain fallback) and Q2 (SDK availability) must be closed before any chunk runs - Report Path:
/Users/mekari/Documents/hub-chat-v2/rfc-review-report.md - RFC Author:
syafrizal.abdillah@mekari.com| Reviewed:2026-06-28
This RFC is among the best-anchored FE integration documents in this repo: every claim is grounded in a verified file:line, the scope is file-by-file, and the agent execution plan is ordered with assertable acceptance criteria. An AI agent could implement Chunks 1–6 almost verbatim if its two upstream dependencies existed. They do not yet: @mekari/sdk is absent from the repo and unverified (grep confirms — present only in docs), and the cross-domain _mekari_account fallback may be structurally impossible for the qontak origin. The single thing that must change before agentic execution: resolve Q2 (publish/confirm the SDK package + its exact interface) and Q1 (lock the fallback contract boundary). The biggest strength is scope+pattern discipline; the biggest gap is that the load-bearing external contract (the SDK API surface) is unverified.
Quick Verdict
Why this RFC can be implemented agentically:
- Every chunk in §4.C names exact files, mock strategy (
vi.mock @mekari/sdk), and assertable acceptance criteria. - Decisions are closed as 7 ADRs (§2.6) with options, rejection rationale, and reversibility; all anchored to real code (
AuthStore.ts:42,AppConfigStore.ts:2, verified). - Rollout/rollback (§4.A/§4.D) is concrete: flag default off, numbered rollback, RUM verification signals.
Why this RFC will cause agent guessing or rework:
@mekari/sdkSession API is not pinned — §2.1 is explicitly "shape, not final code"; the event-subscription mechanism, constructor return, and teardown are unknown (Q2 [critical]).- The
msli/server_downsoft-grace depends on Q1, which the RFC itself flags as possibly "structurally impossible" — an agent would implement a fallback that may be invalid. - No error-message strings/i18n keys; the only user-facing copy ("user has changed") is TBD (Q6).
PRD → RFC Traceability Matrix
Parent cross-product RFC (Confluence) is the authoritative PRD. It was not machine-readable for this review; mapping assessed against the in-RFC §1.A coverage table, which itself enumerates parent-RFC sections.
Standard format (PRD exists)
| PRD Element (parent-RFC section) | RFC Section | Coverage |
|---|---|---|
| Overview / Success Criteria | §1.0, §1.A | Full |
| Proposal / SDK usage | §2.1, §2.2 | Partial — SDK init shape present, concrete API unverified (Q2) |
| SDK contract (4 events) | §2.2 event→action map | Full |
| FE Integration Flows (SDK flow) | §2.3 diagrams | Full |
| FE Integration Flows (Web Session / current-company) | §2.2, §2.3, §2.6-A7 | Partial — ownership of current_company sync unresolved (Q4) |
| OAuth2 auth-code flow | §2.2, §2.6-A6 | Full — reuses /sso-callback |
| User Logout From Product | §2.4 | Full — already implemented, verified pages/logout.vue:315 |
| User Switch Account | §2.2, §2.3, §2.6-A6 | Full |
| HA & Security | §3 | Full (FE share) |
| Rollout Plan | §4 | Full — hub-chat is step 5, not first pilot |
| Open Questions | §5 | Full + FE-specific blockers added |
Local Storage msli / cookies | §2.2, §5 Q1 | Partial — cross-domain conflict unresolved (Q1 [critical]) |
| Database Model | — | n/a — no DB (FE repo) |
| Multiple sessions per account | — | n/a — server/SSO concern |
| Out-of-scope: auto token revoke on idle | §1.A | Full — carried verbatim |
Summary: ~11 of 14 parent items fully covered, 3 partial (all tied to open questions Q1/Q2/Q4), 0 silently dropped. No RFC decision lacks a parent-RFC driver — zero scope creep. PRT is strong; the partials are upstream-owned, not authoring gaps.
Scorecard
Frontend Scorecard (11 categories)
| Category | Score | Evidence-Based Rationale |
|---|---|---|
| PRT — PRD Traceability | 8.0 | §1.A forward+reverse matrix, UI/role/lifecycle sub-tables. Reverse traceability explicit ("no RFC decision without PRD driver"). Capped under 9 only because parent PRD not directly verifiable here. |
| TDC — Technical Decisions | 8.0 | 7 ADRs (§2.6) with options/rejection/reversibility. Deductions: D5 server_down=/logout is "suggested" (inherits parent uncertainty); D7 depends on Q4. |
| CNT — Contract Specificity | 6.5 | event→action map (§2.2) and msli shape are clear, but SDK API itself "shape, not final code" (§2.1); no TS interface for the SDK, subscription, or teardown. Data-fetching = store refetch (OK). Gate: <7.0 because SDK contract unpinned. |
| SCB — Scope Boundaries | 9.0 | §2.0 Repo Map + §4.C chunks name every file to create/modify; non-goals explicit (§2.4 "already implemented", §1.A n/a rows). Agent can produce file-by-file plan directly. |
| DEP — Dependencies | 5.0 | @mekari/sdk unverified — no version/registry/entry (Q2 [critical], grep-confirmed absent). BE flag + BE current_company are external deps named with status. Honest but the core dep is a hard blocker. |
| FMC — Failure Mode Coverage | 6.5 | Covers server_down, origin-mismatch ignore, soft-grace, switch_user redirect (§2.2/§2.3). Gate: <7.0 — no error-message catalog; toast copy TBD (Q6); no per-HTTP-status UI behavior for the refetch calls. |
| NFS — Non-Functional Specificity | 6.0 | Security strong (§3.3 OWASP-mapped: postMessage origin, CSP, no-token-in-logs). But perf throttle "TBD" (Q3), no browser matrix, a11y not addressed (toast focus/SR), i18n copy TBD. |
| TPS — Test Plan Specificity | 8.0 | §4.C per-chunk assertable acceptance (e.g. "postMessage mismatched origin is ignored", "SDK NOT constructed when toggle off"). Maps to vitest commands. Missing: explicit failure-path E2E naming. |
| ROL — Rollout & Rollback | 8.5 | Flag default off (§4.A), numbered agent-executable rollback (§4.D), config contract = centralized_session flag, verification signals via RUM. hub-chat sequenced as step 5 behind Launchpad. |
| OBS — Observability | 7.5 | 3 named RUM actions w/ thresholds (§3.4); no-token payload rule. Deduction: action names unconfirmed (Q7); no dashboard/alert wiring named. |
| CPA — Pattern Alignment | 9.0 | §2.0 "Patterns to Follow" mirrors datadog.client.ts, seamless_auth_first gating (verified AppConfigStore.ts:7), useEventBus, pages/logout.vue reuse. Code will look native. |
Caps applied: CNT and FMC held below 7.0 by rubric "must be present for >=7.0" gates (data/SDK contract; error-message catalog). No overall hard cap fired (no category <5.0; DEP exactly 5.0). Overall set by judgment at 6.5: two [critical] blockers make end-to-end agent execution impossible today, despite high authoring quality.
Decision Closure Assessment
Decision Index
| # | Decision | Status | Critical Gaps |
|---|---|---|---|
| D1 | SDK init in plugins/mekariSession.client.ts | Resolved | none |
| D2 | Gate via AppConfigStore.centralized_session | Resolved | none |
| D3 | useCentralizedSession + useEventBus delivery | Resolved | session:* bus keys "only if needed" — slightly open |
| D4 | current_user = user.sso_id | Resolved | none (verified AuthStore.ts:42) |
| D5 | logged_out/server_down → /logout | Partial | server_down is "suggested"; soft-grace depends on Q1 |
| D6 | switch_user → reset → /sso-callback re-auth | Resolved | none |
| D7 | Current-company = refetch org+company stores | Partial | depends on BE setting current company (Q4) |
| Dx | SDK package identity + API surface | Dangling | Q2 [critical] — package unverified |
| Dy | Cross-domain _mekari_account fallback boundary | Dangling | Q1 [critical] — possibly impossible |
Aggregate: 5 Resolved, 2 Partial, 2 Dangling.
Decision: Dx — SDK package identity & API surface
Status: Dangling
What was decided
"Add @mekari/sdk Session SDK, load with sso_id" (§1.C). §2.1 gives only a sketch: new Session({ current_user: sso_id }), labeled "shape, not final code".
Alternatives considered
NO ALTERNATIVES — it is an external mandated dep from the parent RFC. Acceptable (no choice to make), but the integration mode (bundled vs CDN) is decided (§3.1 bundled, pinned) — good.
Grounding in existing code
NOT GROUNDED — grep @mekari/sdk returns only docs (verified). Agent must add the dep blind.
Interface specification
INCOMPLETE. Missing: package name on registry, version, CJS/ESM entry, constructor signature, how events are subscribed (callback? .on()? returned emitter?), teardown/destroy(), session.refresh() signature.
Failure handling
Partial — server_down mapped, origin-check specified. But what if new Session() throws / SDK fails to load? Not specified.
Challenge results
- Scale: n/a (single client instance).
- Reversibility: high — delete plugin+composable+dep.
- Consistency: consistent with
*.client.tsprecedent. - Agent implementability: NO — agent cannot
pnpm addan unverified package nor writevi.mockmatching an unknown surface. Would guess the event API → rework.
Gaps and suggestions
Missing: registry/access, version, entry format, event-subscription API, teardown.
Suggested resolution: A&L publishes @mekari/sdk@x.y.z (or pre-release) with a typed Session interface; paste the .d.ts excerpt into §2.2. Until then, stub a local types/mekari-sdk.d.ts contract so Chunks 2–3 can proceed against a frozen interface.
Open questions: Q2.
Decision: Dy — Cross-domain _mekari_account fallback boundary
Status: Dangling
What was decided
hub-chat consumes SDK events + its own msli; _mekari_account evaluated only inside SDK/iframe (§2.2). Flagged as possibly "structurally impossible" if hub-chat is expected to read the mekari cookie.
Grounding in existing code
Grounded in memory sso-cross-domain-cookies + COOKIE_DOMAIN=.qontak.com (verified claim in §2.0). The constraint is real.
Interface specification
INCOMPLETE — msli write/read is specified; the grace-vs-force decision logic at server_down is conditional on Q1's answer.
Failure handling
Partial — soft-grace path drawn (§2.3 failure diagram) but its validity is unconfirmed.
Challenge results
- Reversibility: medium — fallback logic touches the forced-logout path; wrong assumption = users wrongly signed out or wrongly kept in.
- Consistency: consistent with cross-tab logout precedent (
app.vue:216, verified). - Agent implementability: NO — agent would implement a grace window that A&L may reject. Must wait on Q1.
Gaps and suggestions
Missing: authoritative statement of which origin evaluates _mekari_account.
Suggested resolution: A&L confirms iframe-only evaluation in writing; then hub-chat implements msli-only grace exactly as §2.3. If hub-chat must read the cookie → redesign required.
Open questions: Q1.
Decision: D5 — logged_out/server_down → /logout
Status: Partial
logged_out→/logout is fully resolved (verified pages/logout.vue:315). server_down mapping is "suggested" and entangled with the Q1 soft-grace. Close by confirming the grace policy.
Decision: D7 — Current-company sync via store refetch
Status: Partial
Reuse of OrganizationStore.getDetail() + CompanyStore.getCompanyDetail() is sound, but correctness depends on hub-core setting current company server-side before refetch (Q4). If not, refetch returns stale company → silent wrong-org bug.
UI State Audit
This enhancement adds essentially no new data-driven component (SDK iframe invisible; reuses existing screens).
| Component | Loading | Empty | Error | Partial | Success | Assessment |
|---|---|---|---|---|---|---|
Re-auth loading (CommonSsoCallbackLoading) | reused | n/a | missing | n/a | reused | 3/5 — no error state if /sso-callback re-auth fails post switch_user |
| "User has changed" toast | n/a | n/a | n/a | n/a | defined (copy TBD) | 1/1 — copy is Q6 |
| Org/company refetch (background) | not defined | not defined | not defined | not defined | implicit | 0/5 — refetch failure after logged_in/switch_user has no defined UI |
Summary: The refetch-failure path (org/company store rejects after logged_in/switch_user) has no specified UI state. Add: on refetch error, behavior = retry? force logout? toast? Currently agent would leave a silent stale-org state.
Performance Budget Check
| Metric | Target | Current Baseline | Source | Assessment |
|---|---|---|---|---|
| SDK init blocking | "must not block first paint" | not stated | §3.2 | qualitative only — no ms target |
session.refresh() cadence | TBD | existing 1s tick (AuthStore.ts:509) | §3.2 / Q3 | MISSING — throttle interval unresolved, risk of hammering sm/current |
| Bundle size delta (SDK bundled) | not stated | — | §3.1 | MISSING — SDK is bundled (§3.1) but no kB budget |
| LCP / INP / CLS | not stated | — | — | n/a for this change, but bundle delta should be tracked |
NO QUANTIFIED PERF BUDGET for the one real perf lever (bundle delta from a bundled SDK + refresh cadence). Add a bundle budget once SDK size known (depends on Q2).
Accessibility Review
| Aspect | Specified? | Details | Assessment |
|---|---|---|---|
| Keyboard navigation | no | reuses existing redirect/logout | acceptable — no new interactive surface |
| Focus management | no | toast via notification:show | incomplete — toast focus/SR announcement not specified |
| ARIA labels | no | — | n/a — no new custom controls |
| Heading hierarchy | n/a | — | n/a |
| Color contrast | n/a | reuses Pixel toast | inherits Pixel |
| Motion sensitivity | n/a | — | n/a |
| Screen reader | no | "user has changed" toast | incomplete — confirm toast is announced (aria-live) via Pixel default |
Low blast-radius (reuses existing screens). Only gap: confirm the reused Pixel toast announces to SR; otherwise no a11y action needed.
Pattern Alignment Check
| Pattern | RFC Approach | Assessment |
|---|---|---|
| Feature-flag gating | follows seamless_auth_first (verified AppConfigStore.ts:7) | aligned |
| Client-only boot | follows *.client.ts (datadog precedent) | aligned |
| Cross-feature events | reuses useEventBus/AppEventMap (plugins/eventBus.ts:6) | aligned |
| Forced logout | reuses pages/logout.vue rather than re-implementing | aligned — avoids parallel cleanup path |
| Store error handling | "mirror CompanyStore.ts" | aligned but unspecified — which error pattern exactly? |
| Datadog RUM | follows datadogRum.setUser (app.vue:152) | aligned; action naming is new (Q7) |
| Analytics events preserved | no existing events dropped | aligned |
Strongest dimension. No parallel systems introduced.
Data Flow Trace
Flow: switch_user
SDK postMessage (different user_sso_id)
→ useCentralizedSession: verify event.origin === account.mekari.com [§3.3]
→ authStore.resetAuthStore() [verified pattern]
→ navigateTo(account.mekari.com/auth?return_to=/sso-callback)
→ /sso-callback?code=... → runSsoCallback → performSsoSignIn [middleware/sso-callback.ts:541, verified]
→ refetch OrganizationStore.getDetail() + CompanyStore.getCompanyDetail()
→ eventBus notification:show "user has changed" [copy TBD — Q6]
→ UI: home
Gaps in flow: (1) refetch-failure branch undefined; (2) current-company correctness assumes BE pre-set (Q4); (3) re-auth failure at /sso-callback has no defined recovery.
Strengths
- Scope + grounding discipline (§2.0, §4.C): every anchor is a verified
file:line(spot-checkedAuthStore.ts:42sso_id ✓,AppConfigStore.ts:7seamless_auth_first ✓,@mekari/sdkabsent ✓). Agent can navigate without searching. - Decision closure as ADRs (§2.6): 7 decisions with options, rejection rationale, reversibility — well above repo norm.
- Rollout/rollback realism (§4.A/§4.D): dark-by-default flag, numbered agent-executable rollback, RUM verification signals, correct sequencing behind Launchpad.
- Self-aware blocker honesty (§5/§7): the RFC flags its own [critical] gaps and refuses to set
status:yes— exactly right.
Biggest Gaps
- DEP/Q2 —
@mekari/sdkunverified (§5 Q2, grep-confirmed): the single load-bearing dependency has no version/registry/interface. Every code chunk (esp. 2,4) is non-compilable until resolved. This caps real-world readiness regardless of authoring quality. - CNT/Q1 — cross-domain fallback may be structurally impossible (§2.2, §5 Q1): if hub-chat is expected to read
_mekari_account, the design breaks; agent would build an invalid fallback. - FMC — no error-message catalog + undefined refetch-failure UI (§1.D, UI State Audit): toast copy TBD; org/company refetch failure has no specified behavior → silent stale-org risk.
Priority Actions
- Q2 / Decision Dx — A&L publishes/confirms the
@mekari/sdkpackage (name, version, registry access) and pastes theSession.d.ts(constructor, event subscription,refresh(), teardown) into §2.2. Unblocks Chunks 2–4 compilation andvi.mockfidelity. - Q1 / Decision Dy — Get written confirmation that
_mekari_accountis evaluated only inside the SDK/iframe; freeze themsli-only grace policy in §2.2. Unblocks Chunk 3 correctness. - FMC — Add an error-message catalog: the "user has changed" toast string/i18n key (Q6) and the behavior on org/company refetch failure (retry vs forced logout vs toast). Unblocks Chunk 3 + UI State completeness.
- Q4 + Q3 — Confirm hub-core sets current_company server-side before refetch (else add explicit FE call), and pin the
session.refresh()throttle interval. Removes the two remaining behavioral guesses.
Implementation Readiness Checklist
Unblocked (agent can proceed)
- PRD → RFC traceability matrix complete (§1.A)
- Rollout plan with feature flag + rollback (§4.A/§4.D)
- Observability metrics defined (§3.4, naming pending Q7)
- Task decomposition with acceptance criteria per chunk (§4.C)
- Pattern alignment verified (§2.0)
- Configuration contract:
centralized_sessionflag, default off - All interfaces/prop types fully specified — SDK API not pinned
- All UI states defined — refetch-failure + re-auth-failure states missing
- Performance budget quantified — refresh throttle + bundle delta missing
- Zero vague words — "suggested" (server_down), "TBD" (throttle, copy, naming)
Blocked (must fix first)
- Q2 —
@mekari/sdkSession package + interface unverified (no chunk compiles) - Q1 — cross-domain
_mekari_accountfallback semantics undefined (possible structural impossibility) - Q8 — approvers incl. mandatory infosec unassigned (CSP/iframe/postMessage)
Verdict: Fix 2 blockers first (Q1, Q2) — plus assign infosec approver (Q8) before sign-off.
Task Manifest
Verifying the RFC's own §4.C decomposition — it is sound; reproduced with dependency notes.
| Order | Chunk | Files to Create/Modify | Acceptance Criteria | Dependencies |
|---|---|---|---|---|
| 1 | Feature flag | common/store/AppConfigStore.ts (+centralized_session?), __tests__/AppConfigStore.spec.ts | type-check passes; field reads, undefined→off | None |
| 2 | useCentralizedSession + SDK lifecycle (TDD) | common/composables/useCentralizedSession.ts (+spec) | SDK constructed w/ current_user===sso_id; mismatched origin ignored; msli written on logged_in | Q2 (SDK interface) |
| 3 | event→action map (TDD) | extend composable + spec | logged_out/server_down→/logout; switch_user→reset+redirect; logged_in→refetch | Q1, Q4, Q6 |
| 4 | Plugin wiring (client-only) | plugins/mekariSession.client.ts (+spec) | SDK starts only when flag ON ∧ authed ∧ sso_id | Chunk 1,2; Q2, Q5 |
| 5 | Observability/RUM | composable (+spec) | RUM action per event, no token/sso_id in payload | Chunk 3; Q7 |
| 6 | Docs | docs/architecture/flows/login/README.md and/or auth-sso/README.md | spoke updated, status: ready, diagram added | Chunk 1–5 |
RFC specifies this decomposition; verified executable in order once Q1+Q2 close.
Dangling Decisions Log
| # | Decision | Location | Owner | Deadline |
|---|---|---|---|---|
| 1 | @mekari/sdk Session package identity + API surface | §2.1, §5 Q2 | Account & Launchpad | unset |
| 2 | Cross-domain _mekari_account fallback boundary | §2.2, §5 Q1 | A&L + Platform | unset |
| 3 | server_down final action (vs "suggested") + soft-grace policy | §2.6-A5, §2.2 | Platform (post Q1) | unset |
| 4 | Current-company sync ownership (FE call vs BE pre-set) | §2.6-A7, §5 Q4 | hub-core + A&L | unset |
Open Questions
| # | Question | Category | Severity |
|---|---|---|---|
| Q1 | Is _mekari_account evaluated only inside SDK/iframe (hub-chat consumes events+msli only)? | TDC/FMC | Blocking |
| Q2 | @mekari/sdk package name, registry/access, version, CJS/ESM entry, event API? | DEP/CNT | Blocking |
| Q3 | Agreed session.refresh() throttle interval? | NFS | Important |
| Q4 | Does hub-core set current_company server-side before refetch, or must FE call SSO? | TDC | Important |
| Q5 | Does centralized session apply to super_admin (today bypasses billing/MQTT)? | SCB/CPA | Important |
| Q6 | "Your account has changed" toast copy/i18n key? | FMC | Important |
| Q7 | Datadog custom action naming convention? | OBS | Nice-to-have |
| Q8 | Assign approvers incl. mandatory infosec? | ROL/Security | Important |
Evidence Notes
- §2.0 Existing Code Anchors / Source Verification — spot-checked and confirmed:
AuthStore.ts:42sso_id✓,AppConfigStore.ts:2/7AppConfig+seamless_auth_first✓,@mekari/sdkabsent from code (docs-only) ✓,listenForLogoutpresent ✓. Raised PRT/CPA/SCB scores. - §5 Concerns — Q1 and Q2 self-flagged as
[critical]; this is why overall is HOLD despite high authoring quality. Drove DEP=5.0 and the overall judgment. - §1.D / UI surfaces — no new visual surface beyond a TBD toast string; the absent refetch-failure UI state is the main FMC/UI-State deduction.
- §4.C/§4.D — concrete chunked plan + numbered rollback; raised TPS/ROL.