Skip to main content

RFC Review: Centralized Web Session — Launchpad FE Integration

Executive Summary

  • Overall Score: 6.5/10
  • 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 — one below threshold, so the "any 3 below 5.0 → max 6.5" gate does not fire. Overall held to 6.5 by judgment: 3 critical open questions block chunks 2/4/6.
  • Implementation Readiness Verdict: HOLD — Q1 (SDK distribution), Q2 (server_down predicate), Q3 (current-company endpoint) block agentic execution of chunks 2, 4, and 6.
  • Report Path: /Users/mekari/Documents/qontak-launchpad-fe/rfc-review-report.md
  • RFC Author: Account & Launchpad team | Reviewed: 2026-06-28

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, four sequence diagrams, and a 7-chunk execution plan with per-chunk acceptance criteria. The biggest strength is scope + pattern grounding (an agent could build chunks 1, 3, 5, 7 today). The biggest gap is that three explicitly-flagged critical decisions remain open and they gate the SDK loader, the current-company sync, and the server_down fallback — roughly half the feature. The one thing that must change before agentic execution: close Q1, Q2, Q3 with concrete contracts (SDK distribution mechanism, an FE-readable fallback predicate, and the current-company endpoint host + response schema).


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), 5 (switch_user), 7 (middleware) reuse existing builders (authenticated.global.ts:80, clearTokenLaunchpad, resetAuth) and have verifiable acceptance criteria.

Why this RFC will cause agent guessing or rework:

  • Q1/Q2/Q3 are critical and unresolved — chunks 2, 4, 6 are explicitly "blocked"; an agent would invent the SDK import path, the fallback predicate, and the endpoint host.
  • No error-message catalog and no quantified non-functionals — agent would invent toast copy and ship without perf/a11y targets.

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 ElementRFC SectionCoverage
Overview / known issues§1.0, §1.1Full
Success Criteria (FE portion)§1.1, §2.xFull
Out of Scope§1.2Full
Dependencies (SDK, service, Redis)§2.1 (upstream, read-only)Full (referenced as upstream contracts)
Technical Design — Proposal§2.2–§2.6Full (FE portion)
SDK contract / events§2.3Full
FE Product Integration Flows§2.4–§2.6Full
Web Session Flow§2.5n/a — Launchpad uses OAuth2 authz-code, not Rails web session (justified)
OAuth2 Authorization Code Flow§2.5, ADR-1Full
User Logout From Product§2.6Full
User Switch Account§2.5 switch_userPartial — flow specified, but "account changed" toast copy/i18n key missing
Database Modeln/a — no DB (FE)
HA & Security§3Partial — FE subset; perf/a11y not quantified
Rollout Plan§4Full (Launchpad = step 4)
Open Questions§5Full (7 questions, severity-tagged)

Summary: ~12 of 14 PRD elements fully covered, 2 partial (toast copy, non-functionals), 0 missing. No RFC decisions lack PRD justification. PRT = 8.0.


Scorecard

Frontend Scorecard (11 categories)

CategoryScoreEvidence-Based Rationale
PRT — PRD Traceability8.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 Decisions6.0D1, D2, D4 resolved as full ADRs with options + rationale. D3 partial (source unresolved, Q4). D5 dangling (Q1). Q2 is effectively an unresolved decision. Multiple blockers self-flagged.
CNT — Contract Specificity6.5SDK contract specified (new Session({current_user}), .on, .refresh; §2.1). State Surface Contract table present (§2.3). But /users/me/current_company has no request/response schema, and msli value format is loosely "now"/timestamp.
SCB — Scope Boundaries8.5Repo 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 — Dependencies6.5Deps named with status: @mekari/sdk "NOT installed" (Q1), current-company host unresolved (Q3), toggle new. Honest, but two key deps blocked.
FMC — Failure Mode Coverage5.5All 4 events have a reaction; hard client-side timeout noted (§3.1). But server_down predicate is broken (Q2), no error-message catalog, current_company fetch-failure undefined, rapid-switch race unaddressed. Cap "FMC<7.0 needs error catalog" applies — held below 7.
NFS — Non-Functional Specificity4.0Security/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 Specificity7.0Each 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 & Rollback7.0Toggle-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 — Observability5.5server_down fallback counter proposed; console.error/warn + useErrorHandler routing (§3.3). But telemetry sink [REQUIRED], no metric/dashboard names finalized.
CPA — Pattern Alignment8.5Patterns-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

