Skip to main content

Task Breakdown — Centralized Web Session (CRM Frontend Integration of @mekari/sdk Session)

Source RFC: crm/centralized-web-session.md (status: RFC — not yet Ready for agent execution) Slicing mode: Vertical (1 task = 1 story end-to-end: UI + integration + tests; merging rule applied) Blocked-task handling: Full-picture — blocked tasks shown inline with unblock conditions; full-scope Skipped table at end. Target repo (verified): /Users/mekari/Documents/crm (Nuxt 2 SPA). Test runner: jest (yarn test = jest test -u; single file = yarn test-file <path>). Lint: yarn lint:js. Build: yarn build (nuxt build).


Reconnaissance notes (grounding for every path below)

CheckResult
Test runnerjest, not vitest. Run-one: yarn test-file <path>; all: yarn test. Lint: yarn lint:js; build: yarn build. (verified in package.json scripts)
Test layouttests/ mirrors source dirs (tests/store/, tests/utils/helpers/, tests/middleware/, …). Specs are *.spec.js, not co-located, not TypeScript.
tests/plugins/Does not exist yet — must be created for the plugin spec. Verified: no tests/plugins/ dir, no existing plugin test anywhere.
@mekari/sdkAbsent from package.json (only @mekari/pixel@^1.1.14). Net-new dependency — install blocked on registry availability (Q6).
@datadog/browser-rumPresent @^4.34.0. datadogRum imported in plugins/datadog-rum.js; addAction/addError not yet used anywhere in the repo.
Cited source filesAll verified present: schemes/crmAuthScheme.js, utils/helpers/auth.js, store/user.js, middleware/crm-user.js, plugins/datadog-rum.js, nuxt.config.js, assets/variables/endpoints.js, adapters/http/utils.js, plugins/auth.js, middleware/redirect-to-v3.js, plugins/mixpanel.js, utils/helpers/package-features.js.
store/user.js anchorsuserLogout:120, deleteSsoCookies:144, deleteUserLocalData:157, getCustomFeature:67, custom_features state:12, SET_CUSTOM_FEATURE:349. All confirmed.
nuxt.config.js pluginsBlock at line 58. Order: ~/plugins/auth (59) → ~/plugins/auto-token-refresh (60) → … ~/plugins/datadog-rum (66). New plugin registers after auto-token-refresh.
Refresh timerutils/helpers/auth.js: setupAutoTokenRefresh:157, REFRESH_BEFORE_EXPIRY_MINUTES=10:13, clearAutoTokenRefresh:248. SDK must not fight this.
Toast componentUnverified — check repo. No global $toast plugin found. Existing toast pattern = a toastCallback({...}) injected into setupAutoTokenRefresh (utils/helpers/auth.js:203). Exact switch_user toast API is genuinely unconfirmed (RFC Q5).
user_sso_idUnverified / absent. Only external_company_id exists (plugins/mixpanel.js:25, company-level). No user SSO id field confirmed (RFC Q2).
_mekari_account cookieRead via js-cookie Cookies.get (already imported in schemes/crmAuthScheme.js:2). CRM reads presence only, never writes.

Effort Summary

Vertical mode — one row per story task. Days are man-days. QA = 20–25% of dev effort for user-facing behaviour, min 0.5; 0 for internal-only.

TaskStoryStatusFE daysBE daysQA daysTotal
1. msli fallback helperS6✅ Actionable1.50.52.0
2. SDK plugin scaffold + toggle gate + origin checkS1, S2⚠️ Partially blocked2.00.52.5
3. SDK event handlers (4 events) + msli cleanupS3⚠️ Partially blocked2.00.52.5
4. Logout → SSO sign_out redirectS5✅ Actionable1.00.51.5
5. Datadog RUM observabilityS7✅ Actionable0.500.5
6. Add @mekari/sdk dependencyS1 (dep)🚫 Blocked0.500.5
7. switch_user proper SSO-autologin flowS3/Q1🚫 Blocked3.00.53.5
8. current_company syncS4🚫 Blocked1.02.00.53.5
Grand total11.52.03.517.0
Actionable-now subtotal (Tasks 1–5)7.02.09.0

