Skip to main content

Signal — Platform Foundation (app shell: auth, tiers, data flow, shared UI, global search, Iris)

Written by Schae Lilley

Signal is Power Digital's internal business-intelligence web app. The Platform Foundation is the cross-cutting shell every user interacts with regardless of which page they open: Google sign-in and the five-tier access model (Member → Team Lead → Admin → Gandalf → Sauron, plus a separate Growth grant system), the left sidebar with the shared period (month) selector, the top-header Global Search (Cmd/Ctrl+K), the Iris AI chat drawer (Cmd/Ctrl+I; enabled by the is_admin flag specifically — Admins and Saurons, not Gandalf-only users), and the central data pipeline that loads employees, client accounts, and revenue for the selected months. It answers "who can see what in Signal, and where does the data on screen actually come from." Top-level data origins: revenue and gross profit come from Vena (finance's system of record, read-only); people, clients, departments, and team assignments come from Nova (Power Digital's client/org platform); everything users change inside Signal (roles, Growth grants, favorites, usage logs) lives in Signal's own records; AI answers come from Anthropic Claude.

Field

Value

Route

Cross-cutting (shell around all ~30 routes; / redirects to /summary)

Access

All tiers — this page documents the access model itself

Nav section

N/A (sidebar, top header, login/loading screens)

Primary data sources

Vena finance revenue (read-only), Nova org/client platform (people, clients, teams; read + limited write-back), Signal's own records (roles, grants, overrides, favorites, logs — entered by users in Signal), Google Workspace identity, Anthropic Claude (AI search + Iris), browser-local preferences

Writes

Auto-provisions the user's role row on first sign-in; usage events + AI generation logs (Signal's own records); "My Accounts" stars; theme/pins/sidebar state (browser-local only). Never writes to Vena or the finance data.

Key code refs