#DecisionStatusCritical Gaps
D1Auth model = OAuth2 authz-codeResolved
D2SDK load = *.client.ts pluginResolved
D3Toggle = mirror useToggleQontakOnePartialtoggle source (env vs backend) unresolved — Q4
D4Event wiring = plugin→composable→mw/storeResolvedtouches shared middleware (medium reversibility)
D5SDK distribution (npm vs CDN)Danglingblocks chunk 2 — Q1
D6server_down fallback predicateDangling_mekari_account unreadable cross-origin — Q2
D7Current-company endpoint host/schemaDanglinghost/owner unknown, no schema — Q3

Aggregate: 3 Resolved, 1 Partial, 3 Dangling (of 7).


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?).

Challenge results

  • Reversibility: High — delete plugin / flip toggle.
  • Consistency: Consistent.
  • Agent implementability: Mostly — agent must invent the idempotency guard (module singleton? if(window.__mekariSession)?).

Gaps and suggestions

Missing: concrete idempotency guard. Suggested resolution: module-level singleton mirroring useToggleQontakOne.ts:11 TOGGLE_SOURCE; guard let sessionInstance in plugin. 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 /sm/current → falls to server_down (but predicate broken — D6).

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 (npm vs CDN)

Status: Dangling

What was decided

NOT DECIDED — "unresolved — see Open Question Q1." Two upstream contracts both listed (§2.1): npm @mekari/sdk and CDN https://account.mekari.com/sm/sdk.js.

Alternatives considered

Both named, neither rejected.

Grounding in existing code

package.json@mekari/sdk absent (§2.0.1). Grounded as absent.

Interface specification

SDK shape known (new Session, .on, .refresh); load mechanism unknown.

Failure handling

CDN script-load failure path undefined (separate from server_down).

Challenge results

  • Reversibility: Medium — import style propagates into plugin + tests.
  • Agent implementability: No — agent cannot write chunk 2 without knowing import vs dynamic <script>.

Gaps and suggestions

Missing: distribution decision; if CDN — load-failure handling. Suggested resolution: prefer npm @mekari/sdk (pinned version, bundle-size delta measured, mockable in Vitest); CDN only if package publishing is blocked, in which case add async script-load with onerror → server_down. Open questions: Q1 — Account/SSO + AL to confirm package availability/version.


Decision: D6 — server_down fallback predicate

Status: Dangling

What was decided

Predicate: "msli < 2h AND _mekari_account valid" — but RFC itself marks the second clause "[UNREADABLE cross-domain — Q2]" in the failure sequence diagram (§2.4).

Alternatives considered

None for the redefined FE-only predicate.

Grounding in existing code

useAuthCookies.ts has no _mekari_account (§2.0.1, §3.2 A01). Confirmed unreadable from Launchpad origin.

Interface specification

Broken — predicate depends on inaccessible state.

Failure handling

This IS the failure-handling path; it is itself unimplementable.

Challenge results

  • Scale: N/A.
  • Reversibility: Medium.
  • Consistency: Conflicts with A01 finding in same RFC.
  • Agent implementability: No — agent would read a cookie that doesn't exist and silently always-fail or always-pass.

Gaps and suggestions

Missing: FE-readable predicate. Suggested resolution: replace with msli (localStorage, written on logged_in) + global_sso_valid_until cookie (useAuthCookies.ts:7): treat as logged_in iff now - msli < 2h AND global_sso_valid_until in future; else revoke + sign-out. Open questions: Q2 — SSO + AL to ratify FE-only predicate.


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:80 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.com is wrong.

Gaps and suggestions

