RFC Review: Centralized Web Session — Launchpad FE Integration
Executive Summary
Addendum (2026-07-02, latest review): This review scored the RFC's SDK contract (D5/D6, CNT, DEP) as validated but pending — treating "npm vs CDN" and the
_mekari_account-basedserver_downpredicate as open alternatives rather than checking them against the real SDK (mekari-account-web-sdkv0.3.0,session.ts/README.md). Neither the RFC nor this review consulted the SDK source. Both alternatives were wrong: the SDK is a bundled git dependency (no CDN ever existed) and it owns theserver_downfallback (msli) internally, so no consumer predicate is possible or needed. A wrong SDK contract validated as a strength (see "Best dimension of the RFC" and the CNT row, below) is a correctness blocker, not a pending dependency — this addendum re-rates accordingly. Scores, verdicts, and the priority-action list below have been updated in place; struck-through content marks what the latest review reconciliation removed.
- Overall Score:
7.0/10(was6.5/10— see addendum) - Rating:
Needs Work - RFC Type:
frontend - Sub-Type:
new-feature - Assessment Confidence:
Medium - Applied Caps/Gates:
None triggered as hard caps. NFS=4.0 (no quantified non-functionals) is the lone sub-5.0 category. Previously 3 critical open questions (Q1, Q2, Q3) were treated as blocking chunks 2/4/6; Q1 and Q2 are now RESOLVED by real-SDK verification (they were never genuinely open — the RFC's contract was simply wrong). Only Q3 (current-company endpoint, a genuine cross-layer BE dependency) still blocks. - Implementation Readiness Verdict:
HOLD — Q3 (current-company endpoint) blocks agentic execution of chunk 4. Q1 (SDK distribution) and Q2 (server_down predicate) are RESOLVED — they were correctness errors in the RFC's SDK contract, not pending decisions; see latest review. - Report Path:
/Users/mekari/Documents/qontak-launchpad-fe/rfc-review-report.md - RFC Author:
Account & Launchpad team| Reviewed:2026-06-28| Re-rated:2026-07-02 (latest review)
This RFC is unusually well-grounded for agentic execution on its closed parts: it cites real files with line numbers (authenticated.global.ts:80, useToggleQontakOne.ts:11, SwitchAccountContent.vue:117), names every file to create/modify, supplies a reading order, four ADRs, and (post-correction) a 6-chunk execution plan with per-chunk acceptance criteria. The biggest gap is that three explicitly-flagged critical decisions remain open and they gate the SDK loader, the current-company sync, and the Corrected: two of those three "critical decisions" were never real decisions — they were fabricated SDK contract details (server_down fallback — roughly half the feature.@mekari/sdk npm-vs-CDN; an _mekari_account-based server_down predicate) that the real SDK, mekari-account-web-sdk v0.3.0, simply does not have. Only current-company endpoint ownership (Q3) remains a genuine open cross-layer dependency. The one thing that must still change before full agentic execution: close Q3 with a concrete host/schema/failure contract. The now-deleted switch_user chunk (~2.5 FE days) is removed entirely — the real SDK has no such event; an account switch surfaces as logged_out.
Quick Verdict
Why this RFC can be implemented agentically:
- Every file is named with grounding line numbers; reading order + patterns-to-follow tables remove codebase guessing (§2.0).
- Chunks 1 (toggle), 3 (
logged_out, now also covering account switches), 6 (middleware) reuse existing builders (authenticated.global.ts:80,clearTokenLaunchpad,resetAuth) and have verifiable acceptance criteria. (The former chunk 5,switch_userre-auth + toast, is deleted — no such SDK event exists.)
Why this RFC will cause agent guessing or rework:
Q1/Q2/Q3 are critical and unresolved — chunks 2, 4, 6 are explicitly "blocked". Corrected: only Q3 (current-company endpoint) is a genuine blocker (chunk 4). Q1 (SDK distribution) and Q2 (server_downpredicate) are resolved by consulting the real SDK — an agent would not have had to invent the import path or a fallback predicate; those were review/RFC gaps, not RFC-external unknowns.- No error-message catalog and no quantified non-functionals — agent would ship without perf/a11y targets. (The "account changed" toast copy gap no longer applies — that toast is deleted along with
switch_user.)
PRD → RFC Traceability Matrix
PRD is a Confluence link (not fetchable in this review); scored against the RFC's own §1.A coverage table, which is bidirectional and explicit.
Standard format (PRD exists)
| PRD Element | RFC Section | Coverage |
|---|---|---|
| Overview / known issues | §1.0, §1.1 | Full |
| Success Criteria (FE portion) | §1.1, §2.x | Full |
| Out of Scope | §1.2 | Full |
| Dependencies (SDK, service, Redis) | §2.1 (upstream, read-only) | Full (referenced as upstream contracts) |
| Technical Design — Proposal | §2.2–§2.6 | Full (FE portion) |
| SDK contract / events | §2.3 | Full |
| FE Product Integration Flows | §2.4–§2.6 | Full |
| Web Session Flow | §2.5 | n/a — Launchpad uses OAuth2 authz-code, not Rails web session (justified) |
| OAuth2 Authorization Code Flow | §2.5, ADR-1 | Full |
| User Logout From Product | §2.6 | Full |
| User Switch Account | §2.3/§2.4 (surfaces as logged_out, no dedicated event) | Full — corrected: the real SDK has no switch_user event; an account switch runs the normal logged_out sign-out flow, so the "account changed" toast requirement and its missing copy/i18n gap no longer apply (latest review) |
| Database Model | — | n/a — no DB (FE) |
| HA & Security | §3 | Partial — FE subset; perf/a11y not quantified |
| Rollout Plan | §4 | Full (Launchpad = step 4) |
| Open Questions | §5 | Full (7 questions, severity-tagged) |
Summary: ~13 of 14 PRD elements fully covered (was 12; the "User Switch Account" row moved from Partial to Full once the fictional switch_user toast requirement was corrected away — latest review), 1 partial (non-functionals), 0 missing. No RFC decisions lack PRD justification. PRT = 8.0 (unchanged — the correction did not add or remove PRD coverage, it fixed a wrong toast requirement).
Scorecard
Frontend Scorecard (11 categories)
| Category | Score | Evidence-Based Rationale |
|---|---|---|
| PRT — PRD Traceability | 8.0 | §1.A coverage table is bidirectional and marks n/a items with justification (e.g. "Web Session Flow → Launchpad uses OAuth2"). PRD not fetchable, capping confidence. |
| TDC — Technical Decisions | 7.5 (was 6.0) | D1, D2, D4 resolved as full ADRs with options + rationale. D3 partial (source unresolved, Q4). D5 and D6 are now Resolved, not dangling — real-SDK verification shows mekari-account-web-sdk is a bundled git dependency (no npm-vs-CDN decision to make) and the server_down fallback is SDK-internal (no FE predicate to define). D7 (current-company) remains genuinely Dangling. latest review. |
| CNT — Contract Specificity | 7.0 (was 6.5) | new Session({current_user}), .on, .refresh; §2.1).currentUser (camelCase), the only valid event name is the literal "event" with a single-arg callback, and there is no .refresh(). The RFC (post-correction) now matches the real SDK. State Surface Contract table present (§2.3). /users/me/current_company still has no request/response schema; msli is no longer FE-surfaced at all (it's SDK-internal, R7), which removes the earlier "loosely specified" msli format complaint entirely. |
| SCB — Scope Boundaries | 8.5 | Repo Map + Existing Code Anchors + every new/modified file named (§2.0); non-goals explicit (§1.2). Agent can produce file-by-file plan from RFC alone. |
| DEP — Dependencies | 7.5 (was 6.5) | Deps named with status: mekari-account-web-sdk "NOT installed" — now correctly resolved to a git-dependency install (Q1, no longer blocked); current-company host still unresolved (Q3, genuinely blocked); toggle new. Only one key dependency remains blocked, down from two. |
| FMC — Failure Mode Coverage | 6.5 (was 5.5) | All 3 real events (logged_in, logged_out, server_down) have a reaction; hard client-side timeout noted (§3.1). server_down predicate is broken (Q2)server_down is now specified as fail-open per PRD 6.10 (no predicate needed, no consumer msli to evaluate). Still missing: an error-message catalog, current_company fetch-failure definition. Cap "FMC<7.0 needs error catalog" still applies — held below 7. |
| NFS — Non-Functional Specificity | 4.0 | Security/OWASP present (§3.2) and good. But no LCP/INP/CLS/bundle budget, no a11y spec, no browser matrix, no i18n. Service-side RPS deferred upstream (correct), FE targets absent. |
| TPS — Test Plan Specificity | 7.0 | Each chunk (§4.C) has a pnpm run test -- <spec> command + "red first" + concrete accept criteria (e.g. "toggle off → returns false"). Missing: enumerated failure-path tests per story. |
| ROL — Rollout & Rollback | 7.0 | Toggle-gated; rollout stages (§4.E) with go/no-go evidence; rollback recipe = flip toggle (§4.D); verification commands (§4.B). Config contract incomplete — toggle source/env name unresolved (Q4). |
| OBS — Observability | 5.5 | server_down fallback counter proposed; console.error/warn + useErrorHandler routing (§3.3). But telemetry sink [REQUIRED], no metric/dashboard names finalized. |
| CPA — Pattern Alignment | 8.5 | Patterns-to-Follow table maps each concern to a reference file (Pinia setup store, useToggleQontakOne:11, useClient:43, toast.notify:117). Reuses existing redirect/sign-out builders; introduces only new composables. |
Decision Closure Assessment
Decision Index
| # | Decision | Status | Critical Gaps |
|---|---|---|---|
| D1 | Auth model = OAuth2 authz-code | Resolved | — |
| D2 | SDK load = *.client.ts plugin | Resolved | — |
| D3 | Toggle = mirror useToggleQontakOne | Partial | toggle source (env vs backend) unresolved — Q4 |
| D4 | Event wiring = plugin→composable→mw/store | Resolved | touches shared middleware (medium reversibility) |
| D5 | SDK distribution | Resolved (was Dangling) | none — bundled git dependency (mekari-account-web-sdk v0.3.0), no CDN alternative ever existed. latest review/Q1. |
| D6 | server_down fallback | Resolved (was Dangling) | none — the SDK owns the msli fallback internally; no consumer predicate exists to define. latest review/Q2. |
| D7 | Current-company endpoint host/schema | Dangling | host/owner unknown, no schema — Q3 (genuine, unaffected by the SDK correction) |
Aggregate: 5 Resolved, 1 Partial, 1 Dangling (of 7) — was 3 Resolved, 1 Partial, 3 Dangling; D5/D6 moved to Resolved via real-SDK verification (latest review).
Decision: D1 — Auth model variant: OAuth2 Authorization Code
Status: Resolved
What was decided
"(b) OAuth2 authz-code flow with bearer tokens." (ADR-1, §2.3.1)
Alternatives considered
(a) web-session + client_credentials + Rails cookie — rejected: "no Rails session in this SPA." Grounded in ssoCallbackStore.ts:53 grantType: authorization_code.
Grounding in existing code
ssoCallbackStore.ts:53, global_sso_* cookies (useAuthCookies.ts:5-7). Fully grounded.
Interface specification
Variant isolates event handlers to token-revocation semantics. Sufficient.
Failure handling
Specified downstream in §2.4 handlers.
Challenge results
- Scale: N/A (per-user auth).
- Reversibility: High — "variant isolated to event handlers."
- Consistency: Consistent with D2/D4.
- Agent implementability: Yes.
Gaps and suggestions
None. Strong ADR.
Decision: D2 — SDK load location: client plugin
Status: Resolved
What was decided
"(b) app/plugins/mekariSession.client.ts." (ADR-2)
Alternatives considered
(a) app.vue useHead, (c) per-page import — rejected for single-init + browser-context guarantee. Grounded in plugins/auth.ts.
Interface specification
Init must be idempotent + toggle-gated (stated consequence).
Failure handling
Idempotency required but idempotency mechanism not specified (how is double-init prevented?). Update (latest review, R11): the real SDK is itself a singleton — a second new Session() call anywhere returns the first instance and silently ignores new options. This makes a bespoke idempotency guard largely unnecessary for preventing duplicate Session construction; the remaining concern is narrower — ensure session.destroy() (not just off()) is called before any re-init (e.g. on toggle flip), since off() alone leaves the iframe + window listener attached.
Challenge results
- Reversibility: High — delete plugin / flip toggle.
- Consistency: Consistent.
- Agent implementability: Yes (was "Mostly") — the SDK's own singleton behavior removes most of the guesswork; the agent only needs to call
destroy()on teardown, not invent a module-level guard from scratch.
Gaps and suggestions
Missing (reduced scope, latest review): explicit session.destroy() call on teardown/toggle-flip; the module-singleton guard against duplicate construction is no longer needed since the SDK enforces it.
Open questions: none blocking.
Decision: D3 — Toggle mechanism
Status: Partial
What was decided
Mirror useToggleQontakOne TOGGLE_SOURCE switch; "default source unresolved — Q4 (env var preferred)." (ADR-3)
Alternatives considered
(a) hardcoded const, (b) runtimeConfig env var, (c) backend per-company — all listed; final source not picked.
Grounding in existing code
useToggleQontakOne.ts:11. Grounded.
Interface specification
Incomplete: env var name, type, default value not given (config contract gap).
Failure handling
Toggle off → no side effects (testable). Adequate.
Challenge results
- Reversibility: High.
- Agent implementability: Partial — agent must guess env var name + default.
Gaps and suggestions
Missing: env var name + default; whether pilot uses env or backend.
Suggested resolution: pilot uses runtimeConfig.public.CENTRALIZED_SESSION (boolean, default false), mirroring existing public config keys in nuxt.config.ts. Flip to backend at PRD step 5.
Open questions: Q4 — confirm source + key name with AL.
Decision: D4 — Event wiring point
Status: Resolved
What was decided
"(b) Plugin forwards events to useCentralizedSession; middleware awaits initial resolution; store actions perform token/company changes." (ADR-4)
Grounding in existing code
authenticated.global.ts single gate. Grounded.
Interface specification
Middleware gains toggle-gated await with timeout (§3.1). Timeout value not numerically specified.
Failure handling
Slow /current → falls to server_down, which (post-correction, D6) is now a well-defined fail-open path with no broken predicate.
Challenge results
- Reversibility: Medium — "touches the shared middleware" (regression surface).
- Consistency: Consistent.
- Agent implementability: Mostly — agent must pick the timeout number.
Gaps and suggestions
Missing: concrete await timeout (ms).
Suggested resolution: 3000ms client-side timeout → emit synthetic server_down; assert existing authenticated.global.spec stays green when toggle off.
Open questions: none blocking.
Decision: D5 — SDK distribution
Status: Resolved (was Dangling — corrected 2026-07-02, latest review)
What was decided
NOT DECIDED — "unresolved — see Open Question Q1." Two upstream contracts both listed (§2.1): npm
Corrected: there was never a real "npm vs CDN" decision to make — no CDN for this SDK has ever existed. The real package is @mekari/sdk and CDN https://account.mekari.com/sm/sdk.js.mekari-account-web-sdk v0.3.0, installed as a bundled git dependency (npm install git+https://<user>:<pass>@bitbucket.org/mid-kelola-indonesia/mekari-account-web-sdk#<version>) and imported via import { Session } from "mekari-account-web-sdk". This finding came from reading the real SDK's README.md/src/index.ts (latest review), which neither the original RFC nor this review consulted.
Alternatives considered
Both named, neither rejected. Corrected: there was no genuine alternative; "CDN" was a fabricated option.
Grounding in existing code
package.json — mekari-account-web-sdk absent (§2.0.1), to be added via the git-dependency install above.
Interface specification
SDK shape verified against real source (session.ts): new Session({ currentUser, sessionUrl?, interval?, checkTimeout?, includeSessionId? }), session.on("event", (data) => {}), session.off(), session.destroy() — no .refresh().
Failure handling
No CDN script-load path exists or is needed — the SDK is bundled at build time. server_down (SDK-internal fallback exhausted) is the only runtime failure mode; see D6.
Challenge results
- Reversibility: N/A — single supported distribution mechanism, not a reversible choice.
- Agent implementability: Yes (was "No") — the import path is now fully specified.
Gaps and suggestions
None remaining. The original review's "prefer npm, CDN as fallback" suggestion is now moot and removed — the fallback branch it recommended was never real. Open questions: none — Q1 resolved.
Decision: D6 — server_down fallback predicate
Status: Resolved (was Dangling — corrected 2026-07-02, latest review)
What was decided
Predicate: "msli < 2h AND
Corrected: there is no consumer-side predicate to define. The real SDK owns the _mekari_account valid" — but RFC itself marks the second clause "[UNREADABLE cross-domain — Q2]" in the failure sequence diagram (§2.4).msli localStorage key (2h expiry) internally — it sets it on logged_in, clears it on logged_out, and uses it itself to decide whether a checkTimeout lapse should emit logged_in (fresh msli) or server_down (stale/absent). Launchpad only ever receives the final logged_in/logged_out/server_down status; it must NOT read or write the msli key itself (doing so would collide with the SDK's own bookkeeping and corrupt its fallback — R7). On server_down, the product-level contract is simply fail-open per PRD 6.10: take no destructive action, do not force sign-out.
Alternatives considered
None needed — there is nothing left for a consumer predicate to decide.
Grounding in existing code
useAuthCookies.ts has no _mekari_account (§2.0.1, §3.2 — the old A01 finding is now moot, since the premise that Launchpad needed to read it was itself wrong). Real SDK: session.ts — msli read/write confined to SDK internals.
Interface specification
server_down is a terminal, fully-specified status: FE fail-open, log/observe only.
Failure handling
No longer "the failure-handling path itself unimplementable" — it is now a one-line no-op-plus-log branch.
Challenge results
- Scale: N/A.
- Reversibility: High — fail-open is a single, easily-reversible branch.
- Consistency: No longer conflicts with anything — the removed
_mekari_accountpremise was the source of the prior contradiction. - Agent implementability: Yes (was "No") — an agent can implement fail-open trivially; there is no predicate to invent.
Gaps and suggestions
None remaining. The original review's suggested resolution — now - msli < 2h && global_sso_valid_until > now, i.e. a consumer-side msli read/write — is itself now known to be WRONG and must NOT be implemented: the SDK owns the msli key, and a consumer localStorage.setItem("msli", …) would collide with and corrupt the SDK's own fallback bookkeeping. This suggestion is struck from the priority-action list below.
Open questions: none — Q2 resolved.
Decision: D7 — Current-company endpoint host/schema
Status: Dangling
What was decided
New useCurrentCompany.ts calls /users/me/current_company via useClient, but "endpoint owner/host is an Open Question (Q3) — do not hardcode api.mekari.com." (§2.5)
Alternatives considered
Kong route vs BE proxy vs direct api.mekari.com — listed, unresolved.
Grounding in existing code
grep current_company → no matches (§2.0.1). Existing profile via authStore.ts:81 (/users/me) through Kong.
Interface specification
Missing: host, CORS/Kong routing, response schema (company id/name/fields).
Failure handling
Fetch failure (CORS reject, 4xx/5xx) behavior undefined.
Challenge results
- Reversibility: Medium.
- Agent implementability: No — agent must guess host and response shape; per RFC's own warning, hardcoding
api.mekari.comis wrong.
Gaps and suggestions
Missing: host, schema, failure behavior.
Suggested resolution: route through existing Kong base (apiBaseUrl, as authStore.ts:81 does) via a Launchpad BE proxy; define response { data: { current_company: { id, name } } } read at data.value.data per repo convention; on failure route through useErrorHandler and keep prior company.
Open questions: Q3 — Launchpad BE to own/confirm host + schema.
UI State Audit
5 states per data-driven surface.
| Surface | Loading | Empty | Error | Partial | Success | Assessment |
|---|---|---|---|---|---|---|
| Session gate (middleware await) | partial (await + timeout, no spinner spec — §1.3 marked [REQUIRED]) | n/a | defined (→server_down, fail-open) | n/a | defined (allow nav) | 2.5/5 |
| Current-company fetch | missing | missing (no company?) | missing | missing | defined (set on store) | 1/5 |
Removed: the "Account changed" toast row — the real SDK has no
switch_userevent, so this surface no longer exists (latest review, R5).
Summary: 0 of 2 remaining surfaces have all states (was 0 of 3). Loading visual is [REQUIRED] (open); current-company fetch states entirely absent — agent will build happy-path only.
Performance Budget Check
| Metric | Target | Current Baseline | Source | Assessment |
|---|---|---|---|---|
| LCP | not stated | not stated | — | missing |
| INP | not stated | not stated | — | missing |
| CLS | not stated | not stated | — | missing |
| Bundle size delta (SDK) | not stated | not stated | — | missing — distribution is now known (git dependency, Q1 resolved), but the delta itself is still unmeasured |
| Middleware await timeout | "hard client-side timeout" (§3.1), no number | — | RFC prose | vague |
NO QUANTIFIED PERFORMANCE BUDGET — agent cannot optimize against targets; SDK bundle delta unmeasured (distribution mechanism is resolved, Q1, but no size measurement exists yet).
Accessibility Review
| Aspect | Specified? | Details | Assessment |
|---|---|---|---|
| Keyboard navigation flow | no | — | missing |
| Focus management | no | loading-gate focus undefined (the "account changed" toast surface no longer exists — R5) | missing |
| ARIA labels | no | — | missing |
| Heading hierarchy | no | — | missing |
| Color contrast | n/a | no toast surface remains in scope (removed with switch_user, R5) | n/a |
| Motion sensitivity | no | — | missing |
| Screen reader behavior | n/a | no toast surface remains in scope | n/a |
A11y is largely unaddressed for the one remaining FE-visible surface (the loading gate, [REQUIRED] — no focus/announce spec). The "account changed" toast that previously anchored several rows here is removed: its trigger (switch_user) does not exist in the real SDK (R5).
Pattern Alignment Check
| Pattern | RFC Approach | Assessment |
|---|---|---|
| State management (Pinia setup store) | follows (authStore.ts) | strong |
| Feature toggle | extends (useToggleQontakOne.ts:11) | strong — but source unresolved (Q4) |
API call (useClient) | follows (useClient.ts:43, body at data.value.data) | strong |
| Auth cookies | reuse (useAuthCookies.ts) | strong |
| SSO re-auth / sign-out redirect | reuse (authenticated.global.ts:80, SwitchAccountContent.vue:117) | strong |
| Toast notification | @mekari/pixel3 toast.notify (authStore.ts:117) remains available as a pattern | n/a for this RFC — its only proposed trigger (switch_user) does not exist in the real SDK, so no toast is wired by this feature (R5) |
| Error normalize | follows (useErrorHandler) | strong (per AGENTS.md) |
Best dimension of the RFC — no parallel systems introduced; new composables only.
Agentic Readiness Deep-Dive
Vague Word Audit
| # | Word/Phrase | Location | Impact | Concrete Replacement |
|---|---|---|---|---|
| 1 | "hard client-side timeout" | §3.1 | agent guesses ms | "3000ms then synthetic server_down" |
| 2 | "non-blocking when toggle off" | §3.1 | OK but unverified | assert existing middleware spec green with toggle off |
| 3 | removed — no longer applicable: the toast and its switch_user trigger do not exist in the real SDK (R5) | n/a | ||
| 4 | removed — no longer applicable: msli is SDK-internal; Launchpad never writes it (R7) | n/a | ||
| 5 | "idempotent" init | ADR-2 | narrowed impact — the SDK's own singleton behavior (R11) covers duplicate-construction; only session.destroy() on teardown is still unstated | explicit session.destroy() call on toggle-flip/unmount |
Total vague words in spec sections: 2 (was 5 — two items were struck because they described features that don't exist in the real SDK, one narrowed by the SDK's singleton behavior)
Dangling Alternatives
| # | Alternatives | Location | Impact |
|---|---|---|---|
| 1 | resolved — no CDN exists; bundled git dependency (latest review) | ||
| 2 | env var vs backend toggle source | ADR-3 / Q4 | agent guesses config key/default |
| 3 | Kong vs api.mekari.com vs BE proxy | §2.5 / Q3 | agent guesses host (chunk 4) |
| 4 | /sm/current vs /sessionmanager/current | resolved — https://sm.mekari.com/current (SDK's sessionUrl default, latest review) |
Total dangling alternatives: 2 (was 4 — #1 and #4 resolved by real-SDK verification, not by a product decision)
Task Decomposition Assessment
| Chunk | Acceptance Criteria | Assessment |
|---|---|---|
| 1 toggle | toggle off→false, on→true; red first | verifiable (blocked Q4 for source) |
| 2 SDK loader | new Session({currentUser}) with launchpad.sso_id (mock) | verifiable — not blocked (Q1, Q5 resolved) |
3 logged_out | clearTokenLaunchpad+resetAuth+sign_out redirect; also fires for account switches (R5) | verifiable |
4 logged_in+company | company fetched/set (no msli write — that's SDK-internal, R7) | verifiable but blocked Q3 |
switch_user | deleted — no such SDK event exists (R5); ~2.5 FE days removed from scope | |
5 server_down (renumbered from 6) | fail-open (PRD 6.10): no destructive action, log/observe only | verifiable — not blocked (Q2 resolved; no predicate to build) |
| 6 middleware (renumbered from 7) | toggle on→await+timeout; off→specs green | verifiable; timeout number missing |
Order is sound (toggle→loader→handlers→middleware). 1 of 6 chunks blocked (was 3 of 7) — only chunk 4 (current-company) remains blocked, on Q3.
Strengths
- Scope grounding (§2.0, SCB 8.5): Repo Map + Existing Code Anchors + Reading Order with real line numbers (
authenticated.global.ts:80,useToggleQontakOne.ts:11) — agent skips codebase discovery entirely. - Pattern alignment (§2.0 Patterns-to-Follow, CPA 8.5): every concern mapped to a reference file; reuses existing redirect/sign-out builders; introduces only new composables — generated code will look native. (The toast-notification pattern was previously listed here too, but its only proposed trigger,
switch_user, does not exist in the real SDK — R5 — so it is no longer part of this RFC's scope.) - Honest decision tracking (§1.B, §5, §7): ADR index, severity-tagged open questions, and a
Ready: nogate. Two of the three originally-cited blockers (Q1, Q2) turned out to be correctness errors in a fabricated SDK contract rather than genuine open decisions — the gate should have needed only Q3 all along, once the real SDK is consulted. Source Verification table (§2.0.1) backs claims with greps, but did not extend to the SDK's own source, which is what let the wrong Q1/Q2 framing stand.
Biggest Gaps
Three critical dangling decisions (Q1/D5, Q2/D6, Q3/D7): block chunks 2, 4, 6— corrected: only one genuine dangling decision remains, Q3/D7 (current-company endpoint), blocking chunk 4. Q1/D5 and Q2/D6 were not real product decisions — the real SDK is a bundled git dependency (no CDN choice) and owns itsserver_downfallback internally (no predicate to define). The original review's belief that D6 was "a logic contradiction" was correct in diagnosis but wrong in remedy — the fix is not a redefined predicate, it's removing the predicate concept entirely.- No error-message catalog + no current_company schema (CNT 7.0, FMC 6.5 — both improved from 6.5/5.5): toast copy/i18n gap no longer applies (toast removed with
switch_user, R5); current_company still has no response shape or fetch-failure behavior — agent still invents this part. - No quantified non-functionals (NFS 4.0, unchanged): no LCP/INP/CLS/bundle budget, no a11y spec, no browser matrix, no timeout number — agent ships unmeasured. Unaffected by the SDK correction.
Priority Actions
Do these before handing to an AI agent.
Q1 / D5 (SDK distribution) — pick npmRESOLVED (latest review):@mekari/sdk... or CDN ...mekari-account-web-sdkv0.3.0, bundled git dependency; no action needed.Q2 / D6 (RESOLVED (latest review) — and the suggested predicate above must NOT be built: the SDK ownsserver_downpredicate) — redefine FE-readable:now - msli < 2h && global_sso_valid_until > now(drop_mekari_account).msliinternally; a consumer write to that key collides with and corrupts the SDK's own fallback bookkeeping. Implementserver_downas fail-open (PRD 6.10) instead.- Q3 / D7 (current-company endpoint) — name host (prefer Kong
apiBaseUrlproxy asauthStore.ts:81), give request/response schema (read atdata.value.data), and fetch-failure behavior viauseErrorHandler. Unblocks chunk 4. Still open. - Non-functionals — add the middleware await timeout (ms) and minimal perf/a11y targets (SDK bundle delta, loading-gate focus/announce spec). Lifts NFS above gate. (The former "toast i18n key" item is removed — no toast remains in scope.)
Implementation Readiness Checklist
Unblocked (agent can proceed)
All types:
- PRD → RFC traceability matrix complete (§1.A)
- All technical decisions resolved (1 dangling: D7 — was 3: D5/D6/D7; D5/D6 resolved latest review)
- All failure modes handled with error message catalog (no catalog;
server_downis now well-defined fail-open, no longer broken) - Configuration contract (toggle source/env key unresolved — Q4)
- Pattern alignment verified (§2.0)
- Rollout plan with flag + rollback (§4.D/§4.E)
- Observability metrics/alerts defined (sink
[REQUIRED]) - Task decomposition with acceptance criteria per chunk (§4.C)
- Zero vague words in spec sections (2 found, was 5)
Frontend:
- All interfaces/prop types specified (current_company schema missing)
- All UI states defined (0/2 remaining surfaces complete, was 0/3 — toast surface removed)
- Performance budget quantified
- Accessibility requirements specified
- Browser support matrix defined
Blocked (must fix first)
-
Q1 — SDK distribution mechanism (chunk 2)RESOLVED (latest review): bundled git dependency. -
Q2 — FE-readableRESOLVED (latest review): no predicate — SDK ownsserver_downpredicate (chunk 6)msliinternally; FE fails open. - Q3 — current-company endpoint host + schema (chunk 4)
Verdict: Fix 1 blocker first (was "Fix 3 blockers first") — chunks 1, 2, 3, 5, 6 are agent-ready today; only chunk 4 (current-company) is blocked, on Q3.
Task Manifest
Verified against RFC §4.C; ordering retained; chunk 5 (
switch_user) deleted and subsequent chunks renumbered to match the corrected RFC (latest review). Blocked chunks flagged.
| Order | Chunk | Files to Create/Modify | Acceptance Criteria | Dependencies |
|---|---|---|---|---|
| 1 | centralized_session toggle | app/common/composables/useCentralizedSessionToggle.ts + spec | off→false, on→true; spec red first | Q4 (source/key) |
| 2 | SDK loader plugin | app/plugins/mekariSession.client.ts + spec | toggle on + launchpad.sso_id → new Session({currentUser}) (mock); off → not loaded | none — Q1, Q5 resolved |
| 3 | logged_out handler | app/common/composables/useCentralizedSession.ts + spec | data.status === "logged_out" → clearTokenLaunchpad+resetAuth+SSO_URL/sign_out redirect; also fires for account switches (R5) | Chunk 1 |
| 4 | logged_in + company sync | app/common/composables/useCurrentCompany.ts + spec; extend useCentralizedSession.ts | company fetched via useClient, set on store (no msli write — SDK-internal, R7) | Q3, Chunk 3 |
switch_user re-auth + toast | useCentralizedSession.ts; modify authenticated.global.ts | DELETED (R5) — no such SDK event exists; ~2.5 FE days removed | — | |
| 5 (was 6) | server_down fallback | extend useCentralizedSession.ts + spec | data.status === "server_down" → fail-open (PRD 6.10), log/observe only | none — Q2 resolved, Chunk 4 |
| 6 (was 7) | middleware integration | app/middleware/authenticated.global.ts | on → await + timeout; off → existing specs green | Chunks 1–5; timeout value |
RFC specified this decomposition; reviewer confirms ordering and dependencies. Author must close Q3 before chunk 4 executes (Q1, Q2 resolved latest review; the former chunk 5 is deleted, not blocked).
Dangling Decisions Log
| # | Decision | Location | Owner | Deadline |
|---|---|---|---|---|
| 1 | §1.B D5 / §2.1 / Q1 | — | RESOLVED (latest review) — bundled git dependency, no CDN | |
| 2 | server_down FE-readable predicate | §2.4 / §3.2 A01 / Q2 | — | RESOLVED (latest review) — SDK owns msli internally; FE fails open |
| 3 | Current-company endpoint host/schema | §2.5 / Q3 | Launchpad BE | unset — genuinely open |
| 4 | Toggle source (env vs backend) + key/default | ADR-3 / Q4 | AL | unset |
| 5 | /sm/current vs /sessionmanager/current | §2.1 / Q5 | — | RESOLVED (latest review) — https://sm.mekari.com/current |
| 6 | CSP / SDK iframe hardening (whitelist sm.mekari.com, not account.mekari.com) | §3.2 / Q6 | infosec | unset |
| 7 (new) | SDK does not validate event.origin — upstream infosec finding, not a decision to make | §3.2 A08/A07 (R9) | infosec + SDK owners | unset |
Open Questions
| # | Question | Category | Severity |
|---|---|---|---|
| 1 | @mekari/sdk or CDN sdk.js? | TDC/DEP | RESOLVED — mekari-account-web-sdk v0.3.0, bundled git dependency; no CDN exists (latest review) |
| 2 | server_down predicate (drop _mekari_account)? | FMC | RESOLVED — no predicate; SDK owns msli internally, FE fails open per PRD 6.10 (latest review) |
| 3 | Current-company endpoint host + response schema + failure behavior? | CNT/DEP | Blocking (unchanged — genuine cross-layer dependency) |
| 4 | Toggle source + env key + default for pilot? | TDC/ROL | Important |
| 5 | TDC | RESOLVED — https://sm.mekari.com/current (latest review) | |
| 6 | Launchpad CSP vs SDK iframe; whitelist sm.mekari.com via frame-src/child-src | NFS (security) | Important |
| 7 | Visible loading state while SDK resolves? | UI State | Nice-to-have |
| 8 | CNT/FMC | REMOVED — no toast remains in scope; its trigger (switch_user) does not exist in the real SDK (R5) | |
| 9 | Middleware await timeout value (ms)? | TDC/NFS | Important |
| 10 (new) | SDK's message listener does not validate event.origin — track as an open upstream infosec finding for the SDK owners, not a question the RFC author can answer | NFS (security), OWASP A08/A07 | Important |
Evidence Notes
- §2.0 Repo Reading Guide + §2.0.1 Source Verification — strong evidence via repo greps (
mekari-account-web-sdkabsent,current_companyno matches), but this table did not extend to consulting the real SDK's own source, which is what let the fabricated npm-vs-CDN and_mekari_accountpredicate stand as "dangling decisions" instead of being identified as wrong contract claims. latest review closed that gap by readingmekari-account-web-sdkv0.3.0'ssession.ts/README.mddirectly. - §1.B Decisions + §5 Open Questions — originally 3 critical dangling decisions held TDC to 6.0 and overall to 6.5; now 1 genuine dangling decision (D7/Q3) with TDC at 7.5 and overall at 7.0 (latest review).
- §3 HA & Security — OWASP-aligned (A02/A05/A07) but no quantified FE perf/a11y → NFS 4.0 (unchanged). New: an A08/A07 finding that the SDK does not validate
event.origin(R9), which no FE-side fix can address — tracked as an open upstream item, not a scoring factor for this RFC. - §4.C Agent Execution Plan — per-chunk test commands + accept criteria gave TPS 7.0; originally 3 of 7 chunks self-flagged blocked, now 1 of 6 (chunk 4, Q3) after chunk 5 (
switch_user) was deleted and Q1/Q2 resolved. - Absence: error-message catalog + current_company schema + UI states — previously capped FMC at 5.5 and CNT at 6.5; now FMC 6.5 / CNT 7.0 since the
server_downpredicate and SDK contract are no longer broken, though the current_company schema gap persists.