Confidence: low. Four [critical] open questions (Q1–Q4) gate the RFC's own "Ready for agent execution: no" marker. The biggest movers: (a) user_sso_id source is unverified (Q2) — without it the Session constructor in Task 2 can't be filled, so Task 2 ships behind a stub; (b) the exact centralized_session feature code string is unverified (Q3); (c) the real switch_user flow (Task 7) and current_company sync (Task 8) have no in-repo contract and are estimated coarsely. Tasks 1, 4, 5 are genuinely actionable today; Tasks 2 and 3 are buildable as shells now with the SDK call/user_sso_id stubbed. The toast component (Q5) is unconfirmed and will move Task 3 slightly.


Task 1: [FE] msli localStorage fallback helper (S6)

A CRM user keeps their session (degraded) when the Session Manager is briefly unreachable, instead of being wrongly signed out — and is signed out only when the fallback genuinely expires.

Status: ✅ Actionable — pure client-side logic, no SDK and no user_sso_id needed; _mekari_account is read-only via the already-imported js-cookie.

Design reference: n/a — no Figma (RFC §1.4; this task has no visible UI).

What to build

A new pure helper module that reads/writes the msli localStorage timestamp and reads _mekari_account cookie presence, and exposes a function that, given "now", returns the fallback decision: logged_in (msli < 2h and cookie present), logged_out (msli > 2h or cookie absent), or server_down (no msli at all). This is the decision engine consumed by Task 3's server_down handler.

Implementation Plan

ActionFileWhat changes
createutils/helpers/mekari-session.jssetMsli(now), clearMsli(), readMsli(), getFallbackDecision(now, { idleWindowMs = 2*60*60*1000 }) returning 'logged_in' | 'logged_out' | 'server_down'; reads _mekari_account via Cookies.get
createtests/utils/helpers/mekari-session.spec.jsUnit tests for each decision branch + set/clear/read round-trip

File path rule: utils/helpers/mekari-session.js matches the existing utils/helpers/ convention (siblings: auth.js, package-features.js). Spec mirrors to tests/utils/helpers/ per the verified layout (tests/utils/helpers/auth.spec.js exists).

Implementation steps

  1. Explore the codebase area — Open utils/helpers/auth.js (verified) and tests/utils/helpers/auth.spec.js to copy the module style (plain ES exports, Date math like setupAutoTokenRefresh) and the jest spec style. Open schemes/crmAuthScheme.js:2 to copy the import Cookies from 'js-cookie' form for the cookie read.
  2. Write failing tests (red) — Create tests/utils/helpers/mekari-session.spec.js. Cover: msli age < 2h + cookie present → 'logged_in'; msli age > 2h → 'logged_out'; cookie absent → 'logged_out'; no msli key → 'server_down'; setMsli/clearMsli/readMsli round-trip. Mock localStorage and Cookies.get. Run yarn test-file tests/utils/helpers/mekari-session.spec.js and confirm failure.
  3. Scaffold — Create utils/helpers/mekari-session.js with the four exported functions; constants MSLI_KEY = 'msli', MEKARI_ACCOUNT_COOKIE = '_mekari_account', IDLE_WINDOW_MS = 2 * 60 * 60 * 1000.
  4. Wire statereadMsli parses localStorage.getItem('msli') to a number; getFallbackDecision compares now - msli against idleWindowMs and Cookies.get('_mekari_account') presence.
  5. Implement behavior — Fill the branch logic exactly per §2.5 failure-path diagram and §2.4 server_down row.
  6. Go greenyarn test-file tests/utils/helpers/mekari-session.spec.js until all pass.
  7. Quality gateyarn lint:js && yarn build.

Acceptance criteria

  • msli age < 2h and _mekari_account present → returns logged_in.
  • msli age > 2h or _mekari_account absent → returns logged_out.
  • no msli key present → returns server_down.
  • setMsli(now) writes a numeric timestamp; clearMsli() removes the key; both verified by re-reading.

Test strategy

Pure-function unit tests. Mock window.localStorage (jest jsdom default) and stub Cookies.get to return/omit _mekari_account. Key assertion: each of the three decision strings is produced for its exact precondition; freeze now to make the 2h boundary deterministic (test exactly at 2h ± 1ms).

Effort estimate

DisciplineDays
Frontend1.5
Backend
QA0.5
Total2.0

Assumptions: pure helper, no SDK dependency, reuses existing js-cookie import; jsdom localStorage available in jest (verified test env). No new package.

Run to verify

yarn test-file tests/utils/helpers/mekari-session.spec.js && yarn lint:js

Depends on

  • None. This is the recommended first task.

Task 2: [FE] SDK plugin scaffold + centralized_session toggle gate + postMessage origin validation (S1, S2)