Missing: host, schema, failure behavior. Suggested resolution: route through existing Kong base (apiBaseUrl, as authStore.ts:80 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.

SurfaceLoadingEmptyErrorPartialSuccessAssessment
Session gate (middleware await)partial (await + timeout, no spinner spec — §1.3 marked [REQUIRED])n/adefined (→server_down)n/adefined (allow nav)2.5/5
"Account changed" toast (switch_user)n/an/amissing (toast fails?)n/adefined (toast.notify) but copy/i18n key missing1/5
Current-company fetchmissingmissing (no company?)missingmissingdefined (set on store)1/5

Summary: 0 of 3 surfaces have all states. Loading visual is [REQUIRED] (open); current-company fetch states entirely absent — agent will build happy-path only.


Performance Budget Check

MetricTargetCurrent BaselineSourceAssessment
LCPnot statednot statedmissing
INPnot statednot statedmissing
CLSnot statednot statedmissing
Bundle size delta (SDK)not statednot statedmissing (relevant once Q1 picks npm)
Middleware await timeout"hard client-side timeout" (§3.1), no numberRFC prosevague

NO QUANTIFIED PERFORMANCE BUDGET — agent cannot optimize against targets; SDK bundle delta unmeasured until distribution (Q1) resolved.


Accessibility Review

AspectSpecified?DetailsAssessment
Keyboard navigation flownomissing
Focus managementnotoast/loading-gate focus undefinedmissing
ARIA labelsnomissing
Heading hierarchynomissing
Color contrastnoreuses Pixel toast (likely AA via design system)missing/assumed
Motion sensitivitynomissing
Screen reader behaviornotoast announcement (aria-live) not specifiedmissing

A11y largely unaddressed. Mitigated by reuse of @mekari/pixel3 toast (inherits design-system a11y), but the loading gate ([REQUIRED]) has no focus/announce spec.


Pattern Alignment Check

PatternRFC ApproachAssessment
State management (Pinia setup store)follows (authStore.ts)strong
Feature toggleextends (useToggleQontakOne.ts:11)strong — but source unresolved (Q4)
API call (useClient)follows (useClient.ts:43, body at data.value.data)strong
Auth cookiesreuse (useAuthCookies.ts)strong
SSO re-auth / sign-out redirectreuse (authenticated.global.ts:80, SwitchAccountContent.vue:117)strong
Toast notificationreuse (authStore.ts:117)strong; copy/i18n key missing
Error normalizefollows (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/PhraseLocationImpactConcrete Replacement
1"hard client-side timeout"§3.1agent guesses ms"3000ms then synthetic server_down"
2"non-blocking when toggle off"§3.1OK but unverifiedassert existing middleware spec green with toggle off
3"account changed" toast (no exact copy)§2.4, §1.3agent invents copyexact i18n key + EN/ID string
4"msli=now"§2.3format ambiguouslocalStorage.msli = Date.now() (epoch ms)
5"idempotent" initADR-2guard mechanism unstatedmodule-singleton guard

Total vague words in spec sections: 5

Dangling Alternatives

#AlternativesLocationImpact
1npm vs CDN SDK§1.B D5 / §2.1 / Q1agent cannot write loader (chunk 2)
2env var vs backend toggle sourceADR-3 / Q4agent guesses config key/default
3Kong vs api.mekari.com vs BE proxy§2.5 / Q3agent guesses host (chunk 4)
4/sm/current vs /sessionmanager/current§2.1 / Q5iframe URL guess (chunk 2)

Total dangling alternatives: 4

Task Decomposition Assessment

ChunkAcceptance CriteriaAssessment
1 toggletoggle off→false, on→true; red firstverifiable (blocked Q4 for source)
2 SDK loadernew Session({current_user}) with launchpad.sso_id (mock)verifiable but blocked Q1, Q5
3 logged_outclearTokenLaunchpad+resetAuth+sign_out redirectverifiable
4 logged_in+companymsli written, company fetched/setverifiable but blocked Q3
5 switch_usertokens cleared→authz redirect→toast on returnverifiable; toast copy missing
6 server_downvalid msli<2h→logged_in else sign-outblocked Q2 (predicate broken)
7 middlewaretoggle on→await+timeout; off→specs greenverifiable; timeout number missing

Order is sound (toggle→loader→handlers→middleware). 3 of 7 chunks blocked.


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/toast builders; introduces only new composables — generated code will look native.
  • Honest decision tracking (§1.B, §5, §7): ADR index, severity-tagged open questions, and a Ready: no gate that correctly refuses to greenlight while Q1–Q3 are open. Source Verification table (§2.0.1) backs claims with greps.

Biggest Gaps

  • Three critical dangling decisions (Q1/D5, Q2/D6, Q3/D7): block chunks 2, 4, 6 — SDK loader, current-company sync, and the entire server_down fallback. Q2 is a logic contradiction (predicate reads a cross-origin cookie the RFC itself proves unreadable, §3.2 A01).
  • No error-message catalog + no current_company schema (CNT 6.5, FMC 5.5): toast copy/i18n key absent; current_company has no response shape or fetch-failure behavior — agent invents both.
  • No quantified non-functionals (NFS 4.0): no LCP/INP/CLS/bundle budget, no a11y spec, no browser matrix, no timeout number — agent ships unmeasured.

Priority Actions

Do these before handing to an AI agent.

  1. Q1 / D5 (SDK distribution) — pick npm @mekari/sdk (pin version, state bundle delta, confirm Vitest mockability) or CDN (then specify async script load + onerror→server_down). Unblocks chunk 2.
  2. Q2 / D6 (server_down predicate) — redefine FE-readable: now - msli < 2h && global_sso_valid_until > now (drop _mekari_account). Unblocks chunk 6; resolves the §3.2 A01 contradiction.
  3. Q3 / D7 (current-company endpoint) — name host (prefer Kong apiBaseUrl proxy as authStore.ts:80), give request/response schema (read at data.value.data), and fetch-failure behavior via useErrorHandler. Unblocks chunk 4.
  4. Error catalog + non-functionals — add exact toast i18n key + EN/ID strings, the middleware await timeout (ms), and minimal perf/a11y targets (SDK bundle delta, toast aria-live). Lifts FMC/NFS above gate.

Implementation Readiness Checklist

Unblocked (agent can proceed)

All types:

  • PRD → RFC traceability matrix complete (§1.A)
  • All technical decisions resolved (3 dangling: D5/D6/D7)
  • All failure modes handled with error message catalog (no catalog; server_down 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 (5 found)

Frontend:

  • All interfaces/prop types specified (current_company schema missing)
  • All UI states defined (0/3 surfaces complete)
  • Performance budget quantified
  • Accessibility requirements specified
  • Browser support matrix defined

Blocked (must fix first)

  • Q1 — SDK distribution mechanism (chunk 2)
  • Q2 — FE-readable server_down predicate (chunk 6)
  • Q3 — current-company endpoint host + schema (chunk 4)

Verdict: Fix 3 blockers first (chunks 1, 3, 5, 7 are agent-ready today).


Task Manifest

Verified against RFC §4.C; ordering retained. Blocked chunks flagged.

OrderChunkFiles to Create/ModifyAcceptance CriteriaDependencies
1centralized_session toggleapp/common/composables/useCentralizedSessionToggle.ts + specoff→false, on→true; spec red firstQ4 (source/key)
2SDK loader pluginapp/plugins/mekariSession.client.ts + spectoggle on + launchpad.sso_idnew Session({current_user}) (mock); off → not loadedQ1, Q5
3logged_out handlerapp/common/composables/useCentralizedSession.ts + specevent → clearTokenLaunchpad+resetAuth+SSO_URL/sign_out redirectChunk 1
4logged_in + company syncapp/common/composables/useCurrentCompany.ts + spec; extend useCentralizedSession.tsmsli written; company fetched via useClient, set on storeQ3, Chunk 3
5switch_user re-auth + toastextend useCentralizedSession.ts; modify authenticated.global.tstokens cleared → authz redirect → toast on /sso-callback returnChunk 3; toast copy needed
6server_down fallbackextend useCentralizedSession.ts + msli helper + specvalid msli<2h → logged_in else sign-outQ2, Chunk 4
7middleware integrationapp/middleware/authenticated.global.tson → await + timeout; off → existing specs greenChunks 1–6; timeout value

RFC specified this decomposition; reviewer confirms ordering and dependencies. Author must close Q1/Q2/Q3 before chunks 2/6/4 execute.


Dangling Decisions Log

#DecisionLocationOwnerDeadline
1SDK distribution (npm vs CDN)§1.B D5 / §2.1 / Q1Account/SSO + ALunset
2server_down FE-readable predicate§2.4 / §3.2 A01 / Q2SSO + ALunset
3Current-company endpoint host/schema§2.5 / Q3Launchpad BEunset
4Toggle source (env vs backend) + key/defaultADR-3 / Q4ALunset
5Session Manager path /sm/current vs /sessionmanager/current§2.1 / Q5Account/SSOunset
6CSP / SDK iframe hardening§3.2 / Q6infosecunset

Open Questions

#QuestionCategorySeverity
1npm @mekari/sdk or CDN sdk.js?TDC/DEPBlocking
2FE-readable server_down predicate (drop _mekari_account)?FMCBlocking
3Current-company endpoint host + response schema + failure behavior?CNT/DEPBlocking
4Toggle source + env key + default for pilot?TDC/ROLImportant
5Canonical Session Manager path?TDCImportant
6Launchpad CSP vs SDK iframe; which hardening?NFS (security)Important
7Visible loading state while SDK resolves?UI StateNice-to-have
8Exact "account changed" toast i18n key + strings?CNT/FMCImportant
9Middleware await timeout value (ms)?TDC/NFSImportant

Evidence Notes

  • §2.0 Repo Reading Guide + §2.0.1 Source Verification — strongest evidence; grep-backed claims (@mekari/sdk absent, _mekari_account not a Launchpad cookie, current_company no matches) drove SCB 8.5 / CPA 8.5 and confirmed Q2 contradiction.
  • §1.B Decisions + §5 Open Questions — 3 critical dangling decisions held TDC to 6.0 and overall to 6.5.
  • §3 HA & Security — OWASP-aligned (A01/A02/A05/A07) but no quantified FE perf/a11y → NFS 4.0.
  • §4.C Agent Execution Plan — per-chunk test commands + accept criteria gave TPS 7.0; 3 of 7 chunks self-flagged blocked.
  • Absence: error-message catalog + current_company schema + UI states — capped FMC at 5.5 and CNT at 6.5.