Machine-verified mapping of every data source feeding each view of Signal (Power Digital's internal BI web app), and everything each view can write. Generated from staging @ commit 98df0c4, verified 2026-07-07.
Every source in this document is named at its core-origin layer — the business system where the data is actually created and maintained. The Snowflake warehouse, warehouse replicas (e.g. ENTERPRISE.RDS_PG_NOVA), Supabase-as-a-database, server caches, and mirrored views are transport, never sources: when Signal reads the Nova roster through a warehouse replica, the source is the Nova platform; when it reads the CSM renewal tracker through a mirrored Snowflake view, the source is the Google Sheet. The one deliberate exception is "Signal's own records" — data entered by users inside Signal itself (stored in Signal's Supabase tables), for which Signal is the true origin. Each system section and the Views table below are self-contained and can be read independently.
Source systems
Vena (vena)
Finance's planning system and the system of record for contract revenue: per-client, per-department, per-month revenue, MGP (gross profit), outsource costs (COGS), and VABO. Vena writes; Signal only reads.
Owner: Finance. Relationship to Signal: External system, read-only from Signal.
Views in Signal fed by Vena:
View | What it provides | Notes |
Summary | MRR (retainer-only recurring revenue), in-contract revenue (retainer+project), MGP (revenue minus outsource cost), outsource costs, VABO (historical months from contracts data; current month from LATEST_VABO_VALUE_2MO override), active-client activity gate, available-months list, and the entire metric-over-time chart | VABO excluded from all revenue/MGP rollups and surfaced on its own tile; project revenue excluded from MRR; months capped at current calendar month; server caches ~30-60 min |
Employees | Client gross profit per blueprint × department × month (FINAL_VENA_CONTRACTS; Vena writes, Signal reads) — drives RPE, Total Gross Profit, MRR, averages, leaderboards; current-month VABO substituted from FINAL_VENA_CLIENTS.LATEST_VABO_VALUE_2MO | Via getAssignmentsData() in server/vena-kpi-engine.ts; per-year master cache |
Staffing | Client contract revenue behind every card and all MRR/RPE dollar figures (FINAL_VENA_CONTRACTS, one row per blueprint × department × service × month; Vena writes, Signal reads), plus the available-months list that the global MonthSelector offers | Read via server/vena-kpi-engine.ts (enrichRevenue, computeMonths); negative-revenue rows dropped; zero-revenue dormant department rows filtered; VABO rows excluded from the board but included in RPE |
Manage Leads | Client list, department rows, monthly revenue / MGP / outsource cost per department-month (FINAL_VENA_CONTRACTS via GET /api/assignments → getAssignmentsData), month range for the MonthSelector, priority tier P1–P3 (FINAL_FEATURES), and the multi-division client universe | Vena writes, Signal reads only; 4-hour default in-memory server cache; VABO rows shown but excluded from seat/revenue rollups |
Submit Pulse Data | Account list behind the client cards (pass 1 from DataContext accounts) plus current-month MGP and latest VABO feeding the pulse-eligibility rule | Active book cached 15 min (server/activeBook.ts); clients join cached 1 hour; on a name collision the Nova client id wins over the Vena-derived id so writes agree with the server ownership map |
My Submissions (Client Pulse) | Client display names in the Client column — accounts built from Vena contract/revenue records keyed by Nova client id (parentName/name from DataContext) | Unmatched client ids display as raw numeric ids |
Pulse Summary | Eligibility revenue checks (current-month MGP from FINAL_VENA_CONTRACTS, LATEST_VABO_VALUE_2MO from FINAL_VENA_CLIENTS) and the revenue-based Department filter membership (accountsByMonth slices in DataContext) | Department filter is revenue-derived (positive revenue in latest loaded month, VABO excluded) and does not apply to the By Division table |
Portfolio Review | MGP, Dept MGP, MRR, Last Month VABO, VABO-2mo ('VABO only' badge), departments column and department revenue breakdown (FINAL_VENA_CLIENTS / FINAL_VENA_CONTRACTS) | UI tooltips explicitly name Vena as source; 15-min server cache; MGP can be $0 before Finance posts entries |
POC Management | Revenue-active client gating (active book: current-month contract MGP > 0 or recent VABO) that determines which clients appear at all | server/activeBook.ts, 15-min cache; same base as Portfolio Review |
Churn Risk | MGP, Current MRR, Last Month VABO columns and the current-month Department filter values | Finance actuals; MGP can be $0 for new contracts before Finance posts. |
Service Expansion | Current-month monthly GP, MRR, VABO figures in the client detail | Via the pipeline's Vena clients table; shared 15-min cached fetch with Churn/Portfolio |
Client Profitability | Contracted monthly revenue per client (Revenue/MRR column and the revenue side of Cost/Profit/Margin) via getFinanceData()/enrichRevenue() — the identical path as the Summary view; VABO rows excluded, churned clients filtered | Contract dataset cached up to 4 h server-side (default TTL); hours-months missing a Vena revenue row borrow the latest available month's revenue as an MRR proxy; $0-revenue clients hidden entirely |
Divisional P&Ls | Department × division × month contract revenue rows, In Contract Revenue, VABO row (per finance-assigned VASO slot, Retail Marketing slots excluded), outsource COGS, and Client Roster per-blueprint revenue + contract end dates + retainer/project type | GET /api/division-pl/revenue, /api/vabos/by-division, /api/vena/outsource, /api/division-pl/client-roster; Corporate dept (Vena 1024) excluded; verified to the penny vs finance (Jun 2026); server venaCache + 30-min browser TTL cache |
Revenue Overlap | Account revenue, MGP, division, department, contract type (FINAL_VENA_CONTRACTS, Vena-written / Signal read-only) and the selectable month list (capped at current calendar month; VABO rows excluded from this view) | Served via GET /api/assignments[/batch] and /api/months → getAssignmentsData()/computeMonths() in server/vena-kpi-engine.ts; server cache 4h default (venaCache 240-min TTL); Vena Blueprint Data Load refreshes ~weekly per code comment |
Capacity Planning (Role + Company) | Contract revenue per client/department/month — drives RPE, MRR, Revenue Gap, RPE Trend chart, and the available-months list (capped at current calendar month) | Signal read-only; served via GET /api/assignments and /api/months from getAssignmentsData()/computeMonths() in server/vena-kpi-engine.ts. VABO included in individual RPE, excluded from the trend chart. |
Growth (People Dash + Performance Radar) | RPE (revenue per employee) and # Accounts | Vena contract + monthly revenue allocated client-side across Signal's team-assignment records (same engine as the Employees view); joined to Growth rows by normalized name. |
AI Solutions | All business metrics on the Baseline vs Current cards, Trend Over Time chart, and per-employee breakdown: total/attributed revenue, MRR, VABO, RPE, seats, active client counts, retention rate (per data month); also MRR context inside health objects and department mapping used in target-to-client resolution | Computed in the browser via captureSolutionMetrics() from DataContext time-series/employee history (GET /api/assignments); baseline frozen as JSON on the baseline snapshot, Current recomputed from the latest data month independent of the global month picker |
Admin Tools (General Access, Assignment History, Hiring Pipeline, JD Sheet Sync, Analytics) | Employee RPE/MRR actuals behind the Hiring Need verdict (via the server KPI engine and DataContext employee metrics) | Thresholds ≥100/85–99/<85 are code constants in HiringPipelineView.tsx |
Platform Foundation (app shell) | Monthly revenue, outsource cost (COGS), MGP per client×department×month; the available-months calendar for the period selector (future/zero months excluded server-side) | Read-only via server/vena-kpi-engine.ts; server cache with /api/vena/cache/invalidate; 30-min months cache |
NetSuite (netsuite)
The accounting general ledger: GL revenue accounts (Summary's closed-month Total Revenue basis), benefits and payroll-tax cost pools, contractor bill payments, and an independent-contractor classification list.
Owner: Finance / Accounting. Relationship to Signal: External system, read-only from Signal.
Views in Signal fed by NetSuite:
View | What it provides | Notes |
Summary | Total Revenue tile and RPE numerator on the unfiltered view — per-month sums of GL revenue accounts 42100/43300/43400/43500/43550/43560 (REVENUE_ACCOUNT_CODES, server/netsuite.ts:178) via GET /api/netsuite-revenue | Closed, positive-figure months only; open month and incomplete closed months (non-positive = reversals only) fall back to Vena+VABO with subtitle/tooltip; live SuiteQL pull if the latest closed month is missing from the pipeline; 30-min server cache, client refetch on tab visibility max every 5 min; Summary view only, never under filters |
Divisional P&Ls | Benefits pool (GL 60500+66000) and payroll-tax pool (GL 60300+65100) pro-rated over ADP gross; FT-CONT/FT-CONT-INT contractor bill payments | Read from FINAL_NETSUITE_ACCOUNT_COSTS / FINAL_NETSUITE_CONTRACTOR_PAYMENTS models in server/payroll-actuals.ts |
Growth (People Dash + Performance Radar) | Independent Contractor classification pill + division/department overrides for ~22 people | Hardcoded mirror of JT's NetSuite IC List in server/payroll-overrides.ts — manual code refresh, not a live integration. |
Nova platform (nova)
Power Digital's internal org & client platform: employee roster and org structure, client master records, client-team assignment history, and weekly employee Pulse check-ins. Since July 2026 Signal also writes Account Director / Sr. Account Director assignments back to Nova via its GraphQL API.
Owner: Nova platform team. Relationship to Signal: External system, read + written by Signal.
Views in Signal fed by Nova platform:
View | What it provides | Notes |
Summary | Client records incl. churn_date (retention), division/tier/vertical; employee roster and full-time headcount (USERS + JOB_TITLES + DIVISIONS); seat assignments (CLIENT_ACCOUNT_ASSIGNMENT_HISTORY) behind Total Seats and Assigned Leads; Nova org profile for the Delivery Employees tile | 1-hour server caches on roster/headcount/client metadata; retention is point-in-time from churn_date vs month-end |
Employees | Roster (name, email, job title, division, department, is_fulltime) via GET /api/employees; complete org lookup incl. terminated users via /api/employees/nova-org; detected seats from client account assignment history; drawer profile (hire date, manager, employment type, contractor); Pulse hours (question 1006, Laravel-decrypted); client churn dates driving retention; since 2026-07-01 cutover, live GraphQL API is source of truth for AD/Sr-AD assignments and post-cutover team history | Roster/org cached 1h server-side (reaches Signal via warehouse replica); Nova GraphQL assignments cached 5 min; roster filtered to company email domains + non-terminated + test-name denylist |
Staffing | Employee roster (name, title, division, department) via GET /api/employees (1-hour server cache); client-team seat history that determines which clients each employee leads and their roles; Account Director / Sr. Account Director designations via GET /api/nova/assignments (live Nova GraphQL, 5-minute server cache) that inject ADs as extra leads on cards | New since Jul 6: the live Nova GraphQL read layer (PRs #94/#97, merged 2026-07-07) — Nova now wins over Signal's cached AD rows for non-consulting single-division clients. Roster enriched server-side with ADP employment type (not shown on this board) |
Manage Leads | Auto-detected department teams (CLIENT_ACCOUNT_ASSIGNMENT_HISTORY seats), employee roster and picker, client metadata (tier/vertical/division/churn), and — new — live AD/Sr. AD designations via Nova GraphQL (GET /api/nova/assignments, clients.executive_sponsor / senior_account_directors, 5-min server cache) plus Nova assignment history endpoints (/history, /at/:date, cutover 2026-07-01) | Since PR #97 live Nova wins over Signal's cached AD/SrAD rows for non-consulting single-division clients |
Employee Pulse (Pulse Coverage) | Weekly employee Pulse check-ins: submission existence (coverage), per-client + non-client hours (question 1006, decrypted server-side with NOVA_APP_KEY), sentiment 0-5 (question 1001), bandwidth 0-10 (question 1002); plus the active-employee roster (users, roles, departments, job titles) that forms the coverage denominator and the fine-grained Nova department labels | Queried live per request via GET /api/pulse-coverage and /api/pulse-coverage/sentiment; replication lag from the Nova platform is ~15 min-6 h (code-comment estimate). Denominator applies hardcoded exclusions (Revenue/Sales/Accounting/Finance/Corporate/Nova depts, exec titles, 23 test accounts, 3 named individuals) and dedupes duplicate Nova accounts by name. |
Employee Pulse (Pulse Coverage) | Employee names, divisions, titles, and coarse fallback departments via the shared GET /api/employees roster consumed through DataContext | Server-cached 1 hour; view prefers the finer Nova department from the pulse-coverage roster over this one. |
KPI Targets | Active employee roster and job titles used for title→role matching, the sync employee total, and (via /api/employees + DataContext) the coverage-bar population | Origin is Nova HR/ops; the code reads it via the warehouse replica (transport). Sync query counts all active titled employees; coverage bar counts only employees with retained client assignments. |
Submit Pulse Data | Client records (id, name, churn date), user directory (whoami email→Nova id defaulting the AD picker, submitter display names), group-director assignment history (GD dropdown + GD submit rights), and executive-sponsor fallback for clients with no Signal AD row | Ownership caches: email→Nova-id map 5 min (PULSE_OWNERSHIP_TTL_MS), exec-sponsor fallback and client/GD lists 1 hour; a Manage Leads reconcile invalidates the pulse AD cache immediately |
My Submissions (Client Pulse) | Caller identity (JWT email → Nova employee id via resolvePulseCallerEmpId, 5-min server cache), employee display names for the Submitted column (GET /api/employees, 1-hr cache), and the team-lead auto-grant basis (executive sponsor / group director on a Nova client) | Page errors with 'account not linked to a Nova user' if the login email is absent from Nova's user directory |
Pulse Summary | Client master records (id, name, churn date) from ENTERPRISE.RDS_PG_NOVA.CLIENTS that define the candidate universe for the pulse-eligible active book | Filtered by pulseEligibility; server-cached 15 min in activeBook.ts; same universe as Submit Pulse and Portfolio Review |
Portfolio Review | Client roster and active-book membership (churn_date), Group Director (group_director_id→USERS), Account Director fallback (executive_sponsor_id), Cardinal badge (ACQUISITION_ID='1005'), AD/GD auto-team-lead grant at sign-in, Nova→Signal AD sync (CLIENT_ACCOUNT_ASSIGNMENT_HISTORY) | Nova data reaches the app via warehouse replica; sync is freshness-guarded, 1-hour TTL |
POC Management | Client names/tiers/divisions, Group Director (assignment history role=group_director), Account Director fallback (executive sponsor), and the employee directory powering the relationship-owner typeahead | GD/AD chips read-only here; AD chip = manual Supabase pick with Nova exec-sponsor fallback |
Churn Risk | Revenue column (Total Active Retainer), contract dates/weeks-to-end, per-contract Active Contract Details JSON (Advanced Details), and the Open-in-Nova deep link | Contract facts reach Signal via the model feature table. |
Division Forecast | Client roster (executive_sponsor / group_director emails) used to auto-grant the is_team_lead flag at sign-in via POST /api/auth/ensure-provisioned — access gating only, no on-screen data on this view | Role flags themselves are stored in Signal's own records (app_users). |
Service Expansion | Client attributes (tier, division, industry, group/account director) via the pipeline's client-features table | SE view reads them from the ML feature table, not live Nova |
Client Profitability | Self-reported hours per client per month from the weekly Pulse check-in (question 1006, Laravel-encrypted, decrypted server-side with NOVA_APP_KEY), Pulse submission records for the Pulse Coverage KPI numerator, client display names, and the employee roster + client-team assignments behind the delivery-employee denominator | Hours fetched fresh per page load and attributed to the calendar month of the submission date; undecryptable rows silently skipped; per-submission hours above 250 clamped to 250 (code constant as of Jul 2026) |
Divisional P&Ls | Employee names, titles, division/department assignment, termination dates for payroll matching; client display names for the Client Roster | ENTERPRISE.RDS_PG_NOVA.USERS/CLIENTS queries; nova↔payroll match cache populated by scripts/sync-payroll-actuals.ts |
Revenue Overlap | Team seats per account (client_account_assignment_history/users/departments — every seated member, lead and support; AM/AD/PM seats match all departments on their client) and each employee's home division/department (users/divisions/departments; the matrix row axis) | Seat query getSeatAssignmentsQuery() in server/snowflake-only-queries.ts; roster via GET /api/employees (1h cache); Group/Managing Directors stripped from Consulting seats unless deliberately assigned; home division read as-of-now, retroactively recolors history |
Capacity Planning (Role + Company) | Employee roster (names, titles, divisions, departments), client-team seat/assignment history used to attribute revenue to people, and live AD/Sr AD assignments (executive_sponsor / senior_account_directors) via GET /api/nova/assignments | Roster via GET /api/employees (1-hour cache); seats joined to Vena revenue in TypeScript; live Nova AD/Sr AD now wins over Signal's cached copy for non-consulting single-division clients (Jul 2026). |
Growth (People Dash + Performance Radar) | Person roster (name, division, department, title, reports-to, role start), pulse sentiment/bandwidth/weekly hours, novaTalent self/manager scores, Vital 5 & Hedgehog answers, Culture Index profile | Roster served via 3-tier cache (fresh 15 min, up to 24 h stale-while-revalidate). Weekly-hours answers are encrypted and decrypted server-side with NOVA_APP_KEY. |
Growth Access management | Division and department picker vocabulary — the distinct novaDivision/novaDepartment values on employee records in the cached Growth People dataset (visibleDivisionsFromPeople; external contractors, blanks, placeholders excluded). PUT validation rejects divisions/departments not present in this vocabulary. | People dataset is fresh 15 min / stale-served up to 24 h with background refresh (code constants); if never built, the picker is empty until the People tab is opened once. Department-scopable divisions (Shared Services, G&A, Healthcare) are a code constant in src/utils/growthDepartments.ts. |
AI Solutions | Employee roster in the single/multi-employee target pickers (names + departments, active company-email staff only) | GET /api/employees reads Nova's user directory (1-hour server cache); division/department picker options are hardcoded canonical Nova org names in src/utils/orgStructure.ts (9 divisions, 31 departments) — code constants, not live |
Admin Tools (General Access, Assignment History, Hiring Pipeline, JD Sheet Sync, Analytics) | Client/employee names for Assignment History diffs; email→division/department lookup for Analytics 'Adoption by division' (1h server cache); AD role list used to auto-grant Team Lead at provisioning | fetchEmailDivisions in server/usageEnrichment.ts |
Platform Foundation (app shell) | Employee roster, client metadata (vertical/tier/division/churn), departments, headcount, client-team assignment history, and live AD/Sr.AD assignments via the new Nova GraphQL connection (/api/nova/*) | 1-hour caches for roster/clients; 5-min cache for Nova assignments; AD/GD emails from Nova auto-grant Team Lead at provisioning |
Views in Signal that write into Nova platform:
View | What it writes |
Manage Leads | AD / Sr. AD chip saves now go DIRECTLY to Nova via POST /api/nova/assignments → Nova updateClient GraphQL mutation (executive_sponsor_id / senior_account_director_ids); Nova auto-writes its client_account_assignment_history. Consulting team saves still push the reconciled AD to Nova server-side via the reconcile endpoint (updateClientAccountDirector), failure shown as 'Saved in Signal, but Nova sync failed' |
Platform Foundation (app shell) | Boundary case (Manage Leads module, foundation-adjacent): AD/assignment changes write back to Nova via POST /api/nova/assignments (updateClient GraphQL mutation); never writes to Vena/finance data |
Payroll (ADP / Deel / Siesa) (payroll)
Three payroll sub-systems: ADP (US employees, live API), Deel (international employees and contractors, live API), and Siesa/SIESTA (Colombia, monthly spreadsheet upload). Feeds salaries, employment types, and personnel costs.
Owner: People / Finance operations. Relationship to Signal: External system, read-only from Signal.
Views in Signal fed by Payroll (ADP / Deel / Siesa):
View | What it provides | Notes |
Employees | Employment type enrichment (Full Time / Part Time / Apprentice) from ADP workerType, name-matched onto the Nova roster | ADP, not Deel, on this view; best-effort name matching, null on mismatch/outage |
Employee Pulse (Pulse Coverage) | ADP employment-type enrichment on the shared /api/employees roster | Not displayed anywhere on the Employee Pulse view; incidental to the shared roster fetch. |
Divisional P&Ls | Personnel Expenses per department/division per month via cost waterfall: ADP gross + pro-rata NetSuite pools → Siesa Colombia → NetSuite contractor bills → Deel invoices → annualized salary ÷ 12; worker tooltips; source-status legend; per-worker CSV export | ADP/Deel synced nightly 04:00 UTC (render.yaml cron); Siesa manual xlsx; staging reads siesta_payroll_test / deel_monthly_actuals_test / deel_people_test (TEMPORARY); endpoints gated by 15-email PAYROLL_AUTHORIZED_EMAILS allowlist |
Capacity Planning (Role + Company) | ADP employment-type enrichment (Full Time/Part Time) attached to each roster row inside GET /api/employees | Name-matched; null when ADP unreachable. Rides on employee objects; not displayed on capacity tables. Origin is ADP, not Deel. |
Growth (People Dash + Performance Radar) | Annual salary, hire date, worker type/category, country, seniority, contract status, DOB, raise history, YTD commission/bonus/total comp, compensation change history | Three sub-systems: ADP (US employees, live API), Deel (international + contractors, live API), Siesta/SIESA (Colombia, monthly spreadsheet upload). Raises/comp-YTD derived from nightly-ingested monthly gross timelines; matched to roster by name. Staging reads TEMP deel_monthly_actuals_test tables. |
KPI Targets Google Sheet (sheets-kpi)
The People team's 'JD Review Process' / KPI Revision Workstream Google Sheet: the authoritative role-based RPE, MRR, and retention targets per job title (plus JD links on the same physical sheet).
Owner: People team. Relationship to Signal: External system, read-only from Signal.
Views in Signal fed by KPI Targets Google Sheet:
View | What it provides | Notes |
Employees | Role-based KPI targets (RPE/MRR/retention per job title) — Google Sheet → Supabase signal_roles → in-memory cachedTitleTargets served at /api/employee-kpi-targets | Sync runs at server startup and manually from the KPI Targets admin view; no scheduled interval in repo; title-based matching with division-suffix fallback |
KPI Targets | Authoritative RPE/MRR/retention target numbers per role, parsed from free-text KPI cells of the 'CX JD Review Process' and 'SS JD Review Process' tabs (sheet ID 1JdhP_o1LHq44X9jJPSGiHAe2UiMHS9HZfrJ4wc1_NrY, KPI_SHEET_ID env override) | Pulled into signal_roles during every sync (Phase 0 of runKpiMatchSync); heuristic parsing — tab-name hints with auto-discovery fallback, values under $200 assumed thousands, archived CX rows skipped. Same physical sheet also feeds JD links. |
Capacity Planning (Role + Company) | Official KPI targets per job title (RPE/MRR/retention) — the basis of Official Target, % to Target, and Above/At/Below classification | KPI Revision Workstream Google Sheet (KPI_SHEET_ID env, hardcoded default) synced by runKpiMatchSync() ~30 s after server boot and on demand via POST /api/kpi-targets/sync into Supabase signal_roles → GET /api/employee-kpi-targets; frontend matches exact title then 'title, division'. |
Platform Foundation (app shell) | Role-based KPI targets synced into Signal's kpi_targets records at server startup (POST /api/kpi-targets/sync) | Drives the Above/At/Below (>105% / 95–105% / <95%) status badges |
JD Map Google Sheet (sheets-jd)
A Google Sheet mapping employee names/titles to job-description Google Docs, read via a service account.
Owner: Ops / People team. Relationship to Signal: External system, read-only from Signal.
Views in Signal fed by JD Map Google Sheet:
View | What it provides | Notes |
Employees | Job-description doc links per person/title (live JD map sheet over static server/jd-data.ts snapshot) via /api/jd-links | Pulled at startup + Gandalf-only POST /api/jd-sheet/sync; name match first, then title |
Admin Tools (General Access, Assignment History, Hiring Pipeline, JD Sheet Sync, Analytics) | JD Sheet Sync source: Name/Title → JD Google Doc map read via Google service account | Sync result held in server memory only; reverts to static snapshot (server/jd-data.ts) on restart/deploy; POST sync is server-enforced Gandalf |
CSM Renewal Tracker Google Sheet (sheets-renewal)
The Client Success team's 2026 client renewal tracker: account leaders' declared renewal, churn, downchurn, project-end, and saved statuses per contract per month. Reaches Signal through a mirrored warehouse view.
Owner: Client Success (CSM) team. Relationship to Signal: External system, read-only from Signal.
Views in Signal fed by CSM Renewal Tracker Google Sheet:
View | What it provides | Notes |
Division Forecast | All on-screen data on all six tabs: revenue at risk (CURRENT_RETAINER), forecasted churn (FORECASTED_CHURN_AMOUNT_FORMULA sheet formula), churn statuses, renewal stages, account/contract/division/Account Director names, and available months — via the mirrored view NOVAX_CUSTOMER_MANAGEMENT.PROD_MODEL.FINAL_CLIENT_RETENTION_FORECASTING_2026, served by GET /api/retention-forecast with a 15-minute server cache | Single API call powers the whole view; sheet→mirror pipeline cadence not defined in the app repo. Endpoint has no server-side auth middleware — gate is frontend-only. |
Hiring Pipeline Google Sheet (sheets-hiring)
The finance-owned hiring pipeline sheet listing open roles per division. It is the system of record: Signal reads roles from it and writes approvals and 'Future Backfill' rows back into it.
Owner: Finance. Relationship to Signal: External system, read + written by Signal.
Views in Signal fed by Hiring Pipeline Google Sheet:
View | What it provides | Notes |
Admin Tools (General Access, Assignment History, Hiring Pipeline, JD Sheet Sync, Analytics) | Hiring Pipeline roles for both tabs (Power Digital 'Divison Sal Cap', Cardinal tab auto-discovered); Approve writes stage '2 - Approved' + date back to the sheet | 15-min server cache (code constant), 'Sync Sheet' bypasses; GET/approve endpoints have no server-side role middleware — Approve is UI-gated to Admin+ |
Views in Signal that write into Hiring Pipeline Google Sheet:
View | What it writes |
Growth (People Dash + Performance Radar) | One-time 'Future Backfill' row appended to the Hiring Pipeline Google Sheet when Backfill is first set to Yes (idempotent via backfill_pushed_at; best-effort, failure doesn't block the save) |
Admin Tools (General Access, Assignment History, Hiring Pipeline, JD Sheet Sync, Analytics) | Hiring Approve button writes stage '2 - Approved' + today's date to the finance hiring Google Sheet (the system of record) and invalidates the server's 15-min hiring cache |
Salesforce (salesforce)
The CRM: open and closed opportunities, service-expansion deals, won/lost contracts, and close rates. Reaches Signal pre-joined into the AI/ML pipeline's tables — Signal never queries Salesforce directly.
Owner: Sales / RevOps. Relationship to Signal: External system, read-only from Signal.
Views in Signal fed by Salesforce:
View | What it provides | Notes |
Portfolio Review | Salesforce history section inside the embedded SE detail tab (FINAL_SALESFORCE_HISTORY); contract wins/losses 3m likely opportunity-derived | Won/lost-contracts upstream origin not definitively stated in repo |
Churn Risk | Detail-panel Salesforce section: open opportunities, open SE opportunities, close rates, won/lost contracts last 3 months, failed-SE-in-last-2-weeks flag, freshness timestamp | Joined into the model score table upstream. |
Service Expansion | Open SE / Failed SE status, won/lost opportunities last 3 months (with verbatim lost-deal explanations), close rate, last closed-won contract, full opportunity history tab | Carried through the weekly pipeline as JSON columns; history via GET /api/predictions/se/salesforce-history/:clientId |
Gong call analysis (gong)
AI call-analysis sentiment derived from recorded client calls: per-contact (POC) satisfaction, engagement, mood, churn-risk, and call-rate scores, plus a client-level Gong sentiment score. Produced by an AI/ML pipeline over Gong recordings; Signal reads the pipeline's output tables.
Owner: AI/ML team (pipeline over Gong recordings). Relationship to Signal: External system, read-only from Signal.
Views in Signal fed by Gong call analysis:
View | What it provides | Notes |
Portfolio Review | Gong Sentiment column, gong activity status, POC Contacts tab (FINAL_POC_SENTIMENT per-contact satisfaction/engagement/mood/influence) | Gong score of exactly 0.5 is a no-data convention and renders as '—' |
POC Management | Per-contact rolling 5-week call-sentiment scores (satisfaction, engagement, churn risk, decision influence, mood, positive/negative call rates) from the AI call-analysis pipeline over recorded client calls (FINAL_POC_SENTIMENT; upstream pipeline defined outside the app repo, call-derived sentiment labeled 'gong' elsewhere in Signal) | Read via fetchPocContactsByClient in server/predictions.ts; 15-min server cache; is_decision_maker stubbed false pending upstream column |
Churn Risk | Call-sentiment inputs (GONG_SCORE) to the sentiment sub-scores and the entire POC Contacts accordion (per-contact satisfaction/engagement/mood/call rates over 5 weeks) | is_decision_maker hardcoded false pending upstream column. |
Service Expansion | POC contact sentiment (satisfaction, engagement, mood, churn risk, call rates; 5-week rolling), Gong sentiment score feeding the Sentiment composite | is_decision_maker hardcoded false pending upstream column |
AI Solutions | Sentiment inputs behind the Sentiment Risk (0-1) metric — Gong call-recording-derived sentiment (source label 'gong'); Pulse survey responses are the other input (label 'gsheets') | Only clients with a real sentiment source count toward avgSentimentRisk; model defaults excluded; sentimentAsOf = latest model-run week shown in UI |
ML models (data-science pipeline) (ml)
The AI/ML team's weekly model outputs: churn risk scores, service-expansion readiness scores, ETCR tables, priority tiers, assembled client feature sets, and weekly historical score archives. Derived data — the models' own upstream inputs include Vena, Nova, Salesforce, Gong, and Pulse.
Owner: AI/ML team. Relationship to Signal: Derived/generated data.
Views in Signal fed by ML models (data-science pipeline):
View | What it provides | Notes |
ETCR (Beta) | All ETCR metrics: per-AD/GD/Division monthly rows (ETCR, retainer/project MRR, confirmed/forecasted churn, commit/best-case pipeline, projected expansion/churn, floor/ceiling) from the upstream ETCR pipeline's four tables FINAL_ETCR_MONTHLY_{AD,GD,DIVISION,DIVISION_DETAILED} in NOVAX_CUSTOMER_MANAGEMENT.PROD_MODEL, read via GET /api/etcr (server/index.ts:11355, SELECT *, requireAuth) | Pipeline built and owned by the AI/ML team (Lily) outside the app repo; the business-system origins of its inputs (confirmed vs forecasted churn, Commit/Best Case expansion stages — possibly Salesforce per adjacent design docs) are not defined in the app repo. Division-detailed table is fetched fail-soft. All ratios, the 60% goal (ETCR_GOAL=0.60), and the project-MRR ÷6 conversion are frontend code constants/computations in ETCRView.tsx. |
Submit Pulse Data | weeks_until_end churn feature and churn status used in the pulse-eligibility rule — churned clients excluded; otherwise a client needs current-month MGP > 0, VABO > 0, or weeks-until-end > 0 to appear in the picker | isPulseEligible() in src/utils/pulseEligibility.ts, mirrored in server/pulseEligibility.ts with test-enforced parity |
Pulse Summary | Churn risk score/category (CHURN_SCORES; thresholds 0.6/0.5/0.3 as code constants), client division and name enrichment, and SE status fields OPEN_SE_CONTRACTS / WON_CONTRACTS_3M / FAILED_SE_IN_LAST_2_WEEKS (FINAL_FEATURES) | AI/ML pipeline tables; refresh cadence not defined in the app repo; clients missing from FINAL_FEATURES get blank division and Unknown SE/churn |
Portfolio Review | Churn Risk / Profile Risk scores (CHURN_SCORES), SE Readiness + sentiment/performance sub-scores (SE_SCORES), SE recommendations, Priority P1/P2/P3 (FINAL_FEATURES.PRIORITY_TIER), tier/industry/contract-details/churn-status/past-due-AR fields, historical score trends (HISTORICALS) | Priority rules and refresh cadence (~weekly) owned by data team; missing tier defaults to P3; portfolio uses raw SE_SCORES readiness |
Churn Risk | Weekly 0–1 churn score plus Client Profile / Sentiment / Performance sub-scores, performance and sentiment sub-signals (SENT_/PERF_ fields), risk-score history (weekly archive), and the assembled client feature set | Model runs outside the Signal repo; scores passed through, never recomputed. Bands 0.3/0.5/0.6 and at-risk threshold 0.30 are Signal code constants. |
Service Expansion | Readiness score + Blueprint/Performance/Sentiment composites and sub-scores, Is Ready flag, dynamic ready threshold (model config, 0.7 code fallback), service recommendations with sub-scores, service history (running/ran/never-ran), weekly historical score trend, contract features (days to end, blueprints) | Weekly data-science pipeline; Signal merges/labels/serves with a 15-min server cache; table score overridden with latest weekly historical snapshot |
AI Solutions | All client-health metrics: 0-1 churn risk scores (avg risk, % at risk >=0.30, Low/Medium/High/Critical tiers), SE readiness and retention scores, plus the weekly historical score runs behind the trend chart and reconstructed baselines | Same churn/SE model feeds and ~15-min server cache as the Churn Risk and Service Expansion views; server/aiSolutionHealth.ts + server/predictions.ts |
Admin Tools (General Access, Assignment History, Hiring Pipeline, JD Sheet Sync, Analytics) | Weekly point-in-time churn bands (and SE readiness) for Analytics 'Usage by client risk' and 'Actions on flagged clients' — risk as of the event week, not today | POST /api/usage/client-status → HISTORICALS CHURN_SCORES/SE_SCORES; card-scoped degradation on failure |
Signal's own records (signal)
Data entered by users in Signal itself and stored in Signal's Supabase tables: team assignments and revenue splits, pulse submissions, notes and action triage, data-issue flags, access tiers and Growth grants, KPI role copies, saved AI text, and more. Signal is the origin — no upstream business system holds this data.
Owner: Signal users / Signal (AI/ML) team. Relationship to Signal: Signal-origin data.
Views in Signal fed by Signal's own records:
View | What it provides | Notes |
Summary | Team-assignment overrides (team_assignments) that win over Nova-detected seats where a client's team is managed in Signal (affects Assigned Leads, Total Seats, per-employee attribution); role flags (app_users) and growth-access grants that decide the Member vs full experience; department_mapping for department display identity | Render-disk assignment backups serve as a Supabase-outage fallback for the override pipeline |
Employees | Signal's own records (entered by users in Signal): saved team assignments from Manage Leads (team_assignments — win over Nova-detected seats for managed clients), allocation % overrides (revenue_split_overrides — applied in every RPE/share calculation), on-leave flags (employee_flags — flagged people excluded from aggregates in every period), VABO client tags (client_flags — drive the drawer's VABO Allocations table), synced KPI-target role copies (signal_roles behind /api/employee-kpi-targets), and app_users role flags that decide the Member 'Your Record' vs full-roster experience | team_assignments read via the service-role proxy GET /api/team-assignments (Render in-memory backups as Supabase-outage fallback); employee_flags / client_flags / revenue_split_overrides read directly from the browser (src/services/supabase.ts); point-in-time team lookups for dates ≥ 2026-07-01 read Nova history instead |
Staffing | Signal's own records (entered by users in Signal): manual lead assignments from Manage Leads (team_assignments — win over Nova-detected teams), managed-clients registry, saved revenue-split overrides from the Employees view (revenue_split_overrides — base layer under simulated % edits), on-leave employee flags (employee_flags — Consulting weighting fallback), cached AD/Sr.AD tables (client_account_directors / client_senior_account_directors), and app_users role flags that gate access | Overrides loaded for the first selected month only |
Manage Leads | Signal's own records (entered by users in Signal): saved team assignments that override the auto-detected team (team_assignments via GET /api/team-assignments service-role read, direct database read as fallback), managed-client adopt/start-blank registry (managed_clients), custom percentage splits (revenue_split_overrides), Consulting role snapshots (consulting_roles, Nova role fallback), save-history Log modal (change_history, last 200 entries), per-division assignments for ~10 multi-division clients (multi_division_assignments), cached AD/Sr. AD tables (client_account_directors / client_senior_account_directors) merged with live Nova, and the app_users is_team_lead flag gating the view | Once a client has saved assignments or a managed decision, the saved team always wins over Nova auto-detection; live reads per month switch with optimistic updates on save |
Employee Pulse (Pulse Coverage) | Access gating only: app_users is_admin/is_gandalf flags checked by AdminRoute (frontend) and requireAdmin (backend, 5-min cache); hardcoded Sauron super-admin email list bypasses DB state | No on-screen data comes from Signal's own records. |
KPI Targets | Working copy of roles and targets (signal_roles), employee-title match log (signal_match_log), sync history / 'Last synced' (signal_sync_log), and admin/Gandalf role flags (app_users) | Supabase = Signal's own records. In-memory server caches (match counts, title→targets map, coverage) back these when RLS blocks reads or before first sync; caches reset on restart and are rebuilt by a 30s startup auto-sync. |
KPI Targets | Curated title→role mapping JD_TITLE_MAP (server/jd-title-map.ts, 165 entries from a 2026-04-17 'Signal Title / JD' Excel export) that deterministically decides which Nova titles match which roles or are excluded | Hardcoded code constant; strict mode — no fuzzy fallback for employees, no auto-create (banner's auto-created count is always 0). Fixing unresolved titles requires a code change or a sheet change. |
Submit Pulse Data | Weekly and QBP form contents, prefill, Submitted/Missing pills, and the submission-history timeline (pulse_weekly_submissions / pulse_qbp_submissions, voided rows shown struck-through); AD/Senior-AD submit rights (client_account_directors / client_senior_account_directors, maintained via Manage Leads); and app_users tier flags (is_team_lead/is_admin/is_gandalf) gating the view | Pulse rows read directly from the browser via authenticated Supabase REST (src/services/supabase.ts); all writes go through the backend — RLS (migration 20260509b_pulse_rls_tightening.sql) blocks direct browser writes |
My Submissions (Client Pulse) | Weekly and QBP submission rows (pulse_weekly_submissions / pulse_qbp_submissions): client, week/quarter, sentiment, pacing, QBP date, status (active/voided), submitted_by, updated_at — Signal's own records entered via /pulse/submit; served by GET /api/pulse/my-submissions filtered to the caller | Includes voided rows; tab counters count voided; division column stored but not displayed |
Pulse Summary | Weekly pulse answers (sentiment, pacing to goal, goals established, business performance YoY) from pulse_weekly_submissions (active rows only); week dropdown options; department_mapping canonical department identity; app_users role flags that gate access | Entered by Account Directors on Submit Pulse; live, upserted per (client, week); voided submissions excluded |
Portfolio Review | Pulse weekly + QBP columns (pulse_weekly_submissions/pulse_qbp_submissions, never cached), AD overrides (client_account_directors), Status chip inputs (recommended_action_feedback, app_users), client notes, action notes, cached Brand Intel (macro_brand_insights), asana_links, role flags (app_users) | Supabase = Signal's own records entered by users in Signal |
POC Management | POC cards (owners, context, title overrides, LinkedIn, reports-to, last 1:1, prospect cards) in poc_cards; AD overrides (client_account_directors), Senior AD rows (client_senior_account_directors), latest-month team rosters (team_assignments) for view-only scope, and app_users role/admin flags | Cards read directly from the browser via open-read RLS (listPocCards); all writes via gated backend |
Churn Risk | Status chips via recommended_action_feedback, manual follow-ups, action notes, client notes, AI summaries/journey narratives (client_summaries), brand intel (macro_brand_insights), recommended-actions snapshots, asana_links, app_users name resolution; also Client Pulse submissions entered in Signal feed the model's sentiment inputs upstream ('gsheets' label is legacy naming for Pulse) | Signal's own records = entered by users in Signal (Supabase transport). |
Service Expansion | Dismissals/auto-resurface state (se_client_feedback), change audit (change_history), action feedback + notes, manual follow-ups, client notes, ★ favorites (user_account_favorites), persisted AI text | Supabase = Signal's own records entered by users; never holds scores |
Client Profitability | team_assignments lead corrections from Manage Leads that shape the delivery-employee (Pulse Coverage) denominator, and app_users admin flags gating the route; Cost/Profit/Margin are derived in code at HOURLY_RATE = 175 $/hour (server/index.ts:3345 — a hardcoded constant, not payroll data) | Since Jul 2026 live Nova AD/Sr-AD assignments override Signal's cached lead rows for non-consulting single-division clients, so the denominator can shift without a Signal edit; the $175 rate, 40% margin tier, 250-hour clamp, and coverage cutoffs are all code constants |
Divisional P&Ls | division_forecasts table (ten-metric forecast grid feeding Team Forecast P&L rows) and app_users.is_gandalf access flag | Supabase; GET /api/division-forecast open read, PUT requires auth only (not Gandalf) |
Revenue Overlap | Signal's own records: team_assignments + managed_clients overrides from Manage Leads (saved teams always win over nova-detected teams), app_users access flags (is_team_lead/is_admin/is_gandalf gating the view), department mapping table, Render-disk assignment backups as Supabase-outage fallback | Merged client-side in DataContext via buildManageLeadsDisplayAccounts; revenue_split_overrides exist in Signal's records but are IGNORED by this view |
Capacity Planning (Role + Company) | Signal's own records (entered by users in Signal): team_assignments lead overrides, revenue_split_overrides, synced signal_roles targets, and the app_users is_admin flag that gates access | team_assignments read uncached via GET /api/team-assignments; splits applied in calculateAllocationsWithOverrides; admin flag read by AuthContext from app_users and cached in sessionStorage. |
Growth (People Dash + Performance Radar) | Manual judgments (performance rating, backfill, retention risk, cannot-lose, on-leave, career path, radar status, WW1–WW3, notes), access grants (growth_access_grants) and per-tab capabilities (growth_access_capabilities), manual Culture Index URL / Vital 5 goal fallbacks | Signal's own records entered by users in Signal; all reads/writes go through division-scope-checked backend proxies; every save stamps editor email + timestamp. |
Growth Access management | Access records shown in the grants table: division/department grant rows (growth_access_grants, active = revoked_at IS NULL) and per-user capability flags for People / Performance Radar / Manage access (growth_access_capabilities), plus the registered-user dropdown (app_users). All entered by users in Signal. | Read via GET /api/growth/access (service-role, RLS-no-policies tables) except app_users which the view reads directly via the Supabase client. Soft-revoked rows retained as audit trail. |
AI Solutions | Solution registry (ai_solutions), snapshots (ai_solution_snapshots: baseline/periodic/manual with metrics/health/capture_meta JSON), cron run log (ai_solution_health_runs), team assignments used to resolve employee targets, revenue split overrides, and the is_gandalf access flag | Signal's own records (entered by users in Signal); frontend reads/writes ai_solutions tables directly via the Supabase client; server uses service-role REST |
Corrections | Entire view: corrections queue rows, status tab counts, expanded detail (current/expected value, reason, reviewer fields, timestamps) — all from the data_corrections table entered by users in Signal, read directly from the browser via src/services/supabase.ts (no Express backend involved) | Disputed figures originate in Vena and are shown on other Signal views, but Corrections never queries Vena — it only stores what the human reported. Counts computed client-side by fetching all rows; 10s query timeout and PAGE_SIZE=50 are code constants. |
Admin Tools (General Access, Assignment History, Hiring Pipeline, JD Sheet Sync, Analytics) | app_users (user tiers, toggles), change_history (audit log), signal_usage_events (Analytics events), recommended_action_feedback (actions-on-flagged-clients), kpi_targets configuration feeding the Hiring Need verdict | Users/Assignment History/Analytics read Supabase directly from the browser; role toggles write app_users; app_users rows provisioned server-side on sign-in with AD auto-team-lead |
Platform Foundation (app shell) | Signal's own records (entered by users in Signal): app_users tier flags, growth_access_grants + growth_access_capabilities, team_assignments/managed_clients overrides, revenue_split_overrides, client/employee flags, user_account_favorites, kpi_targets, signal_usage_events, llm_generation_logs | Supabase is the store; described as Signal's own records per doc rules. Role checks cached 2–5 min server-side + sessionStorage in browser |
Views in Signal that write into Signal's own records:
View | What it writes |
Summary | Flag-a-data-issue from any drill-down row inserts a data_corrections record (status pending) directly from the browser (submitDataCorrection, src/services/supabase.ts:1539); reviewed by admins in Corrections, trackable in My Submissions |
Employees | Admin-only Mark On Leave / Remove Leave toggle in the employee drawer writes flag 'on_leave' to employee_flags (direct browser write, optimistic with rollback); side effect: person excluded from ALL aggregate metrics across Signal, all months (global flag, not month-scoped) |
Manage Leads | team_assignments (team membership per client × department × month, add-before-delete with read-back verify + retries); revenue_split_overrides (custom %); consulting_roles snapshot (refreshed server-side after Consulting saves); managed_clients (adopt/start-blank decision, adoption seeds team_assignments); change_history (audit row per team save); multi_division_assignments (per-division assignments for ~10 multi-division clients — server path exists, not yet wired to the view's chips) |
Manage Leads | Fire-and-forget assignment backup on every team save (POST /api/assignments/backup → server-side store on Render disk) |
KPI Targets | Re-sync Matches (POST /api/kpi-targets/sync → runKpiMatchSync): updates/creates/renames/deletes signal_roles rows from the Google Sheet (incl. division-suffix propagation at fuzzy score ≥ 0.7 and synonym canonicalization); clears and re-inserts the full signal_match_log; appends a success row to signal_sync_log; deletes roles with zero matched employees; rebuilds in-memory caches (match counts, title→targets map served at /api/employee-kpi-targets, coverage breakdown). Same pipeline auto-runs ~30s after every backend restart. No direct target editing on the screen — targets are only editable in the Google Sheet, mappings only in jd-title-map.ts. |
Submit Pulse Data | Weekly pulse upsert via POST /api/pulse/weekly into pulse_weekly_submissions (one row per client × week × division; server re-checks ownership via assertPulseOwnership() and stamps submitted_by from the caller's token; re-saving preserves GD Review Notes; re-submitting a voided week revives the row) |
Submit Pulse Data | QBP upsert via POST /api/pulse/qbp into pulse_qbp_submissions (one row per client × quarter × division; QBP Date required and allowed up to 1 year ahead, narrative + presentation links required) |
Submit Pulse Data | Soft-void via POST /api/pulse/weekly/void and /api/pulse/qbp/void — status flips to 'voided' with deleted_by + trigger-set deleted_at; never hard-deleted; allowed only for the original submitter or a Gandalf admin |
Portfolio Review | GD Review Notes + WoW Changes saved to pulse_weekly_submissions via POST /api/pulse/weekly (ownership-gated: assigned AD/GD or Pulse admin only) |
Portfolio Review | Client notes (client_notes, churn + SE modules), recommended-action triage (recommended_action_feedback) and action notes (action_notes) via embedded Churn Risk tab — flips the Status chip |
Portfolio Review | Brand Intel results persisted to macro_brand_insights (one per client per month); Asana task links to asana_links |
Portfolio Review | Side effect on page load: Nova→Signal AD sync writes client_account_directors + team_assignments (freshness-guarded, 1-hour TTL) |
POC Management | Table poc_cards: create a note card on an existing sentiment-tracked contact (is_prospect=false; owner, title override, LinkedIn, reports-to, last 1:1 date/person, context), create a prospect card (is_prospect=true; name required), edit any of those fields, and soft-archive prospects (archived_at/archived_by; never hard-deleted). Every write attributed to the verified caller email and gated server-side (assertPocEditAllowed) to the client's GD / AD (incl. Senior AD and Nova exec-sponsor fallback) / admin via POST /api/poc-management/cards and PATCH /api/poc-management/cards/:id. No side effects on any other module — as of Jul 2026 nothing else reads poc_cards. |
Churn Risk | Recommended-action Done/Dismiss/Undo events (recommended_action_feedback, drives status chips) |
Churn Risk | Manual follow-ups create/edit/archive (manual_recommended_actions) |
Churn Risk | Per-action threaded notes (action_notes) |
Churn Risk | Client-level notes (client_notes) |
Churn Risk | AI Summary regenerate and Iris Draft generate — overwrite/insert weekly rows in client_summaries (types churn / churn_journey) |
Churn Risk | Brand Intel generation results inserted into macro_brand_insights (per-user daily cap, default 50) |
Churn Risk | Asana task↔client link rows (asana_links), archivable from the card |
Service Expansion | se_client_feedback (dismiss/reactivate with reason, score-at-dismissal, resurface threshold; auto-resurface at ≥0.15 score move) + change_history audit rows |
Service Expansion | client_summaries upserts for type=se (AI Summary regenerate/lazy-generate) and type=se_roadmap (roadmap generate/regenerate), attributed to user email |
Service Expansion | macro_brand_insights (Brand Intel generation, one row per client per month, attributed; counts against per-user daily cap) |
Service Expansion | recommended_action_feedback (done/dismiss auto actions), action_notes (per-action notes), manual_recommended_actions (user follow-ups), client_notes (module-scoped se), user_account_favorites (★), signal_usage_events (telemetry) |
Divisional P&Ls | Division forecast cells: PUT /api/division-forecast upserts (division, month, metric, value) into division_forecasts with editor email + timestamp; fixed 10-metric vocabulary enforced server-side; only forecast months (after the 2-months-back actuals cutoff) are editable in the UI; saved values immediately feed the P&L tab's Team Forecast rows, Gross Profit, Operating Expenses, and Contribution Margin |
Growth (People Dash + Performance Radar) | employee_performance upserts: performance rating, backfill, retention risk + context, cannot lose, on leave, career path score, culture index URL, vital 5 goal, radar_status (PM auto-seeded from Active PM rating; RT/TERM manual), WW1–WW3 dates, notes — editor email + timestamp recorded |
Growth (People Dash + Performance Radar) | growth_access_grants (division/department scope rows, revoke-before-insert) and growth_access_capabilities (can_view_people / can_view_radar / can_manage_access) via PUT /api/growth/access from the /growth/access management screen |
Growth Access management | growth_access_grants: full reconcile of a user's division/department scope per save — rows no longer in scope are soft-revoked (revoked_at stamped, never hard-deleted; revoke applied before insert so failures err toward less access), new rows inserted with granted_by = saving manager's email. 'All divisions' = one division=NULL row; each specific department = its own row. |
Growth Access management | growth_access_capabilities: upsert of the user's single row (can_view_people, can_view_radar, can_manage_access, updated_by/updated_at); row deleted when a save leaves no scope and no capabilities (full revoke). Side effect: backend permission cache invalidated for that email (2-min TTL elsewhere); target user's tab visibility updates on their next sign-in/reload. |
AI Solutions | Create solution → insert into ai_solutions + immediate baseline row into ai_solution_snapshots (browser-computed metrics + frozen health) |
AI Solutions | Delete solution (confirm dialog) → removes ai_solutions row and all its snapshots |
AI Solutions | Change status (Active/Paused/Completed/Archived) → updates ai_solutions.status; only active solutions get weekly health snapshots, archived also drop from the list Health column |
AI Solutions | Edit notes → updates ai_solutions.notes |
AI Solutions | Take Snapshot → inserts a 'manual' row into ai_solution_snapshots (latest-month metrics + current health) |
AI Solutions | Regenerate Baselines → patches metrics/snapshot_month/capture_meta on baseline snapshots that captured empty data |
AI Solutions | System writes: weekly cron (Mondays 09:30 + startup catch-up) inserts 'periodic' health-only snapshots and logs to ai_solution_health_runs; startup backfill patches reconstructed health onto baselines missing it; ops-only manual trigger POST /api/ai-solutions/snapshot-health (X-Snapshot-Secret) |
Corrections | Insert new flag into data_corrections (status pending, submitter's Google email) via FlagIssueModal — reachable from this page and from Summary drill-down flag icons |
Corrections | Update flag status (pending→reviewed/resolved, reviewed→resolved) with reviewed_by email, optional reviewer_notes, and resolved_at timestamp — admin-only in UI |
Corrections | Delete a flag — admin-only in UI, restricted to pending rows by database policy (20260506 migration); reviewed/resolved rows immutable for audit trail |
Admin Tools (General Access, Assignment History, Hiring Pipeline, JD Sheet Sync, Analytics) | Users screen toggles write is_team_lead / is_admin / is_gandalf on the person's app_users row (Sauron rows locked; takes effect on the target's next session/refresh) |
Admin Tools (General Access, Assignment History, Hiring Pipeline, JD Sheet Sync, Analytics) | JD 'Resync JD Sheet' refreshes an in-memory server map from the JD Google Sheet (no database write; lost on restart/deploy). Assignment History and Analytics are read-only. |
Platform Foundation (app shell) | First-sign-in auto-provisioning creates the user's app_users row (Team Lead auto-set for Nova Executive Sponsors/Group Directors); never downgrades |
Platform Foundation (app shell) | My Accounts favorites (user_account_favorites) per user |
Platform Foundation (app shell) | llm_generation_logs: every Iris and AI-search generation (user email, full prompt, response, tokens, latency) |
Anthropic Claude (claude)
AI-generated text produced on demand via the Anthropic API: client AI summaries, growth roadmaps, Brand Intel briefings (with web search), capacity insights, Iris chat, and AI search answers. Generated content is usually persisted back into Signal's own records.
Owner: AI/ML team (via Anthropic API). Relationship to Signal: Derived/generated data.
Views in Signal fed by Anthropic Claude:
View | What it provides | Notes |
Portfolio Review | Brand Intel tab — on-demand macro/brand analysis via claude-sonnet-4-5 with web search | ~$0.50/fresh generation; cached per client per month; 50/user/day cap (env-tunable) |
Churn Risk | AI Summary text (claude-sonnet-4-6), Brand Intel briefing (claude-sonnet-4-5 + web search, prompt brand_intel_v2, ~$0.46–0.50/run, default 50/user/day cap), and Iris Draft journey narrative with [D:field=value] grounding validation | All generated server-side and persisted to Signal's own records. |
Service Expansion | AI Summary (claude-sonnet-4-6, weekly Mon 09:15 cron + lazy + manual), Strategic Growth Roadmap (claude-opus-4-6, on demand, browser-built prompt), Brand Intel (claude-sonnet-4-5 + live web search, monthly per client) | All persisted in Signal's own records (client_summaries, macro_brand_insights); Brand Intel per-user daily cap default 50 |
Capacity Planning (Role + Company) | Capacity Insights — on-demand 3-5 prioritized AI-deployment recommendations plus follow-up chat over a text snapshot of the on-screen capacity numbers | POST /api/claude proxy, default model claude-sonnet-4-6, last 20 messages kept; no logContext passed, so NOT recorded in llm_generation_logs. |
Platform Foundation (app shell) | Global Search 'Ask AI' answers (Haiku claude-haiku-4-5-20251001) and the Iris chat drawer (Opus claude-opus-4-7, Sonnet claude-sonnet-4-6 overload fallback), proxied through POST /api/claude | Iris sees only a client-side snapshot of on-screen data; every generation logged to llm_generation_logs with user email, prompt, response, tokens, latency |
Views in Signal that write into Anthropic Claude:
View | What it writes |
Capacity Planning (Role + Company) | Sends the on-screen capacity snapshot (headlines, division/role breakdown, logged AI notes) to the Anthropic API via POST /api/claude; response displayed only, never persisted anywhere |
Asana (asana)
The task-management tool: Signal reads live task status and creates real Asana tasks from recommended actions; task-to-client links are stored in Signal's own records (asana_links) and sync back via webhook.
Owner: Ops / delivery teams. Relationship to Signal: External system, read + written by Signal.
Views in Signal fed by Asana:
View | What it provides | Notes |
Employees | External intake form for title/dept/division change requests (hardcoded URL, link-out only — no data read) | SYSTEM_CHANGE_BOARD_URL in EmployeeView.tsx |
Portfolio Review | Asana Tasks card in expanded row (live task list via Asana API) | Task↔client links stored in Signal's asana_links |
Churn Risk | Live task status in the Asana Tasks card; task creation from recommended actions; webhook sync back into asana_links | Shows a not-configured state if server lacks the Asana token. |
Service Expansion | Live Asana tasks card, task creation, webhook + 60s pull sync | Links stored in asana_links |
Views in Signal that write into Asana:
View | What it writes |
Portfolio Review | Real task creation via POST /api/asana/tasks from the '+ New task' modal |
Churn Risk | Real Asana task creation via server bot token, pre-filled from the recommended action |
Service Expansion | Creates real tasks in Asana via API; Signal↔Asana link rows stored in asana_links; status syncs back via webhook + 60s pull |
Signal usage logs (usage)
Signal's own telemetry table (signal_usage_events): automatic page views, feature actions, and freeze/long-task performance events. Written invisibly by almost every view and read only by the Sauron-only Analytics view.
Owner: Signal (AI/ML) team. Relationship to Signal: Signal-origin data.
Views in Signal fed by Signal usage logs:
View | What it provides | Notes |
Churn Risk | Invisible telemetry: page_view (module 'churn') and client_detail_view events into signal_usage_events; app-wide freeze/longtask perf events added Jul 7 | Feeds adoption reporting. |
Service Expansion | signal_usage_events telemetry: page opens, client_detail_view, roadmap generated/copied, continue-on-Iris clicks | Powers adoption reporting |
Admin Tools (General Access, Assignment History, Hiring Pipeline, JD Sheet Sync, Analytics) | App-wide page_view events (usePageViewTracking on every route change) plus feature actions for churn/se/portfolio modules | Fetch capped at 5,000 events / 10,000 taken-actions per range (code constants) |
Platform Foundation (app shell) | page_view events per route change plus freeze/longtask telemetry into signal_usage_events; feeds the Sauron-only Analytics view | Churn/SE/Portfolio views self-instrument and are skipped by the route hook to avoid double counting |
Views in Signal that write into Signal usage logs:
View | What it writes |
Summary | Automatic page_view usage event with module 'summary' into signal_usage_events (src/hooks/useUsageTracking.ts), feeding the Sauron-only Analytics view |
ETCR (Beta) | Automatic page_view usage event with module name 'etcr' logged to Signal's own usage records on each visit (app-wide useUsageTracking hook → logUsageEvent); not user-initiated. No other writes — the view is strictly read-only, no edits/overrides/annotations. |
Pulse Summary | Passive page_view usage-telemetry event (user email, module 'pulse-summary') logged to Signal's signal_usage_events table on each visit via usePageViewTracking; otherwise the view is strictly read-only (CSV export is a download, filters are local UI state) |
Portfolio Review | signal_usage_events: page views, client row expands, briefing-strip clicks (direct browser writes; feeds Analytics/Sauron) |
Churn Risk | Invisible usage events (signal_usage_events, module 'churn': page_view, client_detail_view) |
Client Profitability | Passive page_view telemetry only (module 'profitability') into signal_usage_events via usePageViewTracking; otherwise strictly read-only — the Excel export (signal-profitability.xlsx) is a client-side download and filters are local UI state |
Revenue Overlap | Passive page_view telemetry only: logUsageEvent() via usePageViewTracking route hook writes a signal_usage_events row (module 'revenue-overlap') on route entry, feeding the Analytics/adoption view. No user-facing writes — the view is read-only; filter dropdowns don't persist and incoming URL params are consumed then cleared. |
Platform Foundation (app shell) | signal_usage_events: page_view per route change + longtask freeze telemetry (fire-and-forget) |
Browser-local storage (browser-local)
Per-browser, per-user state that never leaves the machine: localStorage/sessionStorage preferences (columns, theme, pinned views), staffing what-if scenarios, drawer states, and client-side CSV/Excel exports.
Owner: Individual user. Relationship to Signal: Signal-origin data.
Views in Signal fed by Browser-local storage:
View | What it provides | Notes |
Summary | Pinned-views list defaulting to /summary (localStorage key signal-pinned-views) and session-shared division/department filter state | View state only, no business data |
Employees | Column show/hide preferences (useTableConfig 'employee_list' / 'kpi_rollup'), pinned nav views, CSV exports (employees.csv, kpi_rollup.csv) | localStorage only; no shared state |
Staffing | The simulation scenario itself (reassignments + allocation-% overrides), persisted to localStorage key signal-staffing-scenario:, auto-saved 200 ms after each change; CSV export generated client-side | Per user, per browser; never leaves the machine; the only 'write' surface of the module |
Manage Leads | Draft percentage-override edits and optimistic UI state before Save | Nothing persists locally beyond the session |
ETCR (Beta) | 'Show forecast inputs' drawer open/closed state (sessionStorage key etcr.showDetails) | Per browser tab session; never sent to any server. |
Employee Pulse (Pulse Coverage) | Excel exports (Coverage: per-employee per-week status/hours/client breakdown; Sentiment: per-employee 4w + last-week values) built entirely from data already loaded in the page | Reflects active filters at export time. |
Portfolio Review | Column visibility/category presets (localStorage key portfolio-review-columns-v9), pinned nav views | Per-browser only, not persisted to any database |
Churn Risk | Table column show/hide configuration (useTableConfig key 'churn-overview' in localStorage) | Per browser, not synced. |
Service Expansion | Column visibility persistence, pinned views, CSV export of filtered table | localStorage / client-side only |
Capacity Planning (Role + Company) | AI Influence Log entries (localStorage key signal_ai_capacity_notes_v1) and table column show/hide preferences (useTableConfig) | Per-browser, per-machine; not shared or backed up. |
Platform Foundation (app shell) | Theme (signal-theme), pinned views (signal-pinned-views), sidebar collapsed state, and the in-memory Growth unlock key (never persisted; relocks after 5 min backgrounded) | Per-device only; Growth key also cleared on reload/sign-out |
Views in Signal that write into Browser-local storage:
View | What it writes |
Staffing | Simulation scenario (client reassignments and allocation-% overrides) saved to the user's own localStorage (key signal-staffing-scenario:, debounced 200 ms); Reset all / per-card revert delete entries; Download CSV writes a local file signal-staffing-YYYY-MM-DD.csv. No writes to Vena, Nova, or Signal's shared records — the view is read-only toward every shared system |
ETCR (Beta) | sessionStorage key etcr.showDetails ('1'/'0') persisting the forecast-inputs drawer state for the tab session. |
Portfolio Review | Column config persisted under localStorage key portfolio-review-columns-v9 |
Churn Risk | Column visibility preferences in localStorage |
Capacity Planning (Role + Company) | Create/delete AI Influence Log entries (division, solution name, notes, plus a frozen snapshot of that division's At/Above %, headcount, and revenue gap) in localStorage key signal_ai_capacity_notes_v1 |
Capacity Planning (Role + Company) | Table column visibility preferences on Role Capacity tables via useTableConfig (cosmetic) |
Platform Foundation (app shell) | Theme, pinned views, sidebar collapsed (localStorage); Growth unlock key in memory only |
Views
One row per Signal view (Power Digital's internal BI web app, staging @ 98df0c4, 2026-07-07). "Source systems" lists core-origin systems only — warehouse/replica/cache layers are transport and intentionally omitted.
View | Route | Access | Source systems | What can be edited |
Summary | /summary | All signed-in users (base-tier Members get a reduced read-only experience: tiles + trend chart only) | Vena; NetSuite; Nova platform; Signal's own records; Browser-local storage | Flag a data issue on any drill-down row (creates a pending correction in Signal's records) |
Employees | /employees | All signed-in users (Members see only their own row; full roster for Team Lead+; extra affordances for Admin+) | Nova platform; Vena; Signal's own records; Payroll (ADP / Deel / Siesa); KPI Targets Google Sheet; JD Map Google Sheet; Asana; Browser-local storage | Admin-only Mark On Leave flag; everything else read-only (allocation edits happen in Manage Leads) |
Staffing | /staffing | Team Lead and above (any non-Member; base-tier Members redirected to /summary) | Vena; Nova platform; Signal's own records; Browser-local storage | Nothing shared — what-if scenarios live only in the user's own browser |
Manage Leads | /leads | Team Lead and above (TeamLeadRoute; Admin/Gandalf/Sauron implicitly qualify) | Vena; Nova platform; Signal's own records; Browser-local storage | Team membership, revenue-split %, Consulting roles, managed-clients adoption (Signal records); AD/Sr. AD chips write directly to Nova |
ETCR (Beta) | /etcr | Team Lead and above (any non-Member; base-tier Members redirected to /summary) | ML models (data-science pipeline); Browser-local storage | Nothing — read-only |
Employee Pulse (Pulse Coverage) | /pulse-coverage | Admin and above (AdminRoute: Admin, Gandalf, Sauron) | Nova platform; Payroll (ADP / Deel / Siesa); Signal's own records; Browser-local storage | Nothing — read-only (client-side Excel exports only) |
KPI Targets | /kpi-targets | Admin or Gandalf (AdminRoute; nav link adminOnly) | KPI Targets Google Sheet; Nova platform; Signal's own records | Re-sync Matches (rebuilds roles/match log from the Google Sheet); target numbers are editable only in the sheet |
Submit Pulse Data | /pulse/submit | Team Lead and above (TeamLeadRoute; admins and Gandalf-tier implicitly qualify) | Signal's own records; Nova platform; Vena; ML models (data-science pipeline) | Weekly pulse rows and QBP rows (upserts through the Signal backend, ownership-checked server-side); soft-void of either kind — original submitter or Gandalf admin only, never hard-deleted |
My Submissions (Client Pulse) | /pulse/my-submissions | Team Lead and above (TeamLeadRoute) | Signal's own records; Nova platform; Vena | Nothing — read-only (rows deep-link to Submit Pulse Data) |
Pulse Summary | /predictions/pulse-summary | Admin and above (AdminRoute; server endpoint enforces sign-in only) | Signal's own records; ML models (data-science pipeline); Nova platform; Vena | Nothing — read-only (CSV export only) |
Portfolio Review | /predictions/portfolio | Any non-Member (MemberAllowedRoute; Team Lead is the typical minimum working tier) | Vena; Nova platform; ML models (data-science pipeline); Gong call analysis; Salesforce; Signal's own records; Asana; Anthropic Claude; Browser-local storage | GD Review Notes + WoW Changes, client/action notes, action triage, Brand Intel generation, Asana task creation/links |
POC Management | /predictions/poc-management | Any non-Member; visibility scoped to the caller's book; edits limited to the client's GD/AD (incl. Sr. AD) or admins | Gong call analysis; Nova platform; Vena; Signal's own records | POC cards: relationship owner, title, LinkedIn, reporting line, last 1:1, context; prospect cards (soft-archivable) |
Churn Risk | /predictions/churn | Team Lead and above (any non-Member; base-tier Members redirected to /summary) | ML models (data-science pipeline); Vena; Nova platform; Salesforce; Gong call analysis; Signal's own records; Anthropic Claude; Asana; Signal usage logs; Browser-local storage | Action triage (done/dismiss), manual follow-ups, action/client notes, AI Summary + Iris Draft regeneration, Brand Intel, Asana tasks |
Division Forecast | /predictions/retention | Team Lead and above (any non-Member; frontend guard only — the API endpoint has no server-side auth middleware) | CSM Renewal Tracker Google Sheet; Nova platform | Nothing — read-only (mirror of the CSM renewal tracker sheet) |
Service Expansion | /predictions/se | Team Lead and above (any non-Member; base-tier Members redirected to /summary) | ML models (data-science pipeline); Salesforce; Gong call analysis; Vena; Nova platform; Anthropic Claude; Signal's own records; Asana; Signal usage logs; Browser-local storage | Dismiss/reactivate clients, action triage, follow-ups, notes, favorites, AI Summary/Roadmap/Brand Intel generation, Asana tasks |
Client Profitability | /profitability | Admin and above (AdminRoute: Admin or Gandalf; nav link adminOnly) | Nova platform; Vena; Signal's own records | Nothing — read-only (client-side Excel export only; passive page-view telemetry) |
Divisional P&Ls | /divisional-pl | Gandalf tier (GandalfRoute); payroll data additionally gated by a server-side email allowlist (PAYROLL_AUTHORIZED_EMAILS) | Vena; Payroll (ADP / Deel / Siesa); NetSuite; Nova platform; Signal's own records | Division forecast cells (10-metric grid, forecast months only) |
Revenue Overlap | /revenue-overlap | Team Lead and above (TeamLeadRoute) | Vena; Nova platform; Signal's own records | Nothing — read-only |
Capacity Planning (Role + Company) | /capacity, /company-capacity | Admin and above (AdminRoute: Admin or Gandalf) | Vena; Nova platform; Payroll (ADP / Deel / Siesa); KPI Targets Google Sheet; Signal's own records; Anthropic Claude; Browser-local storage | AI Influence Log entries and column preferences (browser-local only); Capacity Insights sends a snapshot to Claude (response never persisted) |
Growth (People Dash + Performance Radar) | /growth/people, /growth/performance-radar | Per-tab Growth capabilities (can_view_people / can_view_radar) + division/department scope grant + shared unlock password per tab | Payroll (ADP / Deel / Siesa); Nova platform; NetSuite; Vena; Signal's own records | Performance judgments (rating, retention risk, backfill, cannot-lose, radar status, WW dates, notes); backfill push to the Hiring sheet; access grants via /growth/access |
Growth Access management | /growth/access | canManageGrowthAccess capability (delegated managers or Sauron) + unlock password on every management request | Signal's own records; Nova platform | Growth access grants (division/department scope) and per-user capabilities (People / Radar / Manage) |
AI Solutions | /solutions, /solutions/:id | Gandalf only (GandalfRoute; nav link gandalfOnly) | Vena; ML models (data-science pipeline); Gong call analysis; Signal's own records; Nova platform | Solution registry: create/delete solutions, status, notes, manual snapshots, baseline regeneration |
Corrections | /corrections | Admin and above (AdminRoute; Gandalf-but-not-Admin sees a read-only queue) | Signal's own records | Flag triage (pending → reviewed → resolved), reviewer notes, delete pending flags; new flags via the same modal |
Admin Tools (General Access, Assignment History, Hiring Pipeline, JD Sheet Sync, Analytics) | /users, /audit-log, /hiring, /jd-sync, /analytics | Gandalf for /users, /audit-log, /hiring, /jd-sync; Sauron only for /analytics | Signal's own records; Hiring Pipeline Google Sheet; JD Map Google Sheet; Vena; Nova platform; ML models (data-science pipeline); Signal usage logs | User tier toggles (app_users); hiring approvals (written back to the hiring Google Sheet); JD sheet resync (in-memory) |
Platform Foundation (app shell) | cross-cutting (all routes; / redirects to /summary) | All tiers — documents the access model itself | Vena; Nova platform; Signal's own records; Anthropic Claude; KPI Targets Google Sheet; Browser-local storage; Signal usage logs | My Accounts favorites; theme/pinned views (browser); auto-provisioning + telemetry are automatic system writes |