When the centralized_session toggle is on for the user's company, CRM loads the @mekari/sdk Session on every authenticated page with the current user's SSO id; when the toggle is off (or unreadable), CRM behaves exactly as today.

Status: ⚠️ Partially blocked — the plugin shell, toggle gate, registration order, and event.origin guard are all buildable now with the Session constructor and user_sso_id stubbed. Blocked pieces: real new Session({...}) import awaits Task 6 (@mekari/sdk not installed, Q6); the user_sso_id field source is unverified (Q2); the exact toggle code string is unverified (Q3). Build the shell with these stubbed and a clearly-marked TODO.

Design reference: n/a — design pending; no Figma (RFC §1.4). This task has no visible UI surface (the toast lives in Task 3).

What to build

A new Nuxt plugin that runs after ~/plugins/auth + ~/plugins/auto-token-refresh, reads the centralized_session toggle from store.state.user.custom_features, no-ops when the toggle is off/absent/errored (fail-closed), and when on, constructs the SDK Session with user_sso_id and registers a single guarded message listener that rejects any postMessage whose event.origin !== 'https://account.mekari.com'. Event-to-action mapping is added in Task 3.

Implementation Plan

ActionFileWhat changes
createplugins/mekari-session.jsDefault-export init fn (ctx); reads toggle via ctx.store.state.user.custom_features.find(f => f.code === 'centralized_session' && f.enabled); stub getUserSsoId(ctx) (TODO Q2); guarded Session construction (stubbed import until Task 6); origin-validated listener registration
extendnuxt.config.jsAdd '~/plugins/mekari-session' to plugins[] (line 58 block) after '~/plugins/auto-token-refresh'
createtests/plugins/mekari-session.spec.jsNew file (and tests/plugins/ dir — does not exist yet); tests toggle-on/off/error gating, origin validation, no Session when off

File path rule: plugins/mekari-session.js matches the flat plugins/ convention (siblings: datadog-rum.js, mixpanel.js, auth.js). tests/plugins/ must be created — it does not exist in the repo today (verified). '~/plugins/mekari-session' registration string matches the existing '~/plugins/...' entries in nuxt.config.js:58.

Implementation steps

  1. Explore the codebase area — Open plugins/datadog-rum.js (verified) for the conditional-gate + default-export-init-fn pattern (if (process.env.DD_ENABLED === 'true')), and plugins/auth.js for $auth/store access via the Nuxt ctx. Open middleware/redirect-to-v3.js:49 for the exact store.state.user.custom_features.find(f => f.code === …) read shape. Confirm nuxt.config.js:58–71 plugin order.
  2. Write failing tests (red) — Create tests/plugins/ then tests/plugins/mekari-session.spec.js. Mock the ctx (store.state.user.custom_features, $auth.user). Assert: toggle off/absent → Session constructor (mocked) never called; toggle-read throws → no Session (fail-closed); toggle on → Session constructed once with the stubbed sso id; a message event with wrong origin is ignored, correct origin is accepted. Run yarn test-file tests/plugins/mekari-session.spec.js, confirm failure.
  3. Scaffold — Create plugins/mekari-session.js with the default-export init fn, a getToggle(ctx) helper, a getUserSsoId(ctx) stub returning ctx.$auth.user?.user_sso_id with a // TODO Q2: confirm field comment, and an isTrustedOrigin(origin) guard.
  4. Wire state — Read the toggle from store.state.user.custom_features (same shape as redirect-to-v3.js:49). Guard the whole body in try/catch → fail-closed.
  5. Implement behavior — When toggle on: // import { Session } from '@mekari/sdk' (leave import commented + a mockable factory seam until Task 6), construct new Session({ current_user: ssoId }), attach a window.addEventListener('message', …) (or session.on) that drops untrusted origins. Do not add event→action mapping here (Task 3).
  6. Go greenyarn test-file tests/plugins/mekari-session.spec.js until pass.
  7. Quality gateyarn lint:js && yarn build (build will pass only once the SDK import is stubbed/commented — uncomment in Task 6).

Acceptance criteria

  • Toggle absent/false → Session is not constructed; no listener attached; no console noise.
  • feature_enabled read error → fail-closed (no Session), no regression to existing auth.
  • Toggle on → Session constructed exactly once with current_user = the resolved sso id.
  • message events with origin !== 'https://account.mekari.com' are ignored.
  • Plugin is registered in nuxt.config.js after auto-token-refresh; does not call into setupAutoTokenRefresh.
  • (pending Q2) user_sso_id resolves from a confirmed /users/me field — currently stubbed.
  • (pending Q3) the centralized_session code string matches the real feature_enabled value.