src/App.tsx (routes, guards, sidebar, month selector), src/context/AuthContext.tsx (tiers), src/context/DataContext.tsx (central data), src/components/GlobalSearch.tsx, src/components/Iris/*, src/services/api.ts, src/services/growthUnlock.ts, server/index.ts (all API routes + auth middleware), server/vena-kpi-engine.ts, server/nova-api.ts

Last verified

staging @ 98df0c4, verified 2026-07-07

Purpose

Signal's Platform Foundation exists so that one login, one navigation, one time-period selection, and one data pipeline serve every view in Signal. Before Signal, answers to "how much revenue does this client bring, who is staffed on it, and is it healthy?" lived in disconnected systems: Vena (finance), Nova (client/org platform), and spreadsheets. Signal's backend (Express, server/) joins those sources server-side — the browser never queries the data warehouse directly — and serves roughly 30 views to different leadership audiences. The foundation layer is also where access control happens: a five-tier role model (Member, Team Lead, Admin, Gandalf, Sauron) plus a separate division-scoped "Growth" grant system decide which sidebar sections, routes, and API endpoints each person can use. Finally, the foundation provides the two universal lookup tools: Global Search (instant fuzzy search over employees/accounts plus an optional AI answer) and Iris (an AI analyst chat drawer that sees the data currently on the user's screen).

Access & visibility

Signal sign-in and the tier model (all mechanics below are code as of Jul 2026, in src/context/AuthContext.tsx and server/index.ts):

Sign-in: Google OAuth via Signal's own auth service. Only three email domains may sign in (hardcoded allowlist in AuthContext.tsx): powerdigitalmarketinginc.com, external.powerdigitalmarketinginc.com, cardinaldigitalmarketing.com. Any other domain is signed out with "Access restricted to authorized Power Digital and Cardinal email domains only." Deep links survive login (the path is stored and restored after the OAuth redirect). Local dev can bypass auth with VITE_DEV_BYPASS_AUTH=true.

First sign-in auto-provisioning: the frontend calls POST /api/auth/ensure-provisioned, which creates the user's row in Signal's app_users role table (all flags false), except that people who are an Executive Sponsor (Account Director) or Group Director on any active client in Nova are auto-upgraded to Team Lead (is_team_lead=true). The AD/GD email list is read from Nova's client records and cached server-side for 1 hour. Provisioning never downgrades an existing Team Lead.

The five tiers (resolved from Signal's app_users table; flags cached in the browser's sessionStorage so a page refresh doesn't flash access away, then reconciled against the database in the background):

Tier

How granted

What it unlocks (route guard in src/App.tsx)

Member (base)

Any signed-in user with no elevated flag and no Growth capability

Summary + Employees only. MemberAllowedRoute redirects members away from everything else; Global Search and Iris are hidden.

Team Lead

app_users.is_team_lead (auto-set for Nova ADs/GDs at sign-in; implied by any higher tier)

+ Manage Leads, Client Pulse submission (Submit / My Submissions), Revenue Overlap (TeamLeadRoute)

Admin

app_users.is_admin

+ Corrections, Role & Company Capacity, KPI Targets, Client Profitability, Employee Pulse, Pulse Summary, Insights (hidden nav; direct URL) (AdminRoute; Gandalf also passes every Admin gate). Separately, the Iris AI drawer is enabled by the is_admin flag specifically (IrisContext.tsx, not AdminRoute) — a Gandalf-only user without is_admin does not get the drawer; Sauron does, since Sauron forces all flags

Gandalf

app_users.is_gandalf, plus a hardcoded allowlist in code (currently one person: makenna@)

+ Divisional P&Ls, AI Solutions, General Access (Users admin), Hiring Pipeline, JD Sheet Sync, Assignment History, Garden link (GandalfRoute)

Sauron

Hardcoded super-admin email list in code — currently john@, jaime.diaz@, laura.bejarano@, lily.loyer@ (mirrored server-side)

All flags forced true regardless of database + the Analytics usage dashboard (SauronRoute). Also always allowed to manage Growth access.

Note: since a mid-2026 nav restructure, the prediction/health views (Portfolio Review, POC Management, Churn Risk, Division Forecast, Service Expansion), Staffing, and ETCR are open to every non-Member — they no longer require Team Lead. Client Profitability and Pulse Summary remain Admin+.

Growth access (orthogonal to the tiers): visibility into the Growth section (People Dash with salaries, Performance Radar) is not a tier. It is resolved by the backend from two of Signal's own tables — growth_access_grants (which divisions/departments a person may see; a NULL-division grant = all divisions) and growth_access_capabilities (three per-user booleans: can_view_people, can_view_radar, can_manage_access). The frontend fetches GET /api/growth/my-access at sign-in and caches the result in sessionStorage. On top of the grants there is a second factor: a shared unlock password. Each Growth tab prompts for it (GrowthTabGate in src/App.tsx); the backend enforces the same key (x-growth-key header, checked against the GROWTH_UNLOCK_KEY environment variable) on every /api/growth data request and on the Growth Access management endpoints, so a locked tab genuinely cannot load data. The key is held in browser memory only — a page reload forces re-entry, and it re-locks after the app has been backgrounded ≥ 5 minutes (code constant BACKGROUND_RELOCK_MS as of Jul 2026). Growth access is managed on the "Growth Access" screen (/growth/access), open to Sauron super-admins or anyone holding the delegated can_manage_access capability — it is not Gandalf-gated. Being Sauron does not by itself grant Growth data access; a super-admin must hold a grant like anyone else (only management is automatic).

Sidebar visibility mirrors the route guards: nav sections/links carry flags (teamLeadOnly, adminOnly, gandalfOnly, sauronOnly, peopleOnly, radarOnly, manageGrowthOnly) and are filtered per user. Members see exactly two destinations (Summary, Employees). An "Access" section (General Access for Gandalf; Growth Access for Growth managers) and the Admin section are pinned to the bottom of the sidebar. A separate hardcoded allowlist (PAYROLL_AUTHORIZED_EMAILS in server/index.ts, exactly 15 named emails as of Jul 2026) gates the payroll/personnel-expense API endpoints used by Divisional P&L — that list is independent of the tiers.

Server-side enforcement: every sensitive API route re-checks access with middleware (requireAuth validates the login token; requireAdmin, requireGandalf, requireGrowthAccess, requireGrowthManage, requirePayrollAccess re-read the role/grant tables with short caches of 2–5 minutes). Hiding a nav item is never the only protection.

Data sources & lineage

The browser talks only to Signal's backend API (/api/*, fetch wrapper in src/services/api.ts) and to Signal's own record store for a few user-owned reads/writes. Origin systems below are the true source of each number, not the transport.

On-screen element

Origin system

Code path

Refresh

Caveat

Sign-in (Google button, domain check)

Google Workspace identity

AuthContext.tsx → Signal auth service (Google OAuth)

Session token refreshes ~hourly

Domain allowlist is hardcoded in code

Tier flags (Member/Team Lead/Admin/Gandalf)

Signal's own records (entered by users in Signal — app_users, edited on the General Access screen)

Direct role-table read in AuthContext.tsx; POST /api/auth/ensure-provisioned creates/upgrades the row

Read at sign-in; cached in sessionStorage; background reconcile with 10s timeout + late self-heal

Sauron/Gandalf hardcoded lists in code override the database

Growth section visibility + capabilities

Signal's own records (growth_access_grants, growth_access_capabilities)

GET /api/growth/my-access; enforced per-request by requireGrowthAccess

Server cache 2 min per user; sessionStorage cache in browser

Second-factor unlock password required on top (env GROWTH_UNLOCK_KEY)

Period selector (available months)

Vena (finance revenue calendar)

GET /api/months → computeMonths() in server/vena-kpi-engine.ts

30-min server cache

Months after the current calendar month are excluded server-side (Vena contains scheduled future months through 2027)

Core revenue + team dataset (drives nearly every view)

Vena (revenue, outsource cost, MGP) + Nova (client-team assignment history, departments)

GET /api/assignments, GET /api/assignments/batch → joined in server/vena-kpi-engine.ts

Server-side Vena cache; invalidate via POST /api/vena/cache/invalidate

Vena writes the revenue tables; Signal reads only

Team-assignment overrides (Manage Leads truth)

Signal's own records (team_assignments, managed_clients)

GET /api/team-assignments, GET /api/managed-clients (service-role reads, paginated); Render-disk backup fallback GET /api/assignments/backup/:month

Per data load

A "managed" client never falls back to the Nova-detected team, even if its saved team is empty

AD / Sr. AD chips

Nova (live, via Signal's Nova GraphQL connection) with Signal-record fallback

GET /api/nova/assignments (5-min server cache) merged in DataContext.tsx; writes via POST /api/nova/assignments → Nova updateClient mutation

5-min server cache

For single-division non-consulting clients live Nova wins over any stale Signal-cached row; multi-division and Consulting clients keep Signal's per-division records (fix landed Jul 7 2026)

Client metadata (vertical, tier, division, churn status)

Nova (client records) + churn-model features

GET /api/clients

1-hour server cache

Parent name falls back to the client's own name (no parent relationship in Nova)

Employee roster (names, titles, dept/division)

Nova (user/org records)

GET /api/employees, GET /api/employees/nova-org

1-hour server cache

Junk/test names filtered client-side

Headcount cards

Nova (active users, no termination date)

GET /api/headcount

Refetched when division/department filters change

Time-series charts (trend lines everywhere)

Vena revenue + Nova teams (same join as the core dataset)

GET /api/time-series; all-months history loads in the background after first paint

Once at startup + background

Long-lived tabs may show pre-deploy numbers until reloaded (a banner prompts)

KPI target badges

Google Sheets (KPI target sheet, service-account read) persisted into Signal's own records (kpi_targets)

GET /api/employee-kpi-targets; POST /api/kpi-targets/sync

Startup sync on the server; UI retries once if empty

Sheet ownership/cadence not defined in the app repo

Client flags (VABO), employee flags (on leave), My Accounts stars, revenue-split overrides

Signal's own records (client_flags, employee_flags, user_account_favorites, revenue_split_overrides)

Direct reads/writes from DataContext.tsx

On load and on change (optimistic UI with revert)

Global Search — instant results

Already-loaded Signal data (employees/accounts in DataContext)

Client-side fuzzy match in GlobalSearch.tsx

Instant

Excludes VABO rows; matches only what the current period loaded

Global Search — "Ask AI" (Enter)

Anthropic Claude (Haiku model, claude-haiku-4-5-20251001 as of Jul 2026)

POST /api/claude proxy with a compact employee/account index built in the browser

On explicit Enter only

Results labeled "AI-powered"; capped at 500 employees / 300 accounts in the prompt

Iris AI drawer

Anthropic Claude (Opus claude-opus-4-7; silent fallback to Sonnet claude-sonnet-4-6 on overload)

IrisChat.tsx → POST /api/claude; data context is a client-side snapshot of the current period's employees, accounts, and computed KPIs

Per message

Iris does NOT query databases — it only sees the on-screen snapshot; every generation is logged

Usage analytics (invisible; feeds the Sauron Analytics view)

Signal's own records (signal_usage_events)

usePageViewTracking + useFreezeTelemetry in src/hooks/useUsageTracking.ts → direct insert

Fire-and-forget per route change / per UI freeze

Churn, SE, and Portfolio views log their own richer events; the route hook skips them to avoid double counting

Iris / AI-search generation logs

Signal's own records (llm_generation_logs)

Logged inside POST /api/claude (feature, user email, full prompt, response, tokens, latency)

Per generation

Written before the response returns; failures never block the answer

"New version available" banner

Signal's own deploy metadata (version.json served with the frontend)

useDeployedVersionCheck.ts compares the running bundle's build id

On tab-visibility change, at most every 5 min

Prompts reload only; never auto-reloads

Theme, pinned views, sidebar collapsed

Browser-local (localStorage keys signal-theme, signal-pinned-views, signal-sidebar-collapsed)

App.tsx

Instant

Per device, not synced

Sources that surface in other Signal modules (for orientation only): VABO monthly revenue (Vena), churn/service-expansion model scores and Salesforce history (ML pipeline + Salesforce), pulse answers (Nova), Deel and ADP payroll (Divisional P&L / Growth), NetSuite account costs (profitability), Google Sheets hiring pipeline, Asana task creation, Gong-derived meeting data where noted in those modules.

Metrics & definitions

The foundation layer itself displays few metrics, but it defines the shared vocabulary every view (and Iris) uses:

  • Period / selected months — the array of months chosen in the sidebar period selector. Presets: Current month, Last 3 months, Last 6 months, All time, or a custom start–end range. Every view filters to this selection; multi-month selections aggregate (sum) revenue across the range. Presets anchor to the current calendar month; months with zero revenue, and future months, are not offered (server-side rule in computeMonths(), server/vena-kpi-engine.ts).

  • Revenue (monthly) — the client's billed monthly revenue for the selected month(s). Origin: Vena, finance's system of record. Signal reads it; it is never edited in Signal.

  • Outsource cost — third-party cost of goods sold for the account. Origin: Vena.

  • MGP (Monthly Gross Profit) — revenue minus outsource cost, per account per month. Origin: Vena (computed upstream, read as-is).

  • RPE (Revenue Per Employee) — allocated revenue ÷ headcount; the KPI used for strategist roles. Computed server-side in server/vena-kpi-engine.ts from Vena revenue split across the Nova/Signal team assignments.

  • MRR (for AD roles) — total book value of active accounts an Account Director oversees (not split by allocation). Basis stated in the Iris system prompt and computed in the KPI engine.

  • KPI status thresholds — Above target = >105%, At target = 95–105%, Below = <95% of the role's target (code constants surfaced in the Iris prompt as of Jul 2026; targets come from the KPI Targets sheet/records).

  • Attributed revenue (Global Search employee panel) — the sum over the employee's active accounts of their allocation-percentage share of each account's monthly revenue (allocation logic in src/utils/allocation.ts, honoring revenue_split_overrides).

  • Total client revenue / outsource / MGP (Global Search account panel) — sums across all active sibling department-accounts of the same client name, excluding VABO rows.

How it's used

  1. Set the analysis window before anything else. Deciding whether a dip is noise or a trend: open the sidebar period selector, pick "Last 3 months" or a custom range. Every number in every Signal view now reflects that window — single month = point-in-time, multi-month = aggregated.

  2. Look anything up in five seconds. Mid-meeting, someone asks "who's on the Acme account and what do we make on it?" — press Cmd/Ctrl+K, type "acme", click the account: revenue, outsource cost, MGP, the team with allocation percentages, and every department serving that client, with quick links to Manage Leads, Client Profitability, Churn Risk, and Service Expansion.

  3. Ask Iris instead of hunting through views (Admins and Saurons — the drawer checks the is_admin flag). Press Cmd/Ctrl+I and ask "Where are the biggest RPE gaps to target right now?" — Iris answers from a snapshot of the data on screen for the selected period, with tables and charts, and says so when a question can't be answered from that snapshot.

  4. Tailor the app to your job. Pin the views you live in (pin icon on hover in the sidebar) to a personal shortcut list; star clients as "My Accounts" so client lists can filter to your book; toggle light/dark theme. Pins and theme are per device; My Accounts follows your login.

  5. Grant someone access at the right level. A new director signs in with their company Google account and is auto-provisioned as a Member (Nova Account Directors / Group Directors auto-upgrade to Team Lead). A Gandalf-tier user raises their tier on the General Access screen; a Sauron super-admin or a delegated Growth manager issues division-scoped Growth grants on the Growth Access screen. No IT ticket needed for tier changes — it's data in Signal's own role tables. (Sign-in problems themselves — tokens, OAuth errors — go to IT.)

  6. Trust the lineage when numbers are questioned. Rule of thumb: revenue numbers come from Vena (finance, read-only); people, clients, and detected teams come from Nova; anything a user changed in Signal (team overrides, split overrides, targets, flags) lives in Signal's own records. Wrong team → fix in Manage Leads. Wrong revenue → that's upstream in Vena; flag it via Corrections.

  7. Reload when the banner says so. Long-lived tabs keep running old code across deploys; when a newer build is live, a banner offers Reload / Later. Numbers may be stale until reloaded.

What users can change here

The foundation layer writes little by itself — most writes belong to specific modules — but these are cross-cutting:

  • First sign-in auto-provisioning — creates your row in Signal's app_users role table (and sets Team Lead if you're an Executive Sponsor/Group Director in Nova). Idempotent, invisible, never downgrades.

  • My Accounts stars — per-user favorites in Signal's own records (user_account_favorites).

  • Theme, pinned views, sidebar collapsed state — browser localStorage only; not synced across devices.

  • Growth tab unlock — entering the Growth password stores the key in browser memory only (never persisted); side effect: subsequent /api/growth requests from that tab carry the key. Cleared on reload, sign-out, or ≥5 minutes in the background.

  • Every page you visit — logs a page_view event into Signal's own records (signal_usage_events); UI freezes ≥ a threshold log a longtask event with route/duration diagnostics. These feed the Sauron-only Analytics view.

  • Every Iris message and AI-search query — logged to Signal's own records (llm_generation_logs) including your email, the full prompt (with the data snapshot), the response, token counts, and latency.

  • Nothing in Signal ever writes to Vena or the finance revenue data. The only external write-back from the foundation-adjacent layer is Account Director / assignment changes syncing into Nova (via Signal's Nova connection, from the Manage Leads module).

Caveats & data quality

  • Tier code names are internal. "Gandalf" and "Sauron" are Lord-of-the-Rings-themed internal names; in user-facing language say "executive tier" and "super-admin". The version tag ("v3.5.3 Legolas" as of Jul 2026) changes with releases.

  • Hardcoded lists exist and drift. The Sauron super-admin emails (4 people), a Gandalf allowlist (1 person), and the payroll-access email list (exactly 15 emails as of Jul 2026) live in code (AuthContext.tsx, server/index.ts), not the database. Current membership is a code constant as of Jul 2026, not policy.

  • Growth has three locks. Division grant + per-tab capability + shared unlock password (env-configured; skipped entirely when the env var is unset, e.g. local dev). A comment in server/index.ts claims the password "no longer guards People", but the enforcing middleware (requireGrowthAccess) does check it on all /api/growth data endpoints — the code, not the comment, is authoritative.

  • Role checks are resilient by design, so revocations are not instant. Browser sessionStorage caches tier flags; the server caches admin/growth checks for 2–5 minutes; ambiguous/failed role reads keep the cached value rather than revoking. Expect up to a few minutes (or a fresh sign-in) for a role change to fully apply.

  • AI Agents section is OFF (SHOW_AI_AGENTS = false in src/App.tsx) — Insights/Alerts/Advisor Agent views exist in code but are hidden.

  • Insights is hidden from the nav but its route (/insights, Admin+) still works via direct URL. ETCR is labeled "(Beta)" in the nav and is open to all non-Members.

  • Iris is gated by the is_admin flag specifically (IrisContext.tsx) — Admins and Saurons get it; a Gandalf-only user without is_admin does not. Model names are code constants (Opus 4.7 primary, Sonnet 4.6 overload fallback as of Jul 2026); both audience and models may change. Iris never criticizes named individuals or departments by prompt design — below-target data is framed factually as gaps/opportunities.

  • Iris answers only from the on-screen snapshot. It does not run queries; if the selected period excludes a month, Iris cannot see it. For deep client-specific data questions, the org-wide guidance is to use iris.novapower.io directly.

  • Prompt logging is comprehensive. Iris and AI-search generations are stored with user email, full prompt, and response in Signal's own records. Retention policy: not defined in the app repo.

  • Month presets anchor to the current calendar month and zero-revenue/future months are excluded server-side — screenshots and month lists shift at month boundaries.

  • The Nova-direct assignment layer is mid-migration (phased cutover; point-in-time reads valid only for dates on/after 2026-07-01, earlier dates fall back to Signal's team_assignments). Legacy endpoints remain live until the migration completes.

  • Upstream refresh cadence is not visible in this repo — how often Vena finance data and Nova replication land in the warehouse Signal reads is owned by the data team. Not defined in the app repo.

  • Cardinal Digital Marketing and external. domains may sign in, but their intended user population/tier is not documented in code.

  • Stale-tab risk is mitigated, not eliminated: the "new version available" banner checks at most every 5 minutes and never forces a reload.

Related views

  • Every Signal view reads the foundation's shared state (DataContext): selected months, employees, accounts, division/department filters that persist across views.

  • Summary (/summary) — the landing view and the redirect target of every access denial.

  • Manage Leads (/leads) — the write side of the team data the foundation loads (Signal team_assignments overriding the Nova-detected team; managed_clients suppresses the fallback; AD/Sr. AD changes sync into Nova).

  • General Access (/users, Gandalf) and Growth Access (/growth/access, Sauron or delegated Growth managers) — where the tiers and Growth grants documented here are administered.

  • Analytics (/analytics, Sauron only) — consumes the usage events and AI generation logs the foundation emits.

  • KPI Targets (/kpi-targets, Admin+) — produces the targets that color statuses across Employees/Summary.

  • Growth · People / Performance Radar (/growth/people, /growth/performance-radar) — the views behind the Growth grant + password model.

  • External links: Nova (app.novapower.io) in the sidebar for everyone; Garden (garden.novapower.io) for Gandalf+.

FAQ

Q: Why can I only see Summary and Employees in Signal? A: You're on the base "Member" tier — any signed-in employee with no elevated role. Members see exactly two views (Summary and Employees) and have no Global Search or Iris. If your job needs more, ask a Gandalf-tier admin to raise your tier on Signal's General Access screen (it's a data change in Signal, not an IT ticket). Nova Account Directors and Group Directors are upgraded to Team Lead automatically at sign-in.

Q: Why doesn't the revenue in Signal match Vena exactly? A: Signal reads its revenue, outsource cost, and MGP directly from Vena (finance's system of record) — Signal never edits those numbers. Differences usually come from timing (Signal's server caches Vena data and the upstream pipeline refreshes on its own cadence), from the selected period (multi-month selections sum revenue across the range), or from a long-open browser tab running pre-deploy code (reload when the "new version" banner appears). If a number is genuinely wrong, the fix is upstream in Vena — flag it in Signal's Corrections view.

Q: Who can use Iris, and what data does it actually see? A: Iris (the chat drawer, Cmd/Ctrl+I or the top-right button) is enabled by the is_admin flag specifically (IrisContext.tsx): Admins and Saurons have it (Sauron forces all flags true), but a Gandalf-only user without is_admin does not get the drawer, and Members never see the button. It sees only a snapshot of what's loaded in your session for the selected period — employees, accounts, and the computed KPIs — plus which page you're on. It does not run database queries, so it can't answer about months outside your selected period. Every Iris conversation is logged (your email, full prompt, and response) to Signal's own records. For deep client-data questions beyond the on-screen snapshot, use iris.novapower.io.

Q: Why is Signal asking me for a password on the Growth People tab when I already have access? A: That's by design — Growth data (salaries, performance) has a second factor on top of your division grant. The shared unlock password is enforced by the backend on every Growth data request, is kept only in your browser's memory, and re-locks after a page reload or ≥5 minutes with Signal in the background. If you don't know the password or your grant is missing, ask a Growth access manager (the Growth Access screen is run by super-admins and delegated managers).

Q: I was just given Admin/Team Lead but Signal still shows my old access. Why? A: Role flags are cached for resilience: your browser keeps them in sessionStorage and the server caches role checks for a few minutes, and ambiguous reads deliberately keep the cached value instead of revoking access. Sign out and back in (or wait a few minutes and refresh) and the new role will apply. The same logic means a revoked role can also linger briefly.

Q: What do the period presets actually select, and why is next month missing? A: The sidebar period selector offers Current month, Last 3 months, Last 6 months, All time, or a custom month range. Presets anchor to the current calendar month. Future months are excluded server-side — the finance source contains scheduled future contract months (through 2027) that aren't real actuals yet — and months with zero revenue are skipped. Every view in Signal filters to this selection; multi-month picks aggregate revenue across the range.

Q: Where does the team shown on an account come from — Nova or Signal? A: Both, with clear precedence. Signal's own team assignments (saved in Manage Leads) win whenever they exist; a client marked "managed" in Signal never falls back, even with an empty team. Otherwise Signal shows the team detected from Nova's assignment history. Account Director / Senior AD chips are the exception: for single-division, non-Consulting clients, live Nova is the source of truth and Signal syncs AD changes back into Nova; multi-division and Consulting clients keep Signal's per-division records. If a team looks wrong, fix it in Manage Leads; don't assume the data pipeline is broken.

Q: Is my Signal usage tracked? A: Yes. Every page visit logs a page_view event (your email, the route) into Signal's own records, severe UI freezes log a diagnostic event, and every AI interaction (Iris or Global Search "Ask AI") logs the full prompt and response with your email. These feed the super-admin-only Analytics dashboard. Retention policy is not defined in the app repo.

Q: What are "Gandalf" and "Sauron"? A: Internal code names for the top two access tiers (Signal has a Lord of the Rings naming theme — the loading screen quotes LOTR and releases carry names like "Legolas"). Gandalf ≈ executive tier (Divisional P&Ls, user administration, hiring tools); Sauron ≈ super-admin, a short hardcoded list of the app's owners whose access can't be revoked by a database change. In documentation for wider audiences, say "executive tier" and "super-admin".

Q: Global Search found nothing — what does "Press Enter to ask AI" do? A: Instant results are a local fuzzy match over the employees, accounts, and page names already loaded for your selected period (type ≥2 characters; VABO rows excluded). Pressing Enter with no matches sends your query plus a compact index of employees/accounts to Anthropic Claude, which returns interpreted matches or a short analytical answer, labeled "AI-powered". The AI call only happens on explicit Enter, and it's logged like every AI generation.

Q: Why did Signal show a banner saying a new version is available? A: Your tab has been open across a deploy, so the code running in it is older than what's live — numbers could be computed with outdated logic. Signal checks the deployed build id when you return to the tab (at most every 5 minutes) and prompts — never forces — a reload. Click Reload unless you're mid-edit; "Later" hides the banner only for that specific build.

Did this answer your question?