SRS: UC_4.6.4 — Settings: Controls (AI chat & Reduce motion)
UC_4.6.4: Settings — Controls (AI chat & Reduce motion)
| Field | Value |
|---|---|
| BA in Charge | Anh Hoang |
| Date Created | 2026-08-12 |
| Version | v7 |
| Document References | Client answer 2026-08-24 (Controls — "Reduce Motion: There is no backend API for this" / "AI Desk Manager Toggle: This must be a backend call") · BA/client answers 2026-08-21 (QnA Init Docs — UC_4.6.1–4.6.4: Settings, Q15 / C-10 + Q17 / C-13) · Level Up Cinematic Scene Handling.pdf · RFQ_ Stack Trading Prop Tech V7.pdf (§3.2 GEKKO_CHATTER event · §3.2.1 The Desk Manager — LLM Commentary Engine · §API table "AI Preference Toggle" · §4.3 item 21 The Desk Manager Delivery) · RFQ_ Website and Dashboard Implementation V7.pdf (§Part C — Settings & Profile Module, "Reduce Motion" accessibility toggle) · Zapier Integration V7.pdf (§Users table schema — ai_enabled, ui_preferences) |
Document References
| # | Original Document | Key Sections Used |
|---|---|---|
| 1 | RFQ_ Stack Trading Prop Tech V7.pdf | §API table — AI Preference Toggle (POST user_id, ai_enabled (Boolean) → HTTP 200 OK, "Updates the ai_enabled flag in the user's profile. Used by the frontend to turn the 'Risk Personality' coach on or off") · §3.2 — GEKKO_CHATTER WebSocket event · §3.2.1 — The Desk Manager (engine, 15 trigger events, Token Bucket 10/hour/user, Master Prompt Payload) · §4.3 item 21 — The Desk Manager Delivery (asynchronous, non-blocking) |
| 2 | RFQ_ Website and Dashboard Implementation V7.pdf | §Part C — Settings & Profile Module ("Includes Change Password, AI Chat Toggle, Billing Portal Link… Must include a 'Reduce Motion' accessibility toggle") · §Part C — The Desk Manager ("An optional LLM-driven 'Chatter' feature… rendered as stacked UI cards") |
| 3 | Zapier Integration V7.pdf | §Users table schema — ai_enabled (Boolean), ui_preferences (JSON Object). ⚠️ Since 2026-08-24 this UC writes only ai_enabled; ui_preferences.reduce_motion is withdrawn as a stored field — Ref: BR_4.6.4.2. ✅ 2026-09-03: the client confirms ui_preferences stays in the schema "strictly as a future-proofing measure" with "no active parameters to parse… today" — a deliberately dormant optional JSON column, not a field this UC has lost track of. |
| 4 | QnA from clients — STAGE 2: Dashboard & Evaluation (Settings) | Client-confirmed answers for the Controls & Concierge category (Stage 2 - Dashboard & Fomula (Settings).csv, rows 423–447; Adrian Stack, 2026-08-10) — label change AI Chat → Desk Manager, both tooltip strings, and the three-group Reduce Motion scope directive |
| 5 | References/WBS/[BA Internal] Stacktrading.csv | Line 176 — UC_4.6.4 = Controls (AI chat & Reduce motion), "Trader toggles AI Chat and Reduce Motion preferences, from a persistent panel shown across all Settings tabs"; endpoints POST /ai-preference, POST /accessibility-preference |
| 7 | BA / client answers, 2026-08-21 (QnA Init Docs — UC_4.6.1–4.6.4: Settings) | Q15 / C-10 confirms the BA-proposed branch verbatim: "skip call hoàn toàn — không gọi Gemini, không trừ token bucket, không push GEKKO_CHATTER" → BR_4.6.4.6 is no longer provisional. Q17 / C-13 reverses the v1 rule: "Cũng hiện motion, nhưng hiện motion chạy từ Certificate modal trực tiếp" — the cinematic is not suppressed; the trader lands directly on the Certificate modal and the motion plays from there → BR_4.6.4.8 rewritten. |
| 8 | Level Up Cinematic Scene Handling.pdf (client supply, added 2026-08-18) | §1 Client-Side Rendering — the sequence is "not a static video file… a highly performant, composite React component that renders live in the user's browser", built from a WebM/HEVC background layer, Lottie signature/vector assets, a Framer Motion dynamic data layer, a dynamically generated QR code resolving to PolygonScan, and a client-orchestrated audio stack. §3 Dashboard UI & Interaction Logic — "At the conclusion of the animation (the final certificate state), the UI will present an expanded share menu." Establishes that the Certificate is the terminal state of the animation, which is what makes the 2026-08-21 answer implementable. ⚠️ Not yet registered in References/Customer supplies/_SOURCE_MANIFEST.yaml. |
| 9 | Client answer, 2026-08-24 (Controls — Reduce Motion vs. Desk Manager) | Closes CTRL-EP-01. The BA's question offered a consolidated PUT /profile + ui_preferences.desk_manager_enabled design; the answer does not take it — see BR_4.6.4.2. Verbatim: "Reduce Motion: There is no backend API for this. UI animation performance depends entirely on the specific hardware the trader is currently using. A user may need to reduce motion on a weak travel laptop but keep the animations active on their powerful home desktop." — and: "AI Desk Manager Toggle: This must be a backend call. The Desk Manager commentary is generated asynchronously by our Node.js backend pinging the Gemini API. If the user turns this off, the backend must know immediately so we halt the background worker and stop spending API tokens to generate unused text." → BR_4.6.4.2 rewritten, BR_4.6.4.5 split by scope, §6 / §7 rewritten. |
| 10 | Client answer, 2026-09-03 ([CTRL-EP-05] — the purpose of ui_preferences) | Closes [CTRL-EP-03] and [CTRL-EP-04], the last two endpoint questions this UC carried. Verbatim: "'Reduce Motion' does not require an API and should be using local storage. The AI Preference Toggle has a dedicated backend endpoint because it controls core operational logic and costs, explicitly telling the Node.js middleware to stop pinging the Gemini API. Regarding the ui_preferences field: There are currently no active parameters to parse for this field today. It is intentionally included in the schema strictly as a future-proofing measure. Please maintain this column as an optional JSON object. This ensures the frontend team can easily save arbitrary cross-device layout preferences in future updates without requiring structural database schema changes later." → the contract in BR_4.6.4.2 is re-confirmed unchanged; the only new information is why the dormant ui_preferences column exists. |
| 6 | BA design set + direct BA instruction, 2026-08-12 | 2 screen frames (Controls panel on the Profile & Preferences tab / on the Billing tab, both tooltips visible) — Figma node 7878-116828. Source of the tooltip icon placement and of the panel's persistence across tabs. |
Design asset note: the frames above are not yet committed to
References/Wireframe/Stage 2/Settings/; this document cites the Figma node until the BA commits them (Ref:QnA_init_docs.mdC-18(b), carried over from A-19(b)).
1. Overview
| Field | Content |
|---|---|
| ID | UC_4.6.4 |
| Use Case | Settings — Controls (AI chat & Reduce motion) |
| Description | The Controls panel is a persistent block rendered alongside the tab list on every tab of the Account screen, holding exactly two independent preference toggles: Desk Manager (turns the AI commentary engine on/off) and Reduce Motion (an accessibility control that suppresses non-essential animation across the Dashboard). Each toggle commits immediately on flip — it never routes through the [Save Changes] button of the tab it is rendered next to, and never marks that tab's form dirty (Ref: BR_4.6.4.4). The two toggles differ fundamentally in where they commit: the Desk Manager flip is a backend call (POST /ai-preference), because the backend must know at once to halt its Gemini worker; the Reduce Motion flip has no backend API at all and is handled end to end by the frontend on the trader's current device (Ref: BR_4.6.4.2, Source: Client answer 2026-08-24). Consequently the Desk Manager preference is per user account and follows the trader everywhere, while Reduce Motion is per device — a deliberate choice, since animation performance depends on the hardware in front of the trader (Ref: BR_4.6.4.5). Both toggles behave identically for SIM and LIVE accounts (Ref: BR_4.6.4.9). |
| Zapier Flow | — |
| Zapier Table | — |
| 3rd Party | — |
Out of scope — "Concierge": the client names this category "Controls & Concierge" and the WBS UI note reads "Persistent Controls block (2 toggles) + Concierge entry point", but no source (RFQ V7, Dashboard V7, Zapier V7, wireframe) defines what the Concierge entry point is, what it does, or which endpoint serves it, and the client's own QnA row records the point as still ambiguous. This UC therefore covers only the two toggles. The unrelated "Digital Concierge" of Zapier Flow 38 (Discord bot monitoring #the-pit) belongs to UC_4.5.2 and is not this control (Ref: QnA_init_docs.md C-03, BA confirmed 2026-08-12).
Design: Figma — Controls & Concierge, node 7878-116828
2. Trigger
- The trader clicks the user chip in the sidebar footer (displaying their initials + level, e.g.
SD/Lvl 5) and selects Settings from the dropdown (Ref: UC_4.1.2 §2 Screen Description, row 6) → theAccountscreen renders and theControlspanel is visible immediately, on whichever tab is active (Ref: BR_4.6.4.1). - The trader flips either toggle (Ref: BR_4.6.4.4).
3. Pre-conditions
- The trader is authenticated and inside the Dashboard shell (Ref: UC_4.1.1 §1 Overview).
- A Users record exists for the trader, carrying
ai_enabled(Ref: BR_4.6.4.2). The Reduce Motion toggle has no server-side pre-condition — it reads the device's local store and works even before any backend call returns.
4. Post-conditions
- Desk Manager: the Users record reflects the flipped value in
ai_enabled. No other column is touched (Ref: BR_4.6.4.2). - Reduce Motion: the value is persisted in the browser's local store on the trader's current device. No backend record changes — no API call is made and no column is written (Ref: BR_4.6.4.2).
- Both preferences apply immediately in the current session with no page reload. The Desk Manager value then applies on every subsequent session and every device the trader logs in from; the Reduce Motion value applies on every subsequent session of that device/browser only (Ref: BR_4.6.4.5).
- Downstream consumers observe the new value: the Desk Manager widget (Ref: BR_4.6.4.6) and every animated surface of the Dashboard (Ref: BR_4.6.4.7).
5. Basic Flow — Toggle Desk Manager
- The trader lands on the
Accountscreen. The frontend callsGET /profileand readsai_enabledfrom the response; the Reduce Motion position comes from the device's local store, not from this call (Ref: BR_4.6.4.2). - The
Controlspanel renders both toggles at their stored positions, each with its own tooltip icon (Ref: BR_4.6.4.3). - The trader flips the Desk Manager toggle.
- The toggle moves to its new position immediately and is set to Disabled while the request is in flight (Ref: BR_4.6.4.4).
- The frontend calls
POST /ai-preferencewith{user_id, ai_enabled}. - On HTTP 200: the toggle is re-Enabled at its new position. No success toast and no confirmation popup is shown (Ref: BR_4.6.4.4).
- The Desk Manager widget on the Command tab reacts to the new value (Ref: BR_4.6.4.6).
6. Alternative Flow — Toggle Reduce Motion
🔴 Changed 2026-08-24 — this flow no longer touches the backend. The client's answer is verbatim: "Reduce Motion: There is no backend API for this." The endpoint
POST /accessibility-preferenceand the stored fieldui_preferences.reduce_motionare withdrawn; the frontend owns this toggle end to end (Ref: BR_4.6.4.2).
- Step 1 of §5 is identical, except that the Reduce Motion toggle takes no value from
GET /profile— its position is read from the device's local store (Ref: BR_4.6.4.2). - Step 2 of §5 is identical — the panel renders both toggles with their tooltips (Ref: BR_4.6.4.3).
- The trader flips the Reduce Motion toggle.
- The toggle moves to its new position and stays Enabled. There is no request in flight, therefore no in-flight guard and no Disabled interval (Ref: BR_4.6.4.4).
- The frontend writes the new value to the device's local store. No API call is issued — nothing about this flip reaches the backend (Ref: BR_4.6.4.2).
- No success toast, no confirmation popup — the client explicitly ruled out the blocking-modal pattern used by the breach/reset flows (Ref: BR_4.6.4.4).
- The new motion rule set applies across the Dashboard immediately, without a page reload (Ref: BR_4.6.4.7, BR_4.6.4.8).
Because nothing leaves the browser, this flow has no network failure path: no error toast, no position revert, and no state in which the rendered toggle and the persisted value disagree (Ref: §7 Exceptional Flow row 3).
7. Exceptional Flow
| # | Condition | System behaviour |
|---|---|---|
| 1 | GET /profile fails (network error or HTTP 5xx) at §5 step 1 | The Desk Manager toggle renders Disabled with no position asserted, so the trader cannot flip a control whose stored value is unknown. The Reduce Motion toggle is unaffected — its value never came from that call — and renders at its device-stored position, fully Enabled and usable (Ref: BR_4.6.4.2). Error toast Ref: TE-SYS-01. |
| 2 | POST /ai-preference fails (network error or HTTP 5xx) | The Desk Manager toggle reverts to its previous position and is re-Enabled. Error toast Ref: TE-SYS-01. No value is persisted (Ref: BR_4.6.4.4). |
| 3 | Any network or backend failure, while the trader flips Reduce Motion | No effect on this toggle. It issues no request, so there is nothing to fail: the flip is applied locally, persisted locally, and the motion rule set changes as normal even with the backend completely unreachable (Ref: BR_4.6.4.2). No error toast is shown for this toggle. |
| 4 | The device's local store is unavailable or blocked (private/incognito mode, storage disabled by browser policy, quota exceeded) | The Reduce Motion toggle stays functional for the current session only — the flip applies to the running Dashboard immediately, but is not carried into the next load on that device, which starts again at the default of BR_4.6.4.5. No error toast: the trader is not blocked from anything, and no data is lost that the system ever promised to keep. |
| 5 | Session token expired (HTTP 401) on the Desk Manager call | Error toast Ref: TE-AUTH-01 → redirect to the Auth0 login. The flipped Desk Manager position is discarded. A Reduce Motion value already written to the device's local store survives the redirect and the re-login — it is not tied to the session. |
| 6 | The trader flips the Desk Manager toggle again while its request is still in flight | Not possible — that toggle is Disabled for the duration of the request (Ref: BR_4.6.4.4). This is the concurrency guard against a double-click writing two conflicting values. It does not apply to Reduce Motion, which has no request to guard. |
8. Business Rules
BR_4.6.4.1: Persistent Panel — Rendered on Every Settings Tab
The Controls panel sits below the tab list of the Account screen and is rendered on all four tabs of that screen, not just one (Source: References/WBS/[BA Internal] Stacktrading.csv line 176 — "from a persistent panel shown across all Settings tabs"; confirmed by the design frames, which show the identical panel on both the Profile & Preferences and the Billing tab).
Switching tabs never re-mounts, resets, or hides the panel, and never changes either toggle's position. The tab list itself and its tab → owning UC mapping belong to UC_4.6.2 BR_4.6.2.1, not to this UC.
BR_4.6.4.2: Data Contract — One Toggle Is a Backend Call, the Other Is Not
🔴 Changed 2026-08-24 (CTRL-EP-01 closed). The two toggles are not symmetric. v1–v3 specified a matching pair of write endpoints; the client's answer withdraws one of them outright: "Reduce Motion: There is no backend API for this. UI animation performance depends entirely on the specific hardware the trader is currently using." — while reaffirming the other: "AI Desk Manager Toggle: This must be a backend call… If the user turns this off, the backend must know immediately so we halt the background worker and stop spending API tokens to generate unused text."
A consolidated-endpoint proposal was raised in the same exchange and was not adopted. Ahead of this answer the BA put a single PUT /profile design to the client — build neither POST /ai-preference nor POST /accessibility-preference, and push both toggles as booleans inside one ui_preferences object ({"ui_preferences": {"desk_manager_enabled": true, "reduce_motion": true}}) — noting that RFQ V7 already specifies an AI Preference Toggle API while nothing in any source specifies one for Reduce Motion. The client's reply does not take that route: Reduce Motion loses its API entirely and the Desk Manager stays a backend call on the RFQ V7 contract. The consolidated proposal is therefore withdrawn and desk_manager_enabled is not a field in this specification — recorded here so the option is not re-opened without a new client decision (Ref: QnA_init_docs.md — Trả lời QnA 2026-08-24).
✅ Re-confirmed 2026-09-03 —
[CTRL-EP-03]and[CTRL-EP-04]are now formally closed. Asked again which endpoint and which field the Desk Manager uses, the client answered: "The AI Preference Toggle has a dedicated backend endpoint because it controls core operational logic and costs, explicitly telling the Node.js middleware to stop pinging the Gemini API." Two consequences, both of which this document already specifies — the value of the answer is that they can no longer be re-opened:
- The endpoint is
POST /ai-preference([CTRL-EP-03]closed). The consolidatedPUT /profileroute is not adopted.- The field is the flat column
users.ai_enabled([CTRL-EP-04]closed), notui_preferences.desk_manager_enabled. The Gemini background worker therefore queries a boolean column, not JSONB — no change to the DB schema or the worker's query is needed.The same answer explains the leftover
ui_preferencescolumn: it is kept "strictly as a future-proofing measure" with "no active parameters to parse… today", so BE must "maintain this column as an optional JSON object" for future frontend-only cross-device layout preferences. It is dormant by design — no toggle in this UC belongs in it (Ref: BR_4.6.2.15).
| Toggle | Persisted in | Written by | Payload | Read on load from |
|---|---|---|---|---|
| Desk Manager | users.ai_enabled (Boolean) — server-side | POST /ai-preference | {user_id, ai_enabled (Boolean)} → HTTP 200 OK | GET /profile |
| Reduce Motion | Browser local storage on the trader's current device — no server-side column | Frontend only — no endpoint, no request, no backend involvement | — (nothing is transmitted) | the device's local store |
Desk Manager — confirmed by source, by the client, and by the final contract of 2026-08-27. POST /ai-preference and the ai_enabled column are confirmed by RFQ_ Stack Trading Prop Tech V7.pdf §API table ("AI Preference Toggle") and Zapier Integration V7.pdf §Users table schema. The 2026-08-24 answer gave the reason the call is mandatory rather than optional — the flag drives a background worker that costs money while it runs (Ref: BR_4.6.4.6) — and the 2026-08-27 answer fixes the contract verbatim:
POST /ai-preference
{
"user_id": "UUID",
"ai_enabled": true
}
→ 200 OKThe endpoint is explicitly not folded into PATCH /profile ("không gộp chung vào profile nữa"), which is what keeps the profile endpoints reduced to a single field each (Ref: BR_4.6.2.15).
Reduce Motion — three things are withdrawn, not merely deferred:
| Withdrawn | Was specified in | Status now |
|---|---|---|
POST /accessibility-preference | WBS line 176; UC_4.6.4 v1–v3 | Not built, not called. No backend endpoint exists for this toggle. The WBS endpoint column must be corrected (BA follow-up, outside this UC) |
users.ui_preferences.reduce_motion | QnA_init_docs.md C-05 (applied by default) | Not written and not read by this UC. The ui_preferences JSON column stays in the Zapier Users schema, but this UC no longer has any consumer for it — and that is deliberate: the client confirmed on 2026-09-03 that the column is kept "strictly as a future-proofing measure" with "no active parameters to parse… today", to be "maintain[ed] as an optional JSON object" so the frontend can later store cross-device layout preferences without a schema migration ([CTRL-EP-05]). No toggle in this UC may be placed inside it. |
reduce_motion inside the GET /profile response | QnA_init_docs.md C-04 (applied by default) | Not consumed. The frontend ignores it even if the field is present |
Read path — two endpoints, two distinct jobs (confirmed 2026-08-28). The Desk Manager needs both, and they are not interchangeable:
| Endpoint | When it fires | Job |
|---|---|---|
GET /profile | Once, on first load of the Settings screen | Read the stored position so the toggle renders On or Off correctly. Without it the toggle has no state to initialise from and would render a guess |
POST /ai-preference | On each flip, silently in the background | Write the new value to the database (BR_4.6.4.4) |
ai_enabled must be added to the GET /profile response — mandatory, and the only outstanding backend item in this UC. The endpoint currently returns {profile_photo_url, email, phone_number, ui_preferences} (Source: RFQ_ Stack Trading Prop Tech V7.pdf §Page 36), which does not include it. The client's wording is unambiguous about the severity: having the field is "bắt buộc để Frontend khởi tạo đúng trạng thái ON/OFF của toggle Desk Manager ngay lúc load màn hình". Until BE ships it, this toggle cannot be built correctly (Ref: QnA_init_docs.md C-04).
Where it sits in the response: top-level
ai_enabled, not insideui_preferences. The 2026-08-28 answer allows either ("có trườngai_enabled(hoặc nằm trongui_preferences)"). This document specifies the top-level field, because the write side already targets the dedicatedusers.ai_enabledcolumn rather than the JSON blob — nesting the read insideui_preferenceswould split one value across two shapes.ui_preferencesremains dormant with no reader or writer anywhere in the Settings module (Ref: BR_4.6.2.15) — and the client confirmed on 2026-09-03 that this is intended, which removes the alternative: nestingai_enabledinside a column the client describes as having "no active parameters to parse" is no longer a legitimate reading of the answer. Top-levelai_enabledis now the only shape. The same answer's alternative endpoint nameGET /user-profileis likewise not adopted —GET /profileis the name every other UC in this cluster already reads from.
Local persistence key — confirmed 2026-08-27. The Reduce Motion value is stored in localStorage under the key stacktrading.reduce_motion, type Boolean, so it survives page reloads and new sessions on that device. The v4 caveat that the key name was BA-proposed and "not a client-confirmed contract" is withdrawn — the key is now part of the specification. Absence of the key means the trader has never set the toggle on this device, which resolves to the default Off (Ref: BR_4.6.4.5).
Boundary with UC_4.6.2 is unchanged. UC_4.6.2 still neither reads nor writes ui_preferences, even though the RFQ's Update User Profile signature lists it as an optional parameter (Ref: BR_4.6.2.11; QnA_init_docs.md A-18). Nothing moves to UC_4.6.2 as a result of this change — ui_preferences simply loses its only documented consumer, which the client confirmed on 2026-09-03 is the intended end state ([CTRL-EP-05]).
BR_4.6.4.3: Labels and Tooltips
| Toggle label | Tooltip content |
|---|---|
| Desk Manager | "Enable or disable the AI Desk Manager assistant." |
| Reduce Motion | "Disable non-essential animations and transitions." |
Both strings are client-authored and are used verbatim (Source: QnA from clients — Settings, Controls & Concierge UI/UX row, Adrian Stack 2026-08-10). The trailing period is intentional here: these are descriptive tooltips, not error messages, so the no-trailing-period rule for error strings does not apply.
Label change: the toggle previously called AI Chat in the RFQ and in the earlier wireframes is renamed Desk Manager, aligning the Settings label with the widget name used on the Command tab. The underlying data contract is unchanged — still users.ai_enabled via POST /ai-preference (Source: same QnA row — "LABEL CHANGE: rename the toggle in the Settings / Controls section from 'AI Chat' to 'Desk Manager'… The underlying data contract is unchanged").
Tooltip trigger and dismissal behaviour follow Ref: CR-10.
BR_4.6.4.4: Immediate Commit on Flip — No Save Button, No Confirmation
Each toggle is autonomous. Flipping it is the whole interaction:
- Commit point: the flip itself. There is no
[Save Changes]button, no[Cancel], and no staged/dirty state for these two controls (Source: QnA from clients — Settings,Controls & ConciergeTrigger row — "applied immediately (real-time), no reload needed"). - No confirmation popup on either toggle, in either direction. The client explicitly ruled out the blocking-modal pattern used by the breach/reset flows (Source: same QnA category, Definition row — "NO confirmation popup needed - no blocking modal like the breach/reset pattern").
- Optimistic display (Desk Manager): the toggle moves to its new position on click, before the response returns, so the interaction reads as instant.
- In-flight guard (Desk Manager only): while its request is outstanding, that toggle is Disabled. This prevents a rapid double-click from issuing two writes whose responses could land out of order (Ref: §7 row 6). The other toggle stays Enabled — the two controls are fully independent and never block each other.
- Reduce Motion has no in-flight state at all. It issues no request, so it is never Disabled, never optimistic (the local write is the commit), and never reverts. A rapid double-flip simply lands on whichever position the trader stopped at — there is no ordering problem to guard against (Ref: BR_4.6.4.2).
- On failure: applies to the Desk Manager only — the toggle reverts to its previous position and an error toast is shown (Ref: §7 rows 2 and 5). Reduce Motion has no failure path (Ref: §7 row 3).
- On success: no toast on either toggle. Ref: TS-DASH-01 is reserved for a tab's
[Save Changes]action and does not fire here.
Interaction with the host tab's form. The panel is completely independent of whichever tab is rendered next to it:
- Flipping a toggle does not mark the host tab's form dirty (Ref: BR_4.6.2.8).
- Flipping a toggle while the host tab's form is dirty does not trigger the unsaved-changes warning Ref: CF-01 — no navigation occurs and no staged form value is touched. The form keeps every entered value and its
[Save Changes]button keeps its current state (Ref: BR_4.6.2.9).
BR_4.6.4.5: Preference Scope — Desk Manager Per User, Reduce Motion Per Device
🔴 Changed 2026-08-24 (C-15 split). v1–v3 specified both values as per-user, server-side. That now holds for the Desk Manager only. The client's stated reason for making Reduce Motion device-local is a hardware one, not a storage-convenience one: "UI animation performance depends entirely on the specific hardware the trader is currently using. A user may need to reduce motion on a weak travel laptop but keep the animations active on their powerful home desktop." A per-account value would be wrong behaviour, not merely a different implementation — it would carry the laptop's setting onto the desktop.
| Toggle | Scope | Persisted where | Follows the trader to another device? |
|---|---|---|---|
| Desk Manager | Per user account | Users record, server-side | Yes — same value on every device, browser, and session |
| Reduce Motion | Per device + browser profile | That browser's local store | No — by design |
Consequences:
- Desk Manager.
localStorageis never the source of truth for this toggle; the Users record is. - Reduce Motion. The device's local store is the source of truth. Turning it on for a travel laptop leaves the desktop untouched, and vice versa. There is no "sync my accessibility settings" behaviour and none is expected.
- A new device, a new browser, or a cleared browser store starts Reduce Motion at its default (below) — the trader sets it once per device. This is not data loss: nothing was ever promised to persist beyond that device.
- Both values apply to the authenticated Dashboard only (
UC_4.x). The public marketing site (UC_1.x) is out of this UC's scope and does not read either value — even though the Reduce Motion value now lives in the browser rather than the database; if the marketing site needs motion reduction, that remains an OS-levelprefers-reduced-motionconcern at the CSS layer, unrelated to this toggle (Ref:QnA_init_docs.mdC-14). - Multi-tab behaviour: if the trader has the Dashboard open in more than one tab, flipping either toggle in one tab does not update the other tab's rendered toggle until that tab is reloaded. Preference changes are not broadcast — not over WebSocket for the Desk Manager, and no cross-tab
storage-event sync is required for Reduce Motion. This is accepted behaviour, not a defect: the persisted value is authoritative and the next load in any tab reflects it (Ref:QnA_init_docs.mdC-15, as amended 2026-08-24).
Default values — Desk Manager ON, Reduce Motion OFF.
🔴 Reduce Motion default reversed 2026-08-27 — C-07 is superseded. v1–v4 defaulted Reduce Motion to On, on BA instruction of 2026-08-12. That is withdrawn. The new default is Off, and the stated reason is the one this document flagged as the risk of the old default: a new trader must see "đầy đủ hiệu ứng Level-Up Cinematic hoành tráng và nhấp nháy đỏ
CRITICALcủa Market Status" — the motion-first experience the RFQ's cinematic and motion-reference material is built around. This closes the open question carried since 2026-08-12 and re-raised on 2026-08-24 when the per-device scope made the old default repeat on every machine.
| Toggle | Default position | Applies to | Meaning at that default |
|---|---|---|---|
| Desk Manager | On (ai_enabled = true) | A brand-new account, once | AI commentary runs from the first qualifying event |
| Reduce Motion | Off | Every device the trader opens the Dashboard on for the first time — not just their first account visit | The motion rule set of BR_4.6.4.7 is inactive — the Dashboard renders its full motion presentation, including the Level-Up Cinematic (BR_4.6.4.8) and the CRITICAL blinking treatment on the Market Status widget — until the trader turns the toggle on for that device |
The per-device scope now cuts the other way, and that is the point. Under the old On default, a trader using three machines had to switch the toggle off three times to get the experience the product is designed around. Under Off, the trader who actually needs motion reduction — the "weak travel laptop" case that made this preference device-local (BR_4.6.4.5 opening note) — switches it on for that one machine, and their powerful desktop is untouched. Default and scope now point the same direction: the exception is configured where it applies, and nowhere else.
Once the trader changes a toggle, that choice is what loads afterwards — the Desk Manager choice on every subsequent login anywhere, the Reduce Motion choice on every subsequent session of that same device/browser. Neither is ever re-defaulted by a new session or a level promotion; a new device re-applies only the Reduce Motion default, never the Desk Manager one.
The browser's prefers-reduced-motion (operating-system accessibility setting) is not consulted at any point — neither to seed the first load nor to override the stored value. The default above is fixed (BA decision, 2026-08-12 — Ref: QnA_init_docs.md C-08, which supersedes the earlier OS-seeding proposal). This decision stands after both the 2026-08-24 scope change and the 2026-08-27 default reversal: moving Reduce Motion into the browser puts the OS signal within easy reach of the frontend, but it is still deliberately not read.
⚠️ Note for anyone reading the changelog. The default has returned to the Off value it held in v1, but only half of the v1 behaviour is back. v1 paired
Offwith seeding from the OSprefers-reduced-motionsetting; v2 removed both. v5 restores theOffdefault and leaves the OS seeding removed — a trader whose operating system requests reduced motion still sees full motion until they flip this toggle themselves. That is deliberate (C-08), not an oversight in the reversal.
✅ The consequence flagged in v3–v4 is resolved, not merely accepted. Those versions recorded that an On default inverted the motion-first presentation the RFQ is built around, and asked for client confirmation. The answer of 2026-08-27 is to flip the default, so the concern no longer applies: a new trader on a new device sees the full motion presentation — the Level-Up Cinematic (BR_4.6.4.8) and the
CRITICALblinking on Market Status (BR_4.6.4.7) — without touching a setting.What changes for QC: the default test path is now the full-motion path, and the reduced-motion rule set of BR_4.6.4.7 is reached only after an explicit opt-in. Any test written against v1–v4 that assumed a fresh browser starts with motion suppressed is wrong against this version and must be re-baselined.
BR_4.6.4.6: Downstream Effect of ai_enabled
ai_enabled | Backend | Frontend |
|---|---|---|
true | The async worker evaluates the 15 trigger events, calls the API, and pushes {text, vibe, event_type} over the GEKKO_CHATTER channel | The Desk Manager widget renders the commentary card stack |
false | The worker checks the flag before calling the LLM and skips the call entirely — no Gemini request, no token consumed, no GEKKO_CHATTER push | The Desk Manager widget renders the "Disabled in settings" placeholder instead of the card stack |
The widget itself is owned elsewhere — SIM: UC_4.8.3 (Ref: BR_4.8.3.3, which already defines the "Disabled in settings" placeholder); LIVE: UC_4.12.3 (not yet documented in this repo). This UC owns only the flag.
Further consequences of turning the Desk Manager off and back on:
- Commentary history is discarded on switch-off. Every card generated before the flag went
falseis cleared — the toggle does not merely hide the stack, it drops it. When the trader switches the toggle back on, the widget starts from the same empty state a brand-new account sees ("No data available"— Ref: BR_4.8.3.3) and repopulates from scratch as new events fire. There is no archive, no restore, and no way to recover the discarded cards from the UI (Source: BA instruction, 2026-08-12 — Ref:QnA_init_docs.mdC-11). - No backfill. The engine is stateless (Source: RFQ_ Stack Trading Prop Tech V7.pdf §3.2.1). Events that fired while the flag was
falsegenerate no commentary and are never replayed. Commentary resumes from the next qualifying event onward. - No Token Bucket consumption while off. The 10-critiques-per-hour-per-user budget (Ref: BR_4.8.3.2) is only drawn down by an actual LLM call, which does not happen while the flag is
false.
Switching the Desk Manager off is therefore destructive to commentary history and not undoable — the only reversible part is the feature itself, not the cards it had already produced.
✅ Re-confirmed 2026-08-24 — the reason the call is mandatory. The client's answer states why the Desk Manager toggle cannot follow the Reduce Motion pattern: "This must be a backend call. The Desk Manager commentary is generated asynchronously by our Node.js backend pinging the Gemini API. If the user turns this off, the backend must know immediately so we halt the background worker and stop spending API tokens to generate unused text." Two requirements follow: (1) the backend is notified immediately on flip — the value cannot be held client-side and reconciled later; (2) the worker is halted, not merely muted — the cost saving is the point of the flag, so an implementation that keeps generating commentary and discards it at the frontend fails this rule.
✅ Confirmed 2026-08-21 (C-10 closed). The branch above is no longer BA-proposed. The answer is verbatim: "skip call hoàn toàn — không gọi Gemini, không trừ token bucket, không push
GEKKO_CHATTER." All three consequences are therefore binding, not inferred: (1) no Gemini request is issued whileai_enabled = false, so the LLM cost of a trader with the toggle off is zero; (2) the Token Bucket of BR_4.8.3.2 is not drawn down; (3) noGEKKO_CHATTERmessage is published at all — the widget's"Disabled in settings"placeholder is driven by the flag the frontend already reads, never by an empty push. The flag check happens in the worker, before the LLM call — hiding the output at the frontend while still calling Gemini is explicitly not the specified behaviour.
BR_4.6.4.7: Reduce Motion — Rule Set
When Reduce Motion is On — a value the frontend reads from the device's local store, never from the backend (Ref: BR_4.6.4.2) — the Dashboard applies the following three rules to every animated surface it renders (Source: QnA from clients — Settings, Controls & Concierge Definition + Business logic rows, Adrian Stack 2026-08-10):
| # | Rule | Applies to |
|---|---|---|
| 1 | Disable continuous motion — turn off all infinite loops, background movement, and continuous pulsing effects | Any animation that repeats indefinitely without a user action |
| 2 | Disable parallax — turn off all scroll-linked parallax and depth effects | Any animation driven by scroll position |
| 3 | Simplify transitions — convert heavy positional animations (slides, wipes, bounces) into fast opacity fades or instant state renders | Any animation that moves an element through space to reach its final state |
This is a scope directive by category, not an exhaustive component list — the client stated the rule set, not the inventory. Every UC that owns an animated surface is responsible for honouring these three rules; the rule set is defined once here and referenced, never restated.
Known downstream surfaces already documented in this repo (non-exhaustive — any animation added later falls under the same rules automatically):
| Surface | Owning UC | Behaviour when Reduce Motion is On |
|---|---|---|
Market Status widget, CRITICAL severity — high-alert blinking state | BR_4.1.3.4 | Rule 1 — the blink stops; the widget renders the CRITICAL state statically. The state, its text, and its severity are unchanged, so no warning information is lost |
| Days Remaining counter — pulse/glow when under 10 days remain | UC_4.7.4 (SIM) / UC_4.12.1 (LIVE) | Rule 1 — the pulse/glow is dropped; the numeric countdown value is unchanged |
| Level-Up Cinematic | Ref: BR_4.6.4.8 | Exception — the three rules above do not apply. The celebration still plays; only its starting point moves. See BR_4.6.4.8 |
Animations already in progress when the toggle is flipped to ON settle at their final state rather than rewinding or cutting mid-frame.
Turning the toggle back OFF restores normal motion from that moment forward; animations that were suppressed while it was ON are not replayed.
BR_4.6.4.8: Reduce Motion and the Level-Up Cinematic
🔴 Reversed 2026-08-21 (C-13 re-answered). v1/v2 specified that
reduce_motion = trueskipped the cinematic entirely — the WebM/HEVC background and Lottie overlays were "not loaded at all", the staggered text reveal and animated QR were not played, and the Certificate modal rendered directly "at the state the cinematic would have concluded on". That is withdrawn. The answer is: "Cũng hiện motion, nhưng hiện motion chạy từ Certificate modal trực tiếp" — the motion still plays; what changes is where it starts from.
The Level-Up celebration is the one surface where the three rules of BR_4.6.4.7 do not apply as written. This rule overrides them for this surface only.
When a LEVEL_PROMOTION event fires while Reduce Motion is On on the device the trader is using (Ref: BR_4.6.4.5 — the same trader may see the full pre-roll on another device):
- The frontend still redirects the trader to the
Commandtab, exactly as it does with motion enabled (Source: QnA from clients — Settings,Level-Up & CertificateUI/UX row, Adrian via Figma comment). - The trader lands directly on the Certificate modal. The full-screen cinematic is not played as a pre-roll ahead of it. The Certificate is the sequence's terminal state in any case — "At the conclusion of the animation (the final certificate state), the UI will present an expanded share menu" (Source:
Level Up Cinematic Scene Handling.pdf§3) — so nothing in the narrative is lost by starting there. - The motion still runs, anchored at that modal. Reduce Motion On does not mean "no animation" on this surface: the celebration is preserved and plays from the Certificate modal rather than building up to it.
- Every action on the modal is unaffected: Share (X, LinkedIn, Facebook, Reddit, Copy Link), Download Certificate, and the Redacted PII toggle all behave as normal.
- The public shareable route (
/milestone/[unique-id]) is unaffected in every case. It is an unauthenticated route (Source:Level Up Cinematic Scene Handling.pdf§2), so it never reads the trader's Reduce Motion value — which in any case now lives only in the trader's own browser and is not available to any other visitor's session (Ref: BR_4.6.4.2) — and always mounts the full cinematic component — a visitor's experience of a shared milestone does not depend on the owner's accessibility preference.
The trader therefore loses neither the milestone nor the celebration — only the run-up to it.
⚠️ Open — which layers play, and where they render. The answer settles that motion plays and where it starts; it does not enumerate what plays. The sequence is a live composite of five layers — pre-rendered WebM/HEVC background with alpha, Lottie signature/vector overlays, a Framer Motion dynamic data layer, a dynamically generated QR resolving to PolygonScan, and a client-orchestrated audio stack (Source:
Level Up Cinematic Scene Handling.pdf§1). Four questions must be closed before this surface can be built:
# Question Why it matters 1 Does the motion render inside the modal frame, or full-screen behind it? Determines the component boundary and the modal's own entry animation 2 Which of the five layers play — all of them, or the Framer Motion / Lottie layers without the WebM background? The < 300 KBperformance budget and the RFQ's "reduced-motion guards" were what drove the v1 "load nothing" rule. If the background video now loads regardless, that budget no longer holds3 Does the audio layer play? Autoplay is gated behind a user-interaction unmute ( Level Up Cinematic Scene Handling.pdf§1), and unexpected sound is exactly the class of thing a reduced-motion preference is normally taken to suppress4 If motion plays in both toggle states, what does the toggle actually change on this surface beyond skipping the pre-roll — and is a distinct reduced variant needed at all? Determines whether the cinematic needs one build or two This UC owns the flag, not the cinematic. The four answers belong in the Level-Up & Certificate UC when it is authored; this rule records what has been decided so far and must be re-read there (Ref:
QnA_init_docs.mdC-13). Note also thatLevel Up Cinematic Scene Handling.pdfis not yet registered in_SOURCE_MANIFEST.yaml— a BA follow-up outside this UC.
BR_4.6.4.9: Identical for SIM and LIVE — No Environment Branching
Both toggles behave identically for SIM (Evaluation) and LIVE (funded) accounts: same labels, same tooltips, same endpoints, same storage, same downstream effects (Source: QnA from clients — Settings, Controls & Concierge category, LIVE column: "Same as SIM for all 3 controls. Same AI engine, same toggle logic").
No rule in this UC is gated on account type, level, or environment. The ai_enabled flag reaches whichever Desk Manager widget applies to the trader's environment — SIM UC_4.8.3 or LIVE UC_4.12.3 — through the same flag (Ref: BR_4.6.4.6).
BR_4.6.4.10: Toggles Stay Available Regardless of Account Status
Both toggles remain Enabled and writable for as long as the trader can reach the Dashboard — including while the account is in a Hard Breach / Frosted Glass state (Ref: UC_4.10.2), in ReadOnly after a resignation, or mid-promotion. These are personal accessibility and display preferences, not trading actions, so no account-status gate applies to them.
The Desk Manager widget may independently stop rendering under those states — that is the widget's own visibility rule (Ref: BR_4.8.3.3) and is not affected by, and does not affect, the toggle in this panel.
There is no rate limit, cool-down, audit-log entry, or notification email on either toggle. A Desk Manager flip writes the Users record and nothing else; a Reduce Motion flip writes the device's local store and nothing else — it produces no server-side trace of any kind, so the value cannot be inspected, reported on, or supported from the admin side (Ref: BR_4.6.4.2).
9. Screen Description
9.1 Account screen — Controls panel
| No. | Field Name | Field Type | Displaying rule / Behaviour rule |
|---|---|---|---|
| 1 | Panel header | Label | Displaying rule:- Static text: "Controls". Rendered on every tab of the Account screen, below the tab list — Ref: BR_4.6.4.1. Overflow: not applicable, fixed string. |
| 2 | Desk Manager — label | Label | Displaying rule:- Static text: "Desk Manager". Renamed from "AI Chat" per client direction — Ref: BR_4.6.4.3. Overflow: Truncate with tooltip. |
| 3 | Desk Manager — tooltip icon | Button (Icon) + Tooltip | Displaying rule:- Always rendered, immediately after the label. Tooltip content: "Enable or disable the AI Desk Manager assistant." — Ref: BR_4.6.4.3. Static text only, no interactive element inside.Behaviour rule:- Desktop: hover to show, auto-dismiss on cursor leave. Mobile: tap to show, tap outside to dismiss. Ref: CR-10. |
| 4 | Desk Manager — toggle | Toggle/Switch | Displaying rule:- Two positions, On/Off, bound to users.ai_enabled — Ref: BR_4.6.4.2. Position on load comes from GET /profile; default for a new account is On, and once the trader has changed it, their stored choice loads on every later login — Ref: BR_4.6.4.5.- Enabled in every account status — Ref: BR_4.6.4.10. Temporarily Disabled only while its own request is in flight — Ref: BR_4.6.4.4.Behaviour rule:- On flip: moves position immediately, then calls POST /ai-preference with {user_id, ai_enabled}. This is a backend call and is mandatory — the backend must learn of the change immediately in order to halt its Gemini worker — Ref: BR_4.6.4.2. No confirmation popup, no success toast.- On failure: reverts position + Ref: TE-SYS-01 — Ref: §7 Exceptional Flow.Impact:- Writes users.ai_enabled. Off → backend halts the background worker and stops calling the Gemini API and the Desk Manager widget on the Command tab keeps its header but replaces the card stack with the "Disabled in settings" placeholder. The existing commentary history is discarded, so switching back on restarts the widget from its empty state — the discarded cards are not recoverable. Commentary missed while off is never backfilled — Ref: BR_4.6.4.6. The feature itself is reversible at any time; its past cards are not. Does not mark the host tab's form dirty — Ref: BR_4.6.4.4. |
| 5 | Reduce Motion — label | Label | Displaying rule:- Static text: "Reduce Motion". Overflow: Truncate with tooltip. |
| 6 | Reduce Motion — tooltip icon | Button (Icon) + Tooltip | Displaying rule:- Always rendered, immediately after the label. Tooltip content: "Disable non-essential animations and transitions." — Ref: BR_4.6.4.3. Static text only.Behaviour rule:- Same trigger/dismiss behaviour as row 3. Ref: CR-10. |
| 7 | Reduce Motion — toggle | Toggle/Switch | Displaying rule:- Two positions, On/Off, held entirely on the frontend — there is no backend field behind this control — Ref: BR_4.6.4.2. Position on load is read from the device's local store key stacktrading.reduce_motion, not from GET /profile; default on a device that has no stored value is Off (full motion plays) — reversed 2026-08-27 — and once the trader has changed it, that choice loads on every later session of that same device/browser — Ref: BR_4.6.4.5.- Enabled in every account status — Ref: BR_4.6.4.10. Never Disabled: there is no request in flight to wait for — Ref: BR_4.6.4.4.Behaviour rule:- On flip: moves position immediately and the frontend writes the value to the device's local store. No API call is made — Ref: BR_4.6.4.2. No confirmation popup, no success toast.- No failure path and no revert — nothing is sent over the network, so the toggle keeps working with the backend unreachable — Ref: §7 Exceptional Flow row 3. If the local store is blocked (incognito, storage disabled), the flip still applies for the current session but is not carried into the next load — Ref: §7 row 4.Impact:- Writes nothing server-side. On → applies the three-rule motion set across the whole Dashboard immediately, no reload — Ref: BR_4.6.4.7; the Level-Up celebration still plays but starts from the Certificate modal instead of as a full-screen pre-roll — Ref: BR_4.6.4.8. Affects this device only — the trader's other devices are untouched — Ref: BR_4.6.4.5. Reversible at any time; suppressed animations are not replayed. Does not mark the host tab's form dirty — Ref: BR_4.6.4.4. |
Changelog
| Date | Version | Updated item | Before | After | Notes |
|---|---|---|---|---|---|
| 2026-08-24 | v4 | BR_4.6.4.2 — rewritten (retitled "One Toggle Is a Backend Call, the Other Is Not") · BR_4.6.4.5 — rewritten (retitled "Desk Manager Per User, Reduce Motion Per Device") · §6 Alternative Flow — rewritten · §7 Exceptional Flow — rows 1, 3–6 rewritten, one row added · BR_4.6.4.4 · BR_4.6.4.6 — client rationale added · BR_4.6.4.7 / BR_4.6.4.8 — wording de-coupled from the withdrawn field · BR_4.6.4.10 · §1 Overview · §3 · §4 · §9.1 rows 4 & 7 · Document References rows 3, 5, 9 | The two toggles were symmetric: Reduce Motion wrote users.ui_preferences.reduce_motion through POST /accessibility-preference, was read back from GET /profile, was Disabled while its request was in flight, reverted its position with Ref: TE-SYS-01 on HTTP 5xx, and was scoped per user, server-side — following the trader across every device (C-15). §9.1 row 7 still carried the pre-v3 claim that the Level-Up Cinematic is "skipped". | Reduce Motion has no backend API. The endpoint, the stored field, and the read path are withdrawn; the frontend owns the toggle end to end, persisting it in the browser's local store per device / per browser profile, with no request, no in-flight guard, no failure path, and no server-side trace. Its default of On now applies per device rather than once per account. Desk Manager is unchanged and re-confirmed as a mandatory backend call, with the client's cost rationale recorded in BR_4.6.4.6: the worker must be halted, not muted. §7 gains a row for a blocked local store (incognito / storage disabled). §9.1 row 7 also corrected to match the v3 cinematic reversal. | Client answer, 2026-08-24 — closes CTRL-EP-01, which had been open since v1 (Ref: QnA_init_docs.md C-04, C-06, C-18(a), open item #1). Verbatim: "Reduce Motion: There is no backend API for this. UI animation performance depends entirely on the specific hardware the trader is currently using. A user may need to reduce motion on a weak travel laptop but keep the animations active on their powerful home desktop." / "AI Desk Manager Toggle: This must be a backend call… we halt the background worker and stop spending API tokens." Amends C-15 (per-user, server-side) for the Reduce Motion toggle only. Also withdraws the consolidated-PUT /profile proposal the BA had put to the client in the same exchange: the Desk Manager keeps POST /ai-preference + users.ai_enabled per RFQ V7, and ui_preferences.desk_manager_enabled is not a field in this spec (recorded in BR_4.6.4.2 so it is not re-opened). Two BA follow-ups outside this UC: (1) References/WBS/[BA Internal] Stacktrading.csv line 176 still lists POST /accessibility-preference in its endpoint column and must be corrected; (2) UC_4.6.1_v1.md cross-references (§ Screen Description row 3 and the panel note) state that "each toggle saves immediately through its own endpoint" — corrected in the same change. |
| 2026-09-03 | v7 | Document References row 10 (new) · row 3 · BR_4.6.4.2 — the consolidated-endpoint note gains a closing blockquote · the ai_enabled response-shape note · the Reduce Motion withdrawal table · the UC_4.6.2 boundary note | The document stated the contract correctly but two client questions were still formally open — [CTRL-EP-03] (which endpoint does the Desk Manager call) and [CTRL-EP-04] (which field holds the value) — so BE could still argue for the consolidated PUT /profile + ui_preferences.desk_manager_enabled route. The 2026-08-28 note also still allowed ai_enabled to sit either at top level or inside ui_preferences. And the dormant ui_preferences column was described only as having lost its consumer, with no stated reason for existing. | ✅ [CTRL-EP-03] and [CTRL-EP-04] closed. The client re-confirmed a dedicated endpoint — "The AI Preference Toggle has a dedicated backend endpoint because it controls core operational logic and costs, explicitly telling the Node.js middleware to stop pinging the Gemini API" — so POST /ai-preference + the flat users.ai_enabled column are final and the consolidated route cannot be re-opened. The Gemini worker queries a boolean column, not JSONB. Top-level ai_enabled becomes the only permitted response shape, since the alternative nested it in a column the client says has "no active parameters to parse". ui_preferences is documented as dormant by design — "intentionally included in the schema strictly as a future-proofing measure", to be maintained as an optional JSON object for future frontend-only cross-device layout preferences, with no toggle from this UC permitted inside it. | No behavioural or contract change — every rule in v6 stands. This entry converts two open questions into settled facts and records the client's rationale for a column that otherwise read as an unexplained leftover. Reduce Motion is unchanged (localStorage, per device). The BE dependency — adding ai_enabled to the GET /profile response — remains the only outstanding backend item in this UC. Source: client answer 2026-09-03 → [CTRL-EP-05] in QnA_STAGE2_SETTINGS_CAREER_VIRAL_EN.md §9. |
| 2026-08-28 | v6 | BR_4.6.4.2 — ai_enabled read path confirmed mandatory; response shape and endpoint name pinned | The read path said GET /profile "is now relevant to this UC for one value only — and it still does not carry it", noting ai_enabled must be added. It did not state where in the response the field sits, nor why two endpoints are needed when only one toggle exists. | A table separates the two jobs — GET /profile reads the stored position once on load, POST /ai-preference writes on each flip — answering why both exist. The field is confirmed mandatory: until BE ships it the toggle cannot be built correctly. Shape pinned to top-level ai_enabled, not nested in ui_preferences, so the read matches the dedicated users.ai_enabled column the write already targets. The alternative name GET /user-profile is not adopted. | BA/client answer 2026-08-28 — "bắt buộc để Frontend khởi tạo đúng trạng thái ON/OFF… ngay lúc load màn hình". The answer permitted either response shape and either endpoint name; this document picks one of each and records why, so BE is not left with a choice that would split one value across two shapes. |
| 2026-08-27 | v5 | BR_4.6.4.5 — 🔴 Reduce Motion default reversed On → Off; C-07 superseded | Default was On on every device (BA instruction 2026-08-12, C-07). A standing blockquote flagged the consequence — every trader got animation suppressed until they opted out, on each device after the per-device change of 2026-08-24 — and asked for client confirmation, noting it "inverts the motion-first presentation the RFQ's cinematic and motion-reference material is built around". | Default is Off. A new trader on a new device gets the full motion presentation — Level-Up Cinematic (BR_4.6.4.8) and the CRITICAL blinking on Market Status (BR_4.6.4.7) — with no setting to touch. The flagged consequence is resolved rather than accepted, and a note records that default and per-device scope now point the same way: the trader who needs motion reduction (the "weak travel laptop") opts in on that one machine. ⚠️ QC re-baseline: the default test path is now the full-motion path; any test assuming a fresh browser starts suppressed is wrong against v5. §9.1 row 7 updated. | BA/client answer 2026-08-27, UC_4.6.4 §2 — "Mặc định ban đầu (Default): OFF… để giữ đúng trải nghiệm motion-first ban đầu". Closes the question carried since 2026-08-12 and re-raised 2026-08-24. |
| 2026-08-27 | v5 | BR_4.6.4.2 — stacktrading.reduce_motion key confirmed · POST /ai-preference contract fixed verbatim | The local key was labelled BA-proposed and explicitly "not a client-confirmed contract" — only the per-device persistence was binding. The Desk Manager contract was stated in a table row but no payload was quoted. | The key stacktrading.reduce_motion (Boolean, localStorage) is part of the specification; the BA-proposed caveat is withdrawn, and absence of the key now explicitly resolves to the Off default. POST /ai-preference carries a verbatim payload block ({user_id, ai_enabled} → 200 OK) and an explicit statement that it is not folded into PATCH /profile. | BA/client answer 2026-08-27, UC_4.6.4 §1–§2 — "không gộp chung vào profile nữa". This is what keeps the UC_4.6.2 profile endpoints down to one field each (Ref: BR_4.6.2.15). |
| 2026-08-21 | v3 | BR_4.6.4.6 — pending-confirmation note replaced by a confirmed note · BR_4.6.4.8 — rewritten · BR_4.6.4.7 — downstream table row for the cinematic · BR_4.6.4.5 — default-On consequence note · Document References rows 7–8 · header Document References | C-10: "the worker skips the Gemini call entirely" was BA-proposed and flagged pending, with the cost implication of the alternative (call anyway, hide the output) left open. C-13: reduce_motion = true skipped the cinematic entirely — WebM/HEVC and Lottie not loaded at all, staggered text and animated QR not played — and rendered the Certificate modal directly at the sequence's end state. The Reduce Motion = On default therefore cost every new trader their first Level-Up celebration. | C-10 closed — confirmed verbatim: "skip call hoàn toàn — không gọi Gemini, không trừ token bucket, không push GEKKO_CHATTER". The three consequences are now binding rather than inferred, and the flag check is specified to happen in the worker before the LLM call. C-13 closed — rule reversed: the motion still plays; the trader lands directly on the Certificate modal and the celebration runs from there instead of as a full-screen pre-roll. The cinematic becomes an explicit exception to the BR_4.6.4.7 rule set. The public /milestone/[unique-id] route is unaffected in both states. Four new open items recorded (which layers play, in-modal vs. full-screen, audio, and whether a reduced variant is needed at all). | BA / client answers, 2026-08-21 (QnA Init Docs — UC_4.6.1–4.6.4: Settings, Q15 / C-10 + Q17 / C-13). New source read for this revision: Level Up Cinematic Scene Handling.pdf (client supply added 2026-08-18), which establishes the Certificate as the animation's terminal state and enumerates the five composite layers — ⚠️ not yet registered in _SOURCE_MANIFEST.yaml. The open items are handed to the Level-Up & Certificate UC, not resolved here. |
| 2026-08-12 | v1 | Initial version | — | UC_4.6.4 created: §1–§9, 10 Business Rules (BR_4.6.4.1 → BR_4.6.4.10), 1 Screen Description table (Controls panel, 7 rows). | Init Flow (Agent 1 Auditor → Agent 2 Challenger → Agent 3 Architect). BA answers recorded in QnA_init_docs.md C-01 → C-18. UC_ID confirmed as UC_4.6.4 per WBS line 176 (C-01) and the folder renamed UC_4.6.1-4.6.5 → UC_4.6.1-4.6.4 (C-02); the "Concierge" entry point is excluded for lack of any source definition (C-03). No new list-toast-popup.md ID was needed — the UC reuses TE-SYS-01 and TE-AUTH-01, and deliberately does not fire TS-DASH-01 (that ID stays scoped to [Save Changes]). Three items remain pending client confirmation and are marked inline: POST /accessibility-preference schema + ai_enabled in the GET /profile response (C-04/C-06), the ai_enabled = false backend mechanism (C-10), and the cinematic guard (C-13). |
| 2026-08-12 | v2 | BR_4.6.4.5 (defaults + OS seeding), BR_4.6.4.6 (commentary history), §9.1 rows 4 & 7 | Desk Manager = On / Reduce Motion = Off by default, with first-load seeding from the OS prefers-reduced-motion setting; cards generated before switch-off "reappear when it is turned back on; they are not deleted by the toggle" | Both toggles default On (Reduce Motion On = motion suppressed), the trader's own choice loads on every later login, and the OS prefers-reduced-motion seeding is removed entirely; switching the Desk Manager off now discards the commentary history, so switching it back on restarts the widget from its empty state | BA instruction, 2026-08-12 (Ref: QnA_init_docs.md C-07 override, C-08 rejected, C-11 partially rejected). Behavioural change, not an ID remap. The "no backfill" and "no Token Bucket consumption while off" rules of BR_4.6.4.6 are unchanged. Paired update in UC_4.8.3_v1.md v3 (BR_4.8.3.3 + §6), which owns the widget. A note under BR_4.6.4.5 records that the Reduce Motion = On default suppresses the Level-Up Cinematic for every new trader and should be confirmed with the client. |