Test strategy

Plugin unit test with a hand-rolled ctx mock and a mocked Session factory. Key mock: the @mekari/sdk Session (jest jest.mock once the dep lands, or a injected factory until then). Key assertions: gating matrix (off/error/on) and origin rejection. Stub getUserSsoId to a fixture value so the constructor arg is assertable without Q2 resolved.

Effort estimate

DisciplineDays
Frontend2.0
Backend
QA0.5
Total2.5

Assumptions: reuses the datadog-rum plugin pattern and the redirect-to-v3 feature-read shape; SDK import stubbed behind a factory seam so the shell builds before Task 6; user_sso_id and toggle code stubbed pending Q2/Q3. tests/plugins/ is net-new.

Run to verify

yarn test-file tests/plugins/mekari-session.spec.js && yarn lint:js

Depends on

  • [Task 1] — the plugin will call the msli helper from its handlers (handlers land in Task 3).
  • [External: @mekari/sdk package — Task 6 (Q6)] for the real import.
  • [External: /users/me user_sso_id field — Q2 (pending)] and [centralized_session code string — Q3 (pending)].

Task 3: [FE] SDK event handlers — logged_in / logged_out / switch_user / server_down + msli cleanup (S3)

CRM reacts correctly to every SSO session signal: refreshes its session marker on logged_in, signs out on logged_out, signs out + toasts "user has changed" on switch_user (interim), and on server_down uses the msli fallback before deciding whether to sign out.

Status: ⚠️ Partially blocked — all four handlers and the msli-cleanup edit are buildable now against mocked SDK events. Blocked: the real SDK event subscription depends on Task 6; the switch_user interim path is correct now but the proper SSO-autologin flow is deferred (Task 7, Q1); the exact toast component/API is unconfirmed (Q5).

Design reference: n/a — design pending; reuses the existing notification mechanism, exact toast component unverified (RFC Detail 2.0 Patterns marks it [REQUIRED: confirm component]; Q5). [unverified — check repo].

What to build

The event→action mapping inside plugins/mekari-session.js: logged_insetMsli(now) + RUM action, no navigation; logged_out → dispatch user/userLogout then redirect to account.mekari.com/sign_out (the redirect itself is implemented in Task 4); switch_userclearMsli() + toast "user has changed" + interim sign-out; server_down → call Task 1's getFallbackDecision, then keep-session (degraded) or sign out. Also clear msli inside store/user.js:deleteUserLocalData so logout wipes the marker.

Implementation Plan

ActionFileWhat changes
extendplugins/mekari-session.jsAdd the four session.on(event, …) handlers mapping to msli helper + store dispatch + toast + redirect
extendstore/user.js (deleteUserLocalData:157)Clear msli localStorage key on logout (call clearMsli() or localStorage.removeItem('msli'))
extendtests/plugins/mekari-session.spec.jsAdd per-event tests (4 events)
extendtests/store/user.spec.js (if exists; else create)Assert msli cleared in deleteUserLocalData

File path rule: store/user.js and its deleteUserLocalData action (line 157) verified. tests/store/ exists (many *.spec.js); confirm whether a user.spec.js already exists there before creating — [verify exact store spec filename in repo].

Implementation steps

  1. Explore the codebase area — Re-open plugins/mekari-session.js (from Task 2) and store/user.js: read userLogout (line 120) and deleteUserLocalData (line 157) to see the existing localStorage-clearing style. For the toast, first resolve Q5: search components/_general/ and the user-notifications skill, and look at how setupAutoTokenRefresh's toastCallback (utils/helpers/auth.js:203) is wired by its caller — reuse that exact mechanism rather than inventing a $toast. [unverified — check repo]
  2. Write failing tests (red) — Extend tests/plugins/mekari-session.spec.js with one test per event: logged_insetMsli called, no navigation; logged_outuserLogout dispatched; switch_userclearMsli + toast + sign-out; server_downgetFallbackDecision consulted, keep-alive when it returns logged_in, sign-out when logged_out. Add a tests/store/... assertion that deleteUserLocalData removes msli. Run, confirm red.
  3. Scaffold — Add a handlers = { logged_in, logged_out, switch_user, server_down } map in the plugin; subscribe each via the SDK's session.on('event', (data, error) => …) (mocked until Task 6).
  4. Wire state — Import setMsli/clearMsli/getFallbackDecision from utils/helpers/mekari-session.js (Task 1). Dispatch store.dispatch('user/userLogout') for sign-out events.
  5. Implement behavior — Fill each handler per §2.4 Inbound table and the §2.6 state machine. For switch_user use the interim ADR-5 path (sign-out + toast); leave a // TODO Q1: proper SSO-autologin flow → Task 7. Add clearMsli() into store/user.js:deleteUserLocalData.
  6. Go greenyarn test (or the two test-file specs) until pass.
  7. Quality gateyarn lint:js && yarn build.

