{
"generated": "2026-07-07",
"commit": "98df0c4",
"coreSystems": [
{
"key": "vena",
"name": "Vena",
"kind": "read",
"plain": "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"
},
{
"key": "netsuite",
"name": "NetSuite",
"kind": "read",
"plain": "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"
},
{
"key": "nova",
"name": "Nova platform",
"kind": "write",
"plain": "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"
},
{
"key": "payroll",
"name": "Payroll (ADP / Deel / Siesa)",
"kind": "read",
"plain": "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"
},
{
"key": "sheets-kpi",
"name": "KPI Targets Google Sheet",
"kind": "read",
"plain": "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"
},
{
"key": "sheets-jd",
"name": "JD Map Google Sheet",
"kind": "read",
"plain": "A Google Sheet mapping employee names/titles to job-description Google Docs, read via a service account.",
"owner": "Ops / People team"
},
{
"key": "sheets-renewal",
"name": "CSM Renewal Tracker Google Sheet",
"kind": "read",
"plain": "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"
},
{
"key": "sheets-hiring",
"name": "Hiring Pipeline Google Sheet",
"kind": "write",
"plain": "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"
},
{
"key": "salesforce",
"name": "Salesforce",
"kind": "read",
"plain": "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"
},
{
"key": "gong",
"name": "Gong call analysis",
"kind": "read",
"plain": "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)"
},
{
"key": "ml",
"name": "ML models (data-science pipeline)",
"kind": "derived",
"plain": "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"
},
{
"key": "signal",
"name": "Signal's own records",
"kind": "own",
"plain": "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"
},
{
"key": "claude",
"name": "Anthropic Claude",
"kind": "derived",
"plain": "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)"
},
{
"key": "asana",
"name": "Asana",
"kind": "write",
"plain": "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"
},
{
"key": "usage",
"name": "Signal usage logs",
"kind": "own",
"plain": "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"
},
{
"key": "browser-local",
"name": "Browser-local storage",
"kind": "own",
"plain": "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"
}
],
"modules": [
{
"slug": "summary",
"name": "Summary",
"route": "/summary",
"tier": "All signed-in users (base-tier Members get a reduced read-only experience: tiles + trend chart only)",
"sources": [
{
"system": "vena",
"provides": "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",
"notes": "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"
},
{
"system": "netsuite",
"provides": "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",
"notes": "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"
},
{
"system": "nova",
"provides": "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",
"notes": "1-hour server caches on roster/headcount/client metadata; retention is point-in-time from churn_date vs month-end"
},
{
"system": "signal",
"provides": "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",
"notes": "Render-disk assignment backups serve as a Supabase-outage fallback for the override pipeline"
},
{
"system": "browser-local",
"provides": "Pinned-views list defaulting to /summary (localStorage key signal-pinned-views) and session-shared division/department filter state",
"notes": "View state only, no business data"
}
],
"writes": [
{
"system": "signal",
"what": "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"
},
{
"system": "usage",
"what": "Automatic page_view usage event with module 'summary' into signal_usage_events (src/hooks/useUsageTracking.ts), feeding the Sauron-only Analytics view"
}
]
},
{
"slug": "employees",
"name": "Employees",
"route": "/employees",
"tier": "All signed-in users (Members see only their own row; full roster for Team Lead+; extra affordances for Admin+)",
"sources": [
{
"system": "nova",
"provides": "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",
"notes": "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"
},
{
"system": "vena",
"provides": "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",
"notes": "Via getAssignmentsData() in server/vena-kpi-engine.ts; per-year master cache"
},
{
"system": "signal",
"provides": "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",
"notes": "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"
},
{
"system": "payroll",
"provides": "Employment type enrichment (Full Time / Part Time / Apprentice) from ADP workerType, name-matched onto the Nova roster",
"notes": "ADP, not Deel, on this view; best-effort name matching, null on mismatch/outage"
},
{
"system": "sheets-kpi",
"provides": "Role-based KPI targets (RPE/MRR/retention per job title) — Google Sheet → Supabase signal_roles → in-memory cachedTitleTargets served at /api/employee-kpi-targets",
"notes": "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"
},
{
"system": "sheets-jd",
"provides": "Job-description doc links per person/title (live JD map sheet over static server/jd-data.ts snapshot) via /api/jd-links",
"notes": "Pulled at startup + Gandalf-only POST /api/jd-sheet/sync; name match first, then title"
},
{
"system": "asana",
"provides": "External intake form for title/dept/division change requests (hardcoded URL, link-out only — no data read)",
"notes": "SYSTEM_CHANGE_BOARD_URL in EmployeeView.tsx"
},
{
"system": "browser-local",
"provides": "Column show/hide preferences (useTableConfig 'employee_list' / 'kpi_rollup'), pinned nav views, CSV exports (employees.csv, kpi_rollup.csv)",
"notes": "localStorage only; no shared state"
}
],
"writes": [
{
"system": "signal",
"what": "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)"
}
]
},
{
"slug": "staffing",
"name": "Staffing",
"route": "/staffing",
"tier": "Team Lead and above (any non-Member; base-tier Members redirected to /summary)",
"sources": [
{
"system": "vena",
"provides": "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",
"notes": "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"
},
{
"system": "nova",
"provides": "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",
"notes": "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)"
},
{
"system": "signal",
"provides": "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",
"notes": "Overrides loaded for the first selected month only"
},
{
"system": "browser-local",
"provides": "The simulation scenario itself (reassignments + allocation-% overrides), persisted to localStorage key signal-staffing-scenario:<userId>, auto-saved 200 ms after each change; CSV export generated client-side",
"notes": "Per user, per browser; never leaves the machine; the only 'write' surface of the module"
}
],
"writes": [
{
"system": "browser-local",
"what": "Simulation scenario (client reassignments and allocation-% overrides) saved to the user's own localStorage (key signal-staffing-scenario:<userId>, 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"
}
]
},
{
"slug": "manage-leads",
"name": "Manage Leads",
"route": "/leads",
"tier": "Team Lead and above (TeamLeadRoute; Admin/Gandalf/Sauron implicitly qualify)",
"sources": [
{
"system": "vena",
"provides": "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",
"notes": "Vena writes, Signal reads only; 4-hour default in-memory server cache; VABO rows shown but excluded from seat/revenue rollups"
},
{
"system": "nova",
"provides": "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)",
"notes": "Since PR #97 live Nova wins over Signal's cached AD/SrAD rows for non-consulting single-division clients"
},
{
"system": "signal",
"provides": "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",
"notes": "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"
},
{
"system": "browser-local",
"provides": "Draft percentage-override edits and optimistic UI state before Save",
"notes": "Nothing persists locally beyond the session"
}
],
"writes": [
{
"system": "signal",
"what": "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)"
},
{
"system": "nova",
"what": "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'"
},
{
"system": "signal",
"what": "Fire-and-forget assignment backup on every team save (POST /api/assignments/backup → server-side store on Render disk)"
}
]
},
{
"slug": "etcr",
"name": "ETCR (Beta)",
"route": "/etcr",
"tier": "Team Lead and above (any non-Member; base-tier Members redirected to /summary)",
"sources": [
{
"system": "ml",
"provides": "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)",
"notes": "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."
},
{
"system": "browser-local",
"provides": "'Show forecast inputs' drawer open/closed state (sessionStorage key etcr.showDetails)",
"notes": "Per browser tab session; never sent to any server."
}
],
"writes": [
{
"system": "usage",
"what": "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."
},
{
"system": "browser-local",
"what": "sessionStorage key etcr.showDetails ('1'/'0') persisting the forecast-inputs drawer state for the tab session."
}
]
},
{
"slug": "employee-pulse",
"name": "Employee Pulse (Pulse Coverage)",
"route": "/pulse-coverage",
"tier": "Admin and above (AdminRoute: Admin, Gandalf, Sauron)",
"sources": [
{
"system": "nova",
"provides": "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",
"notes": "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."
},
{
"system": "nova",
"provides": "Employee names, divisions, titles, and coarse fallback departments via the shared GET /api/employees roster consumed through DataContext",
"notes": "Server-cached 1 hour; view prefers the finer Nova department from the pulse-coverage roster over this one."
},
{
"system": "payroll",
"provides": "ADP employment-type enrichment on the shared /api/employees roster",
"notes": "Not displayed anywhere on the Employee Pulse view; incidental to the shared roster fetch."
},
{
"system": "signal",
"provides": "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",
"notes": "No on-screen data comes from Signal's own records."
},
{
"system": "browser-local",
"provides": "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",
"notes": "Reflects active filters at export time."
}
],
"writes": []
},
{
"slug": "kpi-targets",
"name": "KPI Targets",
"route": "/kpi-targets",
"tier": "Admin or Gandalf (AdminRoute; nav link adminOnly)",
"sources": [
{
"system": "sheets-kpi",
"provides": "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)",
"notes": "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."
},
{
"system": "nova",
"provides": "Active employee roster and job titles used for title→role matching, the sync employee total, and (via /api/employees + DataContext) the coverage-bar population",
"notes": "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."
},
{
"system": "signal",
"provides": "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)",
"notes": "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."
},
{
"system": "signal",
"provides": "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",
"notes": "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."
}
],
"writes": [
{
"system": "signal",
"what": "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."
}
]
},
{
"slug": "pulse-submit",
"name": "Submit Pulse Data",
"route": "/pulse/submit",
"tier": "Team Lead and above (TeamLeadRoute; admins and Gandalf-tier implicitly qualify)",
"sources": [
{
"system": "signal",
"provides": "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",
"notes": "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"
},
{
"system": "nova",
"provides": "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",
"notes": "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"
},
{
"system": "vena",
"provides": "Account list behind the client cards (pass 1 from DataContext accounts) plus current-month MGP and latest VABO feeding the pulse-eligibility rule",
"notes": "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"
},
{
"system": "ml",
"provides": "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",
"notes": "isPulseEligible() in src/utils/pulseEligibility.ts, mirrored in server/pulseEligibility.ts with test-enforced parity"
}
],
"writes": [
{
"system": "signal",
"what": "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)"
},
{
"system": "signal",
"what": "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)"
},
{
"system": "signal",
"what": "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"
}
]
},
{
"slug": "pulse-my-submissions",
"name": "My Submissions (Client Pulse)",
"route": "/pulse/my-submissions",
"tier": "Team Lead and above (TeamLeadRoute)",
"sources": [
{
"system": "signal",
"provides": "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",
"notes": "Includes voided rows; tab counters count voided; division column stored but not displayed"
},
{
"system": "nova",
"provides": "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)",
"notes": "Page errors with 'account not linked to a Nova user' if the login email is absent from Nova's user directory"
},
{
"system": "vena",
"provides": "Client display names in the Client column — accounts built from Vena contract/revenue records keyed by Nova client id (parentName/name from DataContext)",
"notes": "Unmatched client ids display as raw numeric ids"
}
],
"writes": []
},
{
"slug": "pulse-summary",
"name": "Pulse Summary",
"route": "/predictions/pulse-summary",
"tier": "Admin and above (AdminRoute; server endpoint enforces sign-in only)",
"sources": [
{
"system": "signal",
"provides": "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",
"notes": "Entered by Account Directors on Submit Pulse; live, upserted per (client, week); voided submissions excluded"
},
{
"system": "ml",
"provides": "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)",
"notes": "AI/ML pipeline tables; refresh cadence not defined in the app repo; clients missing from FINAL_FEATURES get blank division and Unknown SE/churn"
},
{
"system": "nova",
"provides": "Client master records (id, name, churn date) from ENTERPRISE.RDS_PG_NOVA.CLIENTS that define the candidate universe for the pulse-eligible active book",
"notes": "Filtered by pulseEligibility; server-cached 15 min in activeBook.ts; same universe as Submit Pulse and Portfolio Review"
},
{
"system": "vena",
"provides": "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)",
"notes": "Department filter is revenue-derived (positive revenue in latest loaded month, VABO excluded) and does not apply to the By Division table"
}
],
"writes": [
{
"system": "usage",
"what": "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)"
}
]
},
{
"slug": "portfolio-review",
"name": "Portfolio Review",
"route": "/predictions/portfolio",
"tier": "Any non-Member (MemberAllowedRoute; Team Lead is the typical minimum working tier)",
"sources": [
{
"system": "vena",
"provides": "MGP, Dept MGP, MRR, Last Month VABO, VABO-2mo ('VABO only' badge), departments column and department revenue breakdown (FINAL_VENA_CLIENTS / FINAL_VENA_CONTRACTS)",
"notes": "UI tooltips explicitly name Vena as source; 15-min server cache; MGP can be $0 before Finance posts entries"
},
{
"system": "nova",
"provides": "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)",
"notes": "Nova data reaches the app via warehouse replica; sync is freshness-guarded, 1-hour TTL"
},
{
"system": "ml",
"provides": "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)",
"notes": "Priority rules and refresh cadence (~weekly) owned by data team; missing tier defaults to P3; portfolio uses raw SE_SCORES readiness"
},
{
"system": "gong",
"provides": "Gong Sentiment column, gong activity status, POC Contacts tab (FINAL_POC_SENTIMENT per-contact satisfaction/engagement/mood/influence)",
"notes": "Gong score of exactly 0.5 is a no-data convention and renders as '—'"
},
{
"system": "salesforce",
"provides": "Salesforce history section inside the embedded SE detail tab (FINAL_SALESFORCE_HISTORY); contract wins/losses 3m likely opportunity-derived",
"notes": "Won/lost-contracts upstream origin not definitively stated in repo"
},
{
"system": "signal",
"provides": "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)",
"notes": "Supabase = Signal's own records entered by users in Signal"
},
{
"system": "asana",
"provides": "Asana Tasks card in expanded row (live task list via Asana API)",
"notes": "Task↔client links stored in Signal's asana_links"
},
{
"system": "claude",
"provides": "Brand Intel tab — on-demand macro/brand analysis via claude-sonnet-4-5 with web search",
"notes": "~$0.50/fresh generation; cached per client per month; 50/user/day cap (env-tunable)"
},
{
"system": "browser-local",
"provides": "Column visibility/category presets (localStorage key portfolio-review-columns-v9), pinned nav views",
"notes": "Per-browser only, not persisted to any database"
}
],
"writes": [
{
"system": "signal",
"what": "GD Review Notes + WoW Changes saved to pulse_weekly_submissions via POST /api/pulse/weekly (ownership-gated: assigned AD/GD or Pulse admin only)"
},
{
"system": "signal",
"what": "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"
},
{
"system": "signal",
"what": "Brand Intel results persisted to macro_brand_insights (one per client per month); Asana task links to asana_links"
},
{
"system": "asana",
"what": "Real task creation via POST /api/asana/tasks from the '+ New task' modal"
},
{
"system": "usage",
"what": "signal_usage_events: page views, client row expands, briefing-strip clicks (direct browser writes; feeds Analytics/Sauron)"
},
{
"system": "signal",
"what": "Side effect on page load: Nova→Signal AD sync writes client_account_directors + team_assignments (freshness-guarded, 1-hour TTL)"
},
{
"system": "browser-local",
"what": "Column config persisted under localStorage key portfolio-review-columns-v9"
}
]
},
{
"slug": "poc-management",
"name": "POC Management",
"route": "/predictions/poc-management",
"tier": "Any non-Member; visibility scoped to the caller's book; edits limited to the client's GD/AD (incl. Sr. AD) or admins",
"sources": [
{
"system": "gong",
"provides": "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)",
"notes": "Read via fetchPocContactsByClient in server/predictions.ts; 15-min server cache; is_decision_maker stubbed false pending upstream column"
},
{
"system": "nova",
"provides": "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",
"notes": "GD/AD chips read-only here; AD chip = manual Supabase pick with Nova exec-sponsor fallback"
},
{
"system": "vena",
"provides": "Revenue-active client gating (active book: current-month contract MGP > 0 or recent VABO) that determines which clients appear at all",
"notes": "server/activeBook.ts, 15-min cache; same base as Portfolio Review"
},
{
"system": "signal",
"provides": "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",
"notes": "Cards read directly from the browser via open-read RLS (listPocCards); all writes via gated backend"
}
],
"writes": [
{
"system": "signal",
"what": "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."
}
]
},
{
"slug": "churn-risk",
"name": "Churn Risk",
"route": "/predictions/churn",
"tier": "Team Lead and above (any non-Member; base-tier Members redirected to /summary)",
"sources": [
{
"system": "ml",
"provides": "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",
"notes": "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."
},
{
"system": "vena",
"provides": "MGP, Current MRR, Last Month VABO columns and the current-month Department filter values",
"notes": "Finance actuals; MGP can be $0 for new contracts before Finance posts."
},
{
"system": "nova",
"provides": "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",
"notes": "Contract facts reach Signal via the model feature table."
},
{
"system": "salesforce",
"provides": "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",
"notes": "Joined into the model score table upstream."
},
{
"system": "gong",
"provides": "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)",
"notes": "is_decision_maker hardcoded false pending upstream column."
},
{
"system": "signal",
"provides": "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)",
"notes": "Signal's own records = entered by users in Signal (Supabase transport)."
},
{
"system": "claude",
"provides": "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",
"notes": "All generated server-side and persisted to Signal's own records."
},
{
"system": "asana",
"provides": "Live task status in the Asana Tasks card; task creation from recommended actions; webhook sync back into asana_links",
"notes": "Shows a not-configured state if server lacks the Asana token."
},
{
"system": "usage",
"provides": "Invisible telemetry: page_view (module 'churn') and client_detail_view events into signal_usage_events; app-wide freeze/longtask perf events added Jul 7",
"notes": "Feeds adoption reporting."
},
{
"system": "browser-local",
"provides": "Table column show/hide configuration (useTableConfig key 'churn-overview' in localStorage)",
"notes": "Per browser, not synced."
}
],
"writes": [
{
"system": "signal",
"what": "Recommended-action Done/Dismiss/Undo events (recommended_action_feedback, drives status chips)"
},
{
"system": "signal",
"what": "Manual follow-ups create/edit/archive (manual_recommended_actions)"
},
{
"system": "signal",
"what": "Per-action threaded notes (action_notes)"
},
{
"system": "signal",
"what": "Client-level notes (client_notes)"
},
{
"system": "signal",
"what": "AI Summary regenerate and Iris Draft generate — overwrite/insert weekly rows in client_summaries (types churn / churn_journey)"
},
{
"system": "signal",
"what": "Brand Intel generation results inserted into macro_brand_insights (per-user daily cap, default 50)"
},
{
"system": "signal",
"what": "Asana task↔client link rows (asana_links), archivable from the card"
},
{
"system": "asana",
"what": "Real Asana task creation via server bot token, pre-filled from the recommended action"
},
{
"system": "usage",
"what": "Invisible usage events (signal_usage_events, module 'churn': page_view, client_detail_view)"
},
{
"system": "browser-local",
"what": "Column visibility preferences in localStorage"
}
]
},
{
"slug": "division-forecast",
"name": "Division Forecast",
"route": "/predictions/retention",
"tier": "Team Lead and above (any non-Member; frontend guard only — the API endpoint has no server-side auth middleware)",
"sources": [
{
"system": "sheets-renewal",
"provides": "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",
"notes": "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."
},
{
"system": "nova",
"provides": "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",
"notes": "Role flags themselves are stored in Signal's own records (app_users)."
}
],
"writes": []
},
{
"slug": "service-expansion",
"name": "Service Expansion",
"route": "/predictions/se",
"tier": "Team Lead and above (any non-Member; base-tier Members redirected to /summary)",
"sources": [
{
"system": "ml",
"provides": "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)",
"notes": "Weekly data-science pipeline; Signal merges/labels/serves with a 15-min server cache; table score overridden with latest weekly historical snapshot"
},
{
"system": "salesforce",
"provides": "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",
"notes": "Carried through the weekly pipeline as JSON columns; history via GET /api/predictions/se/salesforce-history/:clientId"
},
{
"system": "gong",
"provides": "POC contact sentiment (satisfaction, engagement, mood, churn risk, call rates; 5-week rolling), Gong sentiment score feeding the Sentiment composite",
"notes": "is_decision_maker hardcoded false pending upstream column"
},
{
"system": "vena",
"provides": "Current-month monthly GP, MRR, VABO figures in the client detail",
"notes": "Via the pipeline's Vena clients table; shared 15-min cached fetch with Churn/Portfolio"
},
{
"system": "nova",
"provides": "Client attributes (tier, division, industry, group/account director) via the pipeline's client-features table",
"notes": "SE view reads them from the ML feature table, not live Nova"
},
{
"system": "claude",
"provides": "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)",
"notes": "All persisted in Signal's own records (client_summaries, macro_brand_insights); Brand Intel per-user daily cap default 50"
},
{
"system": "signal",
"provides": "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",
"notes": "Supabase = Signal's own records entered by users; never holds scores"
},
{
"system": "asana",
"provides": "Live Asana tasks card, task creation, webhook + 60s pull sync",
"notes": "Links stored in asana_links"
},
{
"system": "usage",
"provides": "signal_usage_events telemetry: page opens, client_detail_view, roadmap generated/copied, continue-on-Iris clicks",
"notes": "Powers adoption reporting"
},
{
"system": "browser-local",
"provides": "Column visibility persistence, pinned views, CSV export of filtered table",
"notes": "localStorage / client-side only"
}
],
"writes": [
{
"system": "signal",
"what": "se_client_feedback (dismiss/reactivate with reason, score-at-dismissal, resurface threshold; auto-resurface at ≥0.15 score move) + change_history audit rows"
},
{
"system": "signal",
"what": "client_summaries upserts for type=se (AI Summary regenerate/lazy-generate) and type=se_roadmap (roadmap generate/regenerate), attributed to user email"
},
{
"system": "signal",
"what": "macro_brand_insights (Brand Intel generation, one row per client per month, attributed; counts against per-user daily cap)"
},
{
"system": "signal",
"what": "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)"
},
{
"system": "asana",
"what": "Creates real tasks in Asana via API; Signal↔Asana link rows stored in asana_links; status syncs back via webhook + 60s pull"
}
]
},
{
"slug": "client-profitability",
"name": "Client Profitability",
"route": "/profitability",
"tier": "Admin and above (AdminRoute: Admin or Gandalf; nav link adminOnly within the Client Health section)",
"sources": [
{
"system": "nova",
"provides": "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",
"notes": "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)"
},
{
"system": "vena",
"provides": "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",
"notes": "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"
},
{
"system": "signal",
"provides": "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)",
"notes": "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"
}
],
"writes": [
{
"system": "usage",
"what": "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"
}
]
},
{
"slug": "divisional-pl",
"name": "Divisional P&Ls",
"route": "/divisional-pl",
"tier": "Gandalf tier (GandalfRoute); payroll data additionally gated by a server-side email allowlist (PAYROLL_AUTHORIZED_EMAILS)",
"sources": [
{
"system": "vena",
"provides": "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",
"notes": "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"
},
{
"system": "payroll",
"provides": "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",
"notes": "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"
},
{
"system": "netsuite",
"provides": "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",
"notes": "Read from FINAL_NETSUITE_ACCOUNT_COSTS / FINAL_NETSUITE_CONTRACTOR_PAYMENTS models in server/payroll-actuals.ts"
},
{
"system": "nova",
"provides": "Employee names, titles, division/department assignment, termination dates for payroll matching; client display names for the Client Roster",
"notes": "ENTERPRISE.RDS_PG_NOVA.USERS/CLIENTS queries; nova↔payroll match cache populated by scripts/sync-payroll-actuals.ts"
},
{
"system": "signal",
"provides": "division_forecasts table (ten-metric forecast grid feeding Team Forecast P&L rows) and app_users.is_gandalf access flag",
"notes": "Supabase; GET /api/division-forecast open read, PUT requires auth only (not Gandalf)"
}
],
"writes": [
{
"system": "signal",
"what": "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"
}
]
},
{
"slug": "revenue-overlap",
"name": "Revenue Overlap",
"route": "/revenue-overlap",
"tier": "Team Lead and above (TeamLeadRoute)",
"sources": [
{
"system": "vena",
"provides": "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)",
"notes": "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"
},
{
"system": "nova",
"provides": "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)",
"notes": "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"
},
{
"system": "signal",
"provides": "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",
"notes": "Merged client-side in DataContext via buildManageLeadsDisplayAccounts; revenue_split_overrides exist in Signal's records but are IGNORED by this view"
}
],
"writes": [
{
"system": "usage",
"what": "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."
}
]
},
{
"slug": "capacity-planning",
"name": "Capacity Planning (Role + Company)",
"route": "/capacity, /company-capacity",
"tier": "Admin and above (AdminRoute: Admin or Gandalf)",
"sources": [
{
"system": "vena",
"provides": "Contract revenue per client/department/month — drives RPE, MRR, Revenue Gap, RPE Trend chart, and the available-months list (capped at current calendar month)",
"notes": "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."
},
{
"system": "nova",
"provides": "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",
"notes": "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)."
},
{
"system": "payroll",
"provides": "ADP employment-type enrichment (Full Time/Part Time) attached to each roster row inside GET /api/employees",
"notes": "Name-matched; null when ADP unreachable. Rides on employee objects; not displayed on capacity tables. Origin is ADP, not Deel."
},
{
"system": "sheets-kpi",
"provides": "Official KPI targets per job title (RPE/MRR/retention) — the basis of Official Target, % to Target, and Above/At/Below classification",
"notes": "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'."
},
{
"system": "signal",
"provides": "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",
"notes": "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."
},
{
"system": "claude",
"provides": "Capacity Insights — on-demand 3-5 prioritized AI-deployment recommendations plus follow-up chat over a text snapshot of the on-screen capacity numbers",
"notes": "POST /api/claude proxy, default model claude-sonnet-4-6, last 20 messages kept; no logContext passed, so NOT recorded in llm_generation_logs."
},
{
"system": "browser-local",
"provides": "AI Influence Log entries (localStorage key signal_ai_capacity_notes_v1) and table column show/hide preferences (useTableConfig)",
"notes": "Per-browser, per-machine; not shared or backed up."
}
],
"writes": [
{
"system": "browser-local",
"what": "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"
},
{
"system": "browser-local",
"what": "Table column visibility preferences on Role Capacity tables via useTableConfig (cosmetic)"
},
{
"system": "claude",
"what": "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"
},
{
"system": "usage",
"what": "Automatic page_view usage event (module 'capacity' or 'company-capacity') logged to Signal's signal_usage_events table on each visit via the app-wide usePageViewTracking hook (src/hooks/useUsageTracking.ts); fire-and-forget telemetry feeding the Analytics view, not user-initiated"
}
]
},
{
"slug": "growth",
"name": "Growth (People Dash + Performance Radar)",
"route": "/growth/people, /growth/performance-radar",
"tier": "Per-tab Growth capabilities (can_view_people / can_view_radar) + division/department scope grant + shared unlock password per tab",
"sources": [
{
"system": "payroll",
"provides": "Annual salary, hire date, worker type/category, country, seniority, contract status, DOB, raise history, YTD commission/bonus/total comp, compensation change history",
"notes": "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."
},
{
"system": "nova",
"provides": "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",
"notes": "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."
},
{
"system": "netsuite",
"provides": "Independent Contractor classification pill + division/department overrides for ~22 people",
"notes": "Hardcoded mirror of JT's NetSuite IC List in server/payroll-overrides.ts — manual code refresh, not a live integration."
},
{
"system": "vena",
"provides": "RPE (revenue per employee) and # Accounts",
"notes": "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."
},
{
"system": "signal",
"provides": "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",
"notes": "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."
}
],
"writes": [
{
"system": "signal",
"what": "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"
},
{
"system": "signal",
"what": "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"
},
{
"system": "sheets-hiring",
"what": "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)"
}
]
},
{
"slug": "growth-access",
"name": "Growth Access management",
"route": "/growth/access",
"tier": "canManageGrowthAccess capability (delegated managers or Sauron) + unlock password on every management request",
"sources": [
{
"system": "signal",
"provides": "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.",
"notes": "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."
},
{
"system": "nova",
"provides": "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.",
"notes": "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."
}
],
"writes": [
{
"system": "signal",
"what": "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."
},
{
"system": "signal",
"what": "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."
}
]
},
{
"slug": "ai-solutions",
"name": "AI Solutions",
"route": "/solutions, /solutions/:id",
"tier": "Gandalf only (GandalfRoute; nav link gandalfOnly)",
"sources": [
{
"system": "vena",
"provides": "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",
"notes": "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"
},
{
"system": "ml",
"provides": "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",
"notes": "Same churn/SE model feeds and ~15-min server cache as the Churn Risk and Service Expansion views; server/aiSolutionHealth.ts + server/predictions.ts"
},
{
"system": "gong",
"provides": "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')",
"notes": "Only clients with a real sentiment source count toward avgSentimentRisk; model defaults excluded; sentimentAsOf = latest model-run week shown in UI"
},
{
"system": "signal",
"provides": "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",
"notes": "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"
},
{
"system": "nova",
"provides": "Employee roster in the single/multi-employee target pickers (names + departments, active company-email staff only)",
"notes": "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"
}
],
"writes": [
{
"system": "signal",
"what": "Create solution → insert into ai_solutions + immediate baseline row into ai_solution_snapshots (browser-computed metrics + frozen health)"
},
{
"system": "signal",
"what": "Delete solution (confirm dialog) → removes ai_solutions row and all its snapshots"
},
{
"system": "signal",
"what": "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"
},
{
"system": "signal",
"what": "Edit notes → updates ai_solutions.notes"
},
{
"system": "signal",
"what": "Take Snapshot → inserts a 'manual' row into ai_solution_snapshots (latest-month metrics + current health)"
},
{
"system": "signal",
"what": "Regenerate Baselines → patches metrics/snapshot_month/capture_meta on baseline snapshots that captured empty data"
},
{
"system": "signal",
"what": "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)"
}
]
},
{
"slug": "corrections",
"name": "Corrections",
"route": "/corrections",
"tier": "Admin and above (AdminRoute; Gandalf-but-not-Admin sees a read-only queue)",
"sources": [
{
"system": "signal",
"provides": "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)",
"notes": "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."
}
],
"writes": [
{
"system": "signal",
"what": "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"
},
{
"system": "signal",
"what": "Update flag status (pending→reviewed/resolved, reviewed→resolved) with reviewed_by email, optional reviewer_notes, and resolved_at timestamp — admin-only in UI"
},
{
"system": "signal",
"what": "Delete a flag — admin-only in UI, restricted to pending rows by database policy (20260506 migration); reviewed/resolved rows immutable for audit trail"
}
]
},
{
"slug": "admin-tools",
"name": "Admin Tools (General Access, Assignment History, Hiring Pipeline, JD Sheet Sync, Analytics)",
"route": "/users, /audit-log, /hiring, /jd-sync, /analytics",
"tier": "Gandalf for /users, /audit-log, /hiring, /jd-sync; Sauron only for /analytics",
"sources": [
{
"system": "signal",
"provides": "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",
"notes": "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"
},
{
"system": "sheets-hiring",
"provides": "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",
"notes": "15-min server cache (code constant), 'Sync Sheet' bypasses; GET/approve endpoints have no server-side role middleware — Approve is UI-gated to Admin+"
},
{
"system": "sheets-jd",
"provides": "JD Sheet Sync source: Name/Title → JD Google Doc map read via Google service account",
"notes": "Sync result held in server memory only; reverts to static snapshot (server/jd-data.ts) on restart/deploy; POST sync is server-enforced Gandalf"
},
{
"system": "vena",
"provides": "Employee RPE/MRR actuals behind the Hiring Need verdict (via the server KPI engine and DataContext employee metrics)",
"notes": "Thresholds ≥100/85–99/<85 are code constants in HiringPipelineView.tsx"
},
{
"system": "nova",
"provides": "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",
"notes": "fetchEmailDivisions in server/usageEnrichment.ts"
},
{
"system": "ml",
"provides": "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",
"notes": "POST /api/usage/client-status → HISTORICALS CHURN_SCORES/SE_SCORES; card-scoped degradation on failure"
},
{
"system": "usage",
"provides": "App-wide page_view events (usePageViewTracking on every route change) plus feature actions for churn/se/portfolio modules",
"notes": "Fetch capped at 5,000 events / 10,000 taken-actions per range (code constants)"
}
],
"writes": [
{
"system": "signal",
"what": "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)"
},
{
"system": "sheets-hiring",
"what": "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"
},
{
"system": "signal",
"what": "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."
}
]
},
{
"slug": "platform-foundation",
"name": "Platform Foundation (app shell)",
"route": "cross-cutting (all routes; / redirects to /summary)",
"tier": "All tiers — documents the access model itself",
"sources": [
{
"system": "vena",
"provides": "Monthly revenue, outsource cost (COGS), MGP per client×department×month; the available-months calendar for the period selector (future/zero months excluded server-side)",
"notes": "Read-only via server/vena-kpi-engine.ts; server cache with /api/vena/cache/invalidate; 30-min months cache"
},
{
"system": "nova",
"provides": "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/*)",
"notes": "1-hour caches for roster/clients; 5-min cache for Nova assignments; AD/GD emails from Nova auto-grant Team Lead at provisioning"
},
{
"system": "signal",
"provides": "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",
"notes": "Supabase is the store; described as Signal's own records per doc rules. Role checks cached 2–5 min server-side + sessionStorage in browser"
},
{
"system": "claude",
"provides": "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",
"notes": "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"
},
{
"system": "sheets-kpi",
"provides": "Role-based KPI targets synced into Signal's kpi_targets records at server startup (POST /api/kpi-targets/sync)",
"notes": "Drives the Above/At/Below (>105% / 95–105% / <95%) status badges"
},
{
"system": "browser-local",
"provides": "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)",
"notes": "Per-device only; Growth key also cleared on reload/sign-out"
},
{
"system": "usage",
"provides": "page_view events per route change plus freeze/longtask telemetry into signal_usage_events; feeds the Sauron-only Analytics view",
"notes": "Churn/SE/Portfolio views self-instrument and are skipped by the route hook to avoid double counting"
}
],
"writes": [
{
"system": "signal",
"what": "First-sign-in auto-provisioning creates the user's app_users row (Team Lead auto-set for Nova Executive Sponsors/Group Directors); never downgrades"
},
{
"system": "signal",
"what": "My Accounts favorites (user_account_favorites) per user"
},
{
"system": "usage",
"what": "signal_usage_events: page_view per route change + longtask freeze telemetry (fire-and-forget)"
},
{
"system": "signal",
"what": "llm_generation_logs: every Iris and AI-search generation (user email, full prompt, response, tokens, latency)"
},
{
"system": "browser-local",
"what": "Theme, pinned views, sidebar collapsed (localStorage); Growth unlock key in memory only"
},
{
"system": "nova",
"what": "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"
}
]
}
]
}