Acceptance criteria

  • logged_in (id matches) → msli set to now; no navigation occurs.
  • logged_outuser/userLogout dispatched (redirect added in Task 4).
  • switch_usermsli cleared, "user has changed" toast shown, interim sign-out triggered.
  • server_down → fallback consulted; logged_in decision keeps session (degraded), logged_out decision signs out.
  • deleteUserLocalData removes the msli key on every logout.
  • (pending Q5) toast uses the confirmed CRM notification component.
  • (pending Q1) switch_user is interim sign-out only — proper flow is Task 7.

Test strategy

Drive the mocked Session to emit each event and assert the resulting store dispatch / msli mutation / toast call. Key mock: a fake session emitter + spies on setMsli/clearMsli/store.dispatch/the toast callback. Key assertion: server_down branches exactly on getFallbackDecision's return value.

Effort estimate

DisciplineDays
Frontend2.0
Backend
QA0.5
Total2.5

Assumptions: reuses Task 1's helper and existing userLogout; toast reuses the existing notification mechanism (unconfirmed component — Q5); interim switch_user only. No new package.

Run to verify

yarn test-file tests/plugins/mekari-session.spec.js && yarn lint:js

Depends on

  • [Task 1] (msli helper), [Task 2] (plugin shell + subscription seam), [Task 4] (the redirect that logged_out/switch_user/server_down sign-out reuses).
  • [External: @mekari/sdk — Task 6] for the real subscription; [Q5 toast component (pending)].

Task 4: [FE] Wire product logout to account.mekari.com/sign_out (S5)

When a CRM user logs out (in-app or via an SDK sign-out event), the SSO session is also destroyed — the browser is redirected to account.mekari.com/sign_out after CRM's local cleanup completes.

Status: ✅ Actionable — extends an existing, verified action; no SDK or user_sso_id needed. The only caveat (Q7) is a regression-check, not a blocker.

Design reference: n/a — navigation only, no Figma.

What to build

Extend the success path of store/user.js:userLogout so that, after the sign_out POST resolves and local cleanup (deleteUserLocalData + deleteSsoCookies + $auth.reset()) runs, the browser is redirected via window.location.href = 'https://account.mekari.com/sign_out'. Guard the redirect behind the centralized_session toggle so legacy logout is unchanged when the feature is off.

Implementation Plan

ActionFileWhat changes
extendstore/user.js (userLogout:120, success path after :133–134)After local cleanup, if centralized_session on, window.location.href = 'https://account.mekari.com/sign_out'
extend/createtests/store/user.spec.js (verify filename)Assert window.location.href set to the SSO sign_out URL after sign_out resolves; assert no redirect when toggle off

File path rule: store/user.js:userLogout verified at line 120, cleanup at 133–134. The external-redirect-via-window.location.href pattern is verified in middleware/version-switcher.js:13 and middleware/redirect-to-v3.js:65. Test path tests/store/user.spec.js [verify exact filename in tests/store/].

Implementation steps

  1. Explore the codebase area — Open store/user.js and read userLogout (120) end-to-end: it POSTs ${USER_URL}/sign_out then dispatches deleteUserLocalData + deleteSsoCookies. Open middleware/redirect-to-v3.js:65 to copy the window.location.href cross-origin redirect idiom. Resolve Q7 first: grep all callers of userLogout (account-switch, embed layouts) to confirm none expect to stay in-app.
  2. Write failing tests (red) — In tests/store/user.spec.js: mock the sign_out request to resolve, set centralized_session on, assert window.location.href === 'https://account.mekari.com/sign_out' after userLogout; toggle off → assert no redirect. Mock window.location. Run, confirm red.
  3. Scaffold — Add a redirect line guarded by the toggle read in the userLogout success continuation.
  4. Wire state — Read the toggle from state.custom_features (same shape as elsewhere) inside the action.
  5. Implement behavior — Set window.location.href only after cleanup; keep the existing .catch behaviour (proceed to cleanup even if POST errors) intact.
  6. Go greenyarn test-file tests/store/user.spec.js.
  7. Quality gateyarn lint:js && yarn build.

Acceptance criteria

  • After sign_out POST resolves and cleanup runs, window.location.href is set to https://account.mekari.com/sign_out when centralized_session is on.
  • When the toggle is off, userLogout behaves exactly as pre-RFC (no external redirect).
  • Existing .catch path (POST error → still clean up) is preserved.
  • (Q7) No existing in-app userLogout caller is broken by the redirect (verified by caller audit, behind the toggle).

Test strategy

Store-action unit test with mocked request and mocked window.location. Key mock: the sign_out HTTP call (resolve + reject cases). Key assertion: redirect URL set exactly once, only when toggle on, only after cleanup.

Effort estimate

DisciplineDays
Frontend1.0
Backend
QA0.5
Total1.5

Assumptions: reuses existing userLogout + verified window.location.href redirect idiom; redirect gated by the toggle so it's a no-regression change; Q7 is a caller audit, not new code.

Run to verify

yarn test-file tests/store/user.spec.js && yarn lint:js

Depends on

  • None to start (independent of the SDK). Tasks 3's sign-out events reuse this redirect, so land it before/with Task 3.

Task 5: [FE] Datadog RUM observability for session events (S7)

Operators can see session-event volume, fallback rate, and origin-mismatch errors in Datadog for the piloted company, so the rollout can be monitored and rolled back on signal.

Status: ✅ Actionable — reuses the existing @datadog/browser-rum dependency; internal-only instrumentation.

Design reference: n/a — observability config, no UI.

What to build

Emit a RUM custom action mekari_session.event with { event, matched } from each handler in plugins/mekari-session.js, and a RUM error on server_down and on postMessage origin mismatch. No console.log.

Implementation Plan

ActionFileWhat changes
extendplugins/mekari-session.jsImport datadogRum from @datadog/browser-rum; call datadogRum.addAction('mekari_session.event', {...}) in handlers and datadogRum.addError(...) on server_down / origin mismatch
extendtests/plugins/mekari-session.spec.jsAssert addAction/addError called with the expected payload (mock datadogRum)

File path rule: plugins/datadog-rum.js verified — it import { datadogRum } from '@datadog/browser-rum' (line 1). @datadog/browser-rum@^4.34.0 confirmed in package.json. Note: addAction/addError are not yet used anywhere in the repo — confirm the v4 API surface (datadogRum.addAction(name, context), datadogRum.addError(error, context)) when wiring.

Implementation steps

  1. Explore the codebase area — Open plugins/datadog-rum.js (verified) for the import { datadogRum } form and the service: 'qontak-crm-frontend' convention (line 9). Confirm RUM is initialised before the session plugin runs (datadog-rum at nuxt.config.js:66, session plugin registered after — verify ordering).
  2. Write failing tests (red) — Extend tests/plugins/mekari-session.spec.js: mock datadogRum, assert addAction('mekari_session.event', { event, matched }) fires per handled event and addError fires on server_down + origin mismatch. Run, confirm red.
  3. Scaffold — Add import { datadogRum } from '@datadog/browser-rum' to the plugin.
  4. Wire state — Pass { event: <name>, matched: <bool> } from each handler.
  5. Implement behavior — Add addAction to all four handlers; addError in the server_down branch and the origin-mismatch guard (Task 2).
  6. Go greenyarn test-file tests/plugins/mekari-session.spec.js.
  7. Quality gateyarn lint:js && yarn build.

Acceptance criteria

  • Each handled SDK event emits datadogRum.addAction('mekari_session.event', { event, matched }).
  • server_down and postMessage origin mismatch emit datadogRum.addError(...).
  • No new console.log introduced.

Test strategy

Mock the @datadog/browser-rum module; spy on addAction/addError. Key assertion: action name + payload shape per event; error emitted on the two failure conditions.

Effort estimate

DisciplineDays
Frontend0.5
Backend
QA0
Total0.5

Assumptions: reuses existing @datadog/browser-rum dep + RUM init; internal-only (QA 0); thin layer over Task 3's handlers.

Run to verify

yarn test-file tests/plugins/mekari-session.spec.js && yarn lint:js && yarn build

Depends on

  • [Task 2] (origin guard to instrument) and [Task 3] (the handlers to instrument).

Task 6: [BE] Add @mekari/sdk dependency 🚫 (S1 dependency)

The @mekari/sdk Session package is installed and pinned so the plugin (Task 2/3) can import it for real and the build resolves it.

Status: 🚫 Blocked — unblock condition: confirm @mekari/sdk is published to a Mekari/private npm registry CRM can install from (RFC §5 Q6 [important]). The package is absent from package.json today (verified — only @mekari/pixel). Until then, Tasks 2/3/5 run against a stubbed/commented import.

Design reference: n/a — dependency change, no UI.

What to build

Run yarn add @mekari/sdk (pinned), commit package.json + yarn.lock, then uncomment the real import { Session } from '@mekari/sdk' in plugins/mekari-session.js (the factory seam left in Task 2).

Implementation Plan

ActionFileWhat changes
extendpackage.json (+ yarn.lock)Add pinned @mekari/sdk dependency
extendplugins/mekari-session.jsUncomment/replace the stubbed import with the real @mekari/sdk Session

File path rule: package.json verified (no @mekari/sdk present). plugins/mekari-session.js is created in Task 2.

Implementation steps

  1. Verify registry access (Q6) — Confirm CRM's .npmrc / registry can resolve @mekari/sdk. If it 404s, the task stays blocked — escalate to A&L.
  2. yarn add @mekari/sdk (pin exact version).
  3. Replace the Task-2 import seam with import { Session } from '@mekari/sdk'.
  4. yarn install --frozen-lockfile passes; yarn build resolves the import.

Acceptance criteria

  • @mekari/sdk present in package.json deps at a pinned version.
  • yarn install --frozen-lockfile passes.
  • yarn build resolves the @mekari/sdk import.

Test strategy

No new unit test — covered by Tasks 2/3/5 once the real module replaces the mock; the gate is yarn build resolving the import.

Effort estimate

DisciplineDays
Frontend0.5
Backend
QA0
Total0.5

Assumptions: labelled [BE]/infra-ish (dependency + registry), trivial once Q6 confirms availability; no code logic.

Run to verify

yarn install --frozen-lockfile && yarn build

Depends on

  • [External: @mekari/sdk registry availability — Q6 (pending)]. Unblocks the real imports in Tasks 2, 3, 5.

Task 7: [FE] switch_user proper SSO-autologin flow 🚫 (S3 / Q1)

On switch_user, instead of the interim "sign out + re-login", CRM seamlessly re-establishes a session for the new account via the SSO-autologin round-trip (PRD web-session flow) — no credential re-entry.

Status: 🚫 Blocked — unblock condition: resolve RFC §5 Q1 [critical]: CRM is token-based (not OAuth2 authz-code) and has no SSO-autologin callback route today (crm-user redirects to crmV1Host/login). The SSO-autologin contract for token products must be defined (by A&L) before this can be built without hallucinating the contract. Interim behaviour (sign-out + toast) ships in Task 3.

Design reference: n/a — design pending (depends on the resolved flow; possible bespoke interstitial is RFC §5 Q5).

What to build (once unblocked)

An SSO-autologin redirect/callback path: destroy product session → redirect to product sign-in → SSO autologin with valid _mekari_account → callback → new product session → fetch current company. Requires a CRM callback route + token re-issue path that does not exist today.

Implementation Plan

ActionFileWhat changes
createpages/... or middleware/... callback route[unverified — contract undefined] SSO-autologin callback
extendplugins/mekari-session.jsReplace interim switch_user handler with the autologin round-trip
extendschemes/crmAuthScheme.js[unverified] token re-issue from SSO callback

File path rule: all paths [unverified — check repo / contract undefined]. The exact route and token-reissue mechanism cannot be specified until Q1's contract exists.

Acceptance criteria

  • (pending Q1) switch_user re-establishes a session for the new account without credential re-entry, per the defined SSO-autologin contract.
  • Current company resolves for the new account after switch.

Effort estimate

DisciplineDays
Frontend3.0
Backend
QA0.5
Total3.5

Assumptions: coarse estimate — no contract exists (Q1). Likely also needs BE/SSO callback support not scoped here. Figure is a placeholder to size the deferred work, not a committed estimate.

Depends on

  • [External: SSO-autologin contract for token products — Q1 [critical] (pending)], [Task 2], [Task 3], [Task 6].

Task 8: [FE+BE] current_company sync after session 🚫 (S4)

After a session is (re-)established, CRM reflects the user's current SSO company so the product context matches SSO.

Status: 🚫 Blocked — unblock condition: resolve RFC §5 Q4 [critical] + ADR-7: no CRM current_company endpoint or field exists (only team endpoints: store/user.js:244,261,274), and whether CRM even has a per-company-switch equivalent is unconfirmed. Needs a BE RFC/contract before any FE work. Deferred entirely in this RFC.

Design reference: n/a — deferred.

What to build (once unblocked)

FE consumption of a (to-be-defined) CRM current_company endpoint/field to set company context after logged_in/switch_user. The endpoint itself is BE work (separate RFC).

Implementation Plan

ActionFileWhat changes
createBE current_company endpoint[unverified — covered in BE RFC, link REQUIRED]
extendstore/user.js[unverified] set company context from the new endpoint

File path rule: all paths [unverified — check repo / BE RFC pending]. CRM uses teams, not SSO current_company — no contract to anchor against today.

Acceptance criteria

  • (pending Q4 + BE RFC) CRM company context matches the SSO current company after session establishment.

Effort estimate

DisciplineDays
Frontend1.0
Backend2.0
QA0.5
Total3.5

Assumptions: coarse — BE endpoint with business logic (2–3 BE days range, taken at 2.0) plus FE wiring; no in-repo contract (Q4). Estimate is a placeholder for deferred scope.

Depends on

  • [External: CRM current_company BE contract / BE RFC — Q4 [critical] (pending)], [Task 2], [Task 3].

Ordering rationale

  1. Start with Task 1 (msli helper). It is pure, fully actionable, and is the decision engine Task 3's server_down handler consumes — building it first lets the plugin handlers wire against a tested helper rather than a stub. Zero external dependency.
  2. Task 2 then Task 3 form the plugin core. Task 2 (shell + toggle gate + origin guard) must precede Task 3 (event handlers) — same file, and Task 3 attaches handlers to the subscription seam Task 2 creates. Both are buildable now with the SDK import and user_sso_id stubbed; this is the critical path and the bulk of the FE effort (4.5 FE days).
  3. Task 4 (logout → SSO redirect) is independent and should land alongside Task 3, because Task 3's sign-out events (logged_out / switch_user / server_down) reuse that redirect. It's safely toggle-gated, so it can merge before the SDK is even installed.
  4. Task 5 (RUM) is a thin final layer over Tasks 2/3 — do it last among the actionable set; it's needed for the rollout's go/no-go signal but blocks nothing.
  5. Critical path to "done" runs through the external blockers, not CRM code. The four [critical] OQs are the real gate (the RFC's own §7 marker is "no"). Push A&L/Infosec on: Q6 (publish @mekari/sdk to the registry → unblocks the real import in Task 6, turning Tasks 2/3/5 from stubbed to complete), Q2 (user_sso_id field in /users/me → unblocks the Session constructor arg), Q3 (exact centralized_session code string), and Q1/Q4 (the SSO-autologin and current_company contracts → unblock Tasks 7/8). Also confirm Q5 (toast component) before finishing Task 3 and Q8 (CSP delivery mechanism, Infosec) before pilot. Net: ~9 man-days of CRM FE work (Tasks 1–5) can proceed today behind the toggle, but cannot be fully wired/shipped until Q6 + Q2 + Q3 land.

Skipped stories

Full-scope mode — every 🚫 Blocked task with its unblock condition (detail is in each task body above):

Story / TaskReason / unblock condition
Task 6 — Add @mekari/sdk dep (S1 dep)Blocked on Q6 [important] — confirm @mekari/sdk is published to a Mekari/private npm registry CRM can install from. Package absent from package.json today.
Task 7 — switch_user proper SSO-autologin flow (S3 / Q1)Blocked on Q1 [critical] — CRM is token-based with no SSO-autologin callback route; the autologin contract for token products is undefined. Interim sign-out+toast ships in Task 3.
Task 8 — current_company sync (S4)Blocked on Q4 [critical] + ADR-7 — no CRM current_company endpoint/field exists (CRM uses teams); needs a BE RFC/contract first. Deferred.

Additional non-blocking unknowns that constrain the actionable tasks (not skipped, but flagged in-task): Q2 (user_sso_id source — stubs Task 2's constructor arg), Q3 (exact centralized_session code — stubs Task 2's gate), Q5 (toast component — Task 3), Q7 (logout-caller regression audit — Task 4), Q8 (CSP delivery, Infosec — pilot gate).