Skip to main content

Signal — Growth (People Dash + Performance Radar)

Written by Schae Lilley

The Growth module is Signal's confidential people-and-compensation area. People Dash (/growth/people) is a per-person census that merges payroll (salary, raises, YTD compensation from ADP, Deel, and Siesta), Nova platform signals (pulse sentiment, bandwidth, weekly hours, novaTalent reviews, Culture Index, Vital 5/Hedgehog), and revenue-per-employee — and lets leaders record judgments (Performance rating, Retention Risk, Cannot Lose, Backfill) directly on each row. Performance Radar (/growth/performance-radar) is the working list for active performance-management cases (Active PM → RT / Term, with weekly check-in dates). It answers: "who is underpaid vs their output, who is overloaded or at risk, and where does each performance case stand?" Access is the tightest in Signal: each tab requires (1) a per-tab capability granted in Signal's own access records, (2) a division/department scope grant, and (3) a shared unlock password entered per tab per page-load. Data origins: ADP / Deel / Siesta payroll, the Nova platform, a hardcoded NetSuite independent-contractor list, Vena revenue (for RPE), and Signal's own records (entered by users in Signal).

Field

Value

Route

/growth/people (People Dash), /growth/performance-radar (Performance Radar); access management at /growth/access

Access

Per-tab capabilities from Supabase table growth_access_capabilities (can_view_people, can_view_radar), division/department scope from growth_access_grants, plus a shared unlock password (GROWTH_UNLOCK_KEY) entered per tab. Management screen requires can_manage_access (or Sauron super-admin) plus the same password.

Nav section

"Growth" (shows only if the user can view People or Performance Radar). Access management appears as "Growth Access" under the separate "Access" nav section.

Primary data sources

ADP (US payroll), Deel (international/contractor payroll), Siesta/SIESA (Colombia monthly payroll export), Nova platform (org chart, pulse surveys, talent reviews, Culture Index, Vital 5/Hedgehog), NetSuite IC list (hardcoded mirror), Vena revenue (RPE), Signal's own records (judgments, access grants)

Writes

Supabase employee_performance (ratings, flags, radar workflow), growth_access_grants + growth_access_capabilities (access management), one-time append to the Hiring Pipeline Google Sheet when Backfill = Yes

Key code refs

src/views/growth/PeopleDashView.tsx, src/views/growth/PerformanceRadarView.tsx, src/views/GrowthAccessView.tsx / GrowthAccessPanel.tsx, src/App.tsx (PeopleRoute / RadarRoute / GrowthManageRoute / GrowthTabGate), src/services/growthUnlock.ts, server/index.ts (/api/growth/*, buildGrowthPeople), server/growth-access.ts, server/payroll.ts, server/payroll-overrides.ts

Last verified

staging @ 98df0c4, verified 2026-07-07

Purpose

Power Digital's people data is scattered across four payroll systems (ADP for US employees, Deel for international employees and contractors, Siesta/SIESA for Colombia, NetSuite for a small hardcoded list of independent contractors), the Nova platform (org chart, pulse surveys, talent reviews), and spreadsheets. Before the Growth module, answering "who is underpaid relative to their output?", "who hasn't had a raise in two years?", or "who is overloaded and at risk of leaving?" required stitching exports together by hand — and there was no system of record for leadership's own judgments (performance rating, retention risk, backfill decisions).

The Growth module solves both problems:

  • People Dash (/growth/people) merges payroll, Nova signals, and revenue-per-employee into one sortable census and lets leaders record assessments directly on each row.

  • Performance Radar (/growth/performance-radar) tracks active performance-management cases — who is on a performance plan ("Active PM"), when their weekly check-ins happened (WW1/WW2/WW3 dates), and how the case ended (RT = returned to track, or Term = terminated).

Because both views expose individual salaries, the Growth module is the most tightly access-controlled area of Signal.

Access & visibility

Access to the Growth module has three independent layers, all verified against staging @ 98df0c4 (2026-07-07):

  1. Per-tab capability (who sees which tab). Each user has an optional row in the Supabase table growth_access_capabilities (one row per email; migration 20260703_growth_access_capabilities.sql) with three flags: can_view_people (People Dash tab), can_view_radar (Performance Radar tab), can_manage_access (the Growth Access management screen). The backend resolves these at GET /api/growth/my-access; the frontend route guards PeopleRoute and RadarRoute in src/App.tsx redirect to /summary if the matching capability is missing. If a user has a division grant but no capabilities row, they default to People-visible, Radar-hidden, Manage-hidden (backward-compat rule in resolveCapabilities, server/growth-access.ts). Performance Radar is therefore opt-in per user — a plain division grant does not show it.

  2. Division/department scope (which rows they see). Grants live in the Supabase table growth_access_grants (migrations 20260701 + 20260702): a row with division = NULL means all divisions; otherwise the user sees only people in the listed division(s). For exactly three divisions — Shared Services, G&A, and Healthcare (code constant DEPARTMENT_SCOPED_DIVISIONS in server/growth-access.ts as of Jul 2026) — a grant can be narrowed to specific departments. The backend middleware requireGrowthAccess resolves the caller's scope (cached server-side for 2 minutes) and every Growth data endpoint filters rows, salaries, raises, and performance records to that scope. Capabilities gate only tab visibility; scope gates the data.

  3. Unlock password (second factor on every open). Each viewing tab (People, Performance Radar) prompts for a shared unlock password (GrowthTabGate in src/App.tsx). The password is validated against the backend (GET /api/growth/people with an x-growth-key header) before being stored — in memory only (src/services/growthUnlock.ts), never in localStorage/sessionStorage — so a page reload forces re-entry, and the tabs re-lock after Signal has been backgrounded for 5 minutes or more (code constant BACKGROUND_RELOCK_MS as of Jul 2026). The backend enforces the same key (GROWTH_UNLOCK_KEY env var, checked in requireGrowthAccess) on every /api/growth/* data request, so a locked tab genuinely cannot load data. An unset GROWTH_UNLOCK_KEY disables the check (local dev).

Nav visibility: the "Growth" sidebar section appears only if the user can view People or Performance Radar, and each tab link is gated by its own capability. Users with no Growth capability see no trace of the module.

Access management happens on the standalone "Growth Access" screen at /growth/access, listed under the separate "Access" nav section (next to the Gandalf-only "General Access"). It is open to anyone holding can_manage_access — a delegated permission, no longer restricted to the hardcoded super-admin ("Sauron") list, though Sauron members (John, Jaime, Laura, Lily — code constant SUPER_ADMIN_EMAILS) always retain manage access. The management endpoints (GET/PUT /api/growth/access, middleware requireGrowthManage) also require the unlock password; the management screen stores its copy of the key in sessionStorage (signal_growth_admin_key), unlike the viewing tabs' memory-only key. Managers set, per email: the division/department scope and the three capability checkboxes in one save. Grant updates revoke-before-insert so a partial failure always fails toward less access. Being a super-admin does not by itself grant data access — even a Sauron must have a scope grant to see people.

Data sources & lineage

Both Growth views load the same person feed (GET /api/growth/people) and enrich it with separate calls. "Signal's own records" = data entered by users in Signal (stored in Signal's Supabase database).

On-screen element

Origin system

Code path

Refresh

Caveat

Person roster: name, division, department, job title, Reports To, role start date, external flag

Nova platform (org data)

buildGrowthPeople() in server/index.ts → GET /api/growth/people

3-tier cache: in-memory fresh 15 min, served up to 24 h stale while a background refresh runs; a persisted snapshot (growth_people_snapshot) survives server restarts

Reports To comes from Nova manager links (~86% coverage per code comment); rows can be up to ~24 h old by design

Payroll identity & pay: Annual Salary, hire date, worker type (Employee/Contractor/EOR), category, country, seniority, contract status, end date, date of birth, team

ADP (US employees), Deel (international + contractors), Siesta/SIESA (Colombia — monthly spreadsheet upload, not an API)

fetchAllWorkers() in server/payroll.ts, merged into the same /api/growth/people feed

ADP/Deel fetched live at feed-build time; Siesta reads the latest monthly upload

People matched to payroll by name; the "Unmatched in Signal" card counts people with no payroll record. Date of birth / seniority / contract status are Deel-only

"Independent Contractor" classification pill + division/department overrides for ~22 people

NetSuite (JT's "IC List") — hardcoded in code, not a live integration

server/payroll-overrides.ts

Manual code change when the list changes

Code comment notes classification "should live in Nova long-term"

Pulse Sentiment (x/5) and Pulse Bandwidth (x/10)

Nova platform (employee pulse surveys, questions 1001/1002)

Same /api/growth/people feed

Rolling 4-week average, recomputed with the feed

Blank if the person hasn't submitted pulses recently

Pulse Weekly Hours

Nova platform (pulse question 1006; answers stored encrypted, decrypted server-side with NOVA_APP_KEY)

GET /api/growth/weekly-hours

Computed on request; average of the person's last 4 reported weeks

Per-client hours capped at 250 per submission (code constant as of Jul 2026)

novaTalent Self / Manager scores (%)

Nova platform (talent review skillset surveys)

Same /api/growth/people feed

Latest completed review per person per review type

Shown as percent of max score

Vital 5 / Hedgehog answers (lightbox)

Nova platform (embedded Retool forms)

Same /api/growth/people feed

Latest year's form only

If empty, a manually typed Vital 5 goal (Signal's own records) is the fallback

Culture Index profile image (lightbox)

Nova platform (behavior traits chart URL)

Same /api/growth/people feed

Latest row per user

Manual URL fallback stored in Signal's own records (employee_performance.culture_index_url)

RPE and # Accounts

Vena (contract + monthly revenue) allocated across Signal's own records (team assignments + revenue split overrides)

Computed client-side: DataContext.getEmployeeMetrics() → computeEmployeeRpe in src/utils/employeeRevenue.ts

Follows the app-wide revenue data load

Joined to Growth rows by normalized name — a name mismatch shows "—". Same engine as the Employees view, so numbers match across views

Last Raise Date / Amount / %

Derived from monthly payroll actuals (ADP + Deel gross-pay timelines ingested into Signal's payroll database, synced nightly at 04:00 UTC by the signal-payroll-sync cron in render.yaml, which runs scripts/sync-payroll-actuals.ts)

GET /api/growth/last-raises; results persisted per person to employee_last_raises

Recomputed in the background when >24 h stale; the UI polls every 15 s while "Loading raise history…"

Raise = a sustained jump of ≥3% comparing 3-month median windows of base earnings on each side (code constants RAISE_THRESHOLD = 0.03, TRAILING_MONTHS = 3 / FORWARD_MONTHS = 3 in server/index.ts as of Jul 2026); the month immediately before the candidate must additionally still be closer to the old pay level (anchor check pinning the raise to the first month at the new level). A raise appears only once ~3 months of post-raise payroll data exist. Not available for Siesta workers. Staging currently reads a deel_monthly_actuals_test table marked TEMP

Commission Earned YTD, Bonus Earned YTD, Total Compensation + pacing ▲/▼%

ADP and Deel monthly gross actuals (same ingested timelines)

GET /api/growth/comp-ytd

Current calendar year, recomputed on request from cached actuals

Gross earnings, not fully-loaded cost. Siesta excluded (cost-only actuals); Deel has no separate commission line

Compensation history panel (expanded row)

ADP (live "Regular Pay History" API) or Deel (live contract-amendments API)

GET /api/growth/people/:novaId/compensation-history

Fetched on click, cached 10 min server-side

Siesta shows an explanatory "monthly snapshots, no change history" message

Performance, Backfill, Cannot Lose, Retention Risk (+Context), On Leave, Career Path, Radar Status, WW1–WW3 dates, Notes

Signal's own records (entered by users in Signal; Supabase table employee_performance)

GET /api/growth/employee-performance, PUT /api/growth/employee-performance/:novaId

Immediate on save

Every save stamps the editor's email and timestamp. All traffic goes through the division-scope-checked backend proxy

Access grants and capabilities

Signal's own records (growth_access_grants, growth_access_capabilities)

GET/PUT /api/growth/access; self-check GET /api/growth/my-access

Scope/capability lookups cached server-side 2 min; a grant edit invalidates the cache

Transient lookup failures fall back to the last-known scope rather than locking the user out

Population rules baked into the feed (buildGrowthPeople() SQL, verified at staging @ 98df0c4): active Nova users only; company email domains (powerdigitalmarketinginc.com, powerdigital.com, cardinaldigitalmarketing, plus the @external.powerdigitalmarketinginc.com contractor subdomain); full-time flag required (external-subdomain workers exempt); a hardcoded test-account list excluded; and — per leadership request — VPs/SVPs/EVPs, Presidents, Founders, an explicit C-suite title list, and the Corporate department are excluded entirely. Roles literally titled "Chief X" outside that explicit list (e.g. Chief Product Officer) are kept. External-domain workers appear only on Performance Radar; People Dash filters them out. Performance Radar additionally strips VP/C-suite titles client-side.

Metrics & definitions

Every metric below appears on People Dash and/or Performance Radar (both views share most columns).

  • Annual Salary — the worker's annualized base pay in USD. Origin: ADP (US), Deel (international/contractor), or Siesta (Colombia), whichever payroll system the person matched to by name.

  • Last Raise Date / Amount / % — the most recent sustained increase in base earnings, detected from the person's monthly payroll timeline: a jump of ≥3% comparing 3-month median windows on each side (code constants RAISE_THRESHOLD = 0.03 and TRAILING_MONTHS = 3 / FORWARD_MONTHS = 3 in server/index.ts as of Jul 2026; chosen to filter bonus/PTO noise). Both windows must have a full 3 months of data — so a raise appears only once ~3 months of post-raise payroll exist — and the month immediately before the candidate must still be closer to the old level (an anchor check that pins the raise date to the first month at the new level). Amount is the annualized delta (monthly delta × 12). Origin: derived from ADP/Deel monthly gross actuals. Not computed for Siesta workers.

  • Commission Earned YTD / Bonus Earned YTD — sums of the commission and bonus earning lines in the current calendar year's monthly gross actuals. Origin: ADP (both lines) and Deel (bonus only — Deel has no separate commission line).

  • Total Compensation + pacing ▲/▼% — the current year's actual gross earnings to date, with a pacing arrow comparing the projected annual run-rate (YTD gross ÷ months with data × 12) against the person's annualized base salary. Origin: ADP/Deel monthly gross actuals. Gross earnings, not fully-loaded employer cost.

  • RPE (Revenue Per Employee) — the revenue allocated to this person across their client assignments, from Vena contract/monthly revenue split across Signal's own team-assignment records (with any revenue-split overrides applied). Computed client-side by the same engine the Employees view uses, so the two views always match. Joined to the Growth roster by normalized name.

  • # Accounts — the count of client accounts the person is assigned to, from the same RPE engine.

  • Pulse Sentiment (x/5) — rolling 4-week average of the person's self-reported sentiment from Nova pulse surveys (question 1001). Higher is better (green high / red low).

  • Pulse Bandwidth (x/10) — rolling 4-week average of self-reported workload from Nova pulse surveys (question 1002). Red = high = overloaded.

  • Pulse Weekly Hrs — average of the person's last 4 reported weeks of self-reported hours (Nova pulse question 1006, decrypted server-side). Color thresholds (code constants in PeopleDashView.tsx as of Jul 2026): green below 45 h, amber 45–49.9 h, red at 50 h and above.

  • novaTalent Self % / Manager % — the person's latest completed talent-review score, as a percentage of the maximum possible score, per review type (self-review and manager-review). Origin: Nova platform talent reviews.

  • Performance rating — a manual leadership judgment with five values (stored slug in parentheses): Elite (elite) = "A+ standard, exceeding expectations within role AND going above & beyond outside role"; Above Average (above_average) = exceeding within role; Average (average); Watch (below_average_watch) = not meeting expectations, active coaching, temporary; Active PM (below_average_act_now) = active performance documentation, backfill consideration. Setting Active PM automatically surfaces the person on Performance Radar (enforced both in the UI and server-side). Origin: Signal's own records.

  • Radar Status — the state of a performance case: PM (Active PM — set only via the People Dash performance rating, shown as a disabled option on the Radar), RT (returned to track — set on the Radar), TERM (terminated — set on the Radar with a confirmation; the person moves off the active list but stays queryable via the "Terminated only" filter). Legacy values RADAR and REDIRECT were removed from the UI in Jun 2026 (migration 20260604_radar_status_prune.sql). Origin: Signal's own records.

  • WW1 / WW2 / WW3 — three date fields recording weekly check-ins for a performance case. The UI never expands the abbreviation; "weekly win / check-in week" is inferred, not defined in the app repo. Origin: Signal's own records.

  • Est. Total Payroll (summary card) — sum of annualized salaries for everyone currently in view, with a "N of M with salary data" subtitle.

  • Theoretical Backfill Cap (summary card) — sum of annualized salaries of everyone marked Backfill = Yes: the money freed if all of them were backfilled. Shown only when non-zero.

  • Unmatched in Signal (summary card) — count of roster people with no payroll record found by name matching. Shown only when non-zero.

  • Classification (Performance Radar column) — derived pill: a hardcoded NetSuite IC-list override wins; otherwise ADP, SIESA, EOR Employee / Contractor / Deel (from Deel contract type), or External (external-domain worker with no payroll match).

How it's used

  1. Comp / raise review for a division. On People Dash (/growth/people), filter to your division, sort by Last Raise Date ascending to see who has gone longest without a raise; cross-check Annual Salary against RPE (is output outpacing pay?) and the Total Compensation pacing arrow; click the row to read the full ADP/Deel compensation change history before proposing an adjustment.

  2. Retention risk scan. On People Dash, sort by Pulse Bandwidth or Pulse Weekly Hrs descending — red badges mean overloaded people; check Pulse Sentiment for low scores. Flag genuine concerns with the Retention Risk checkbox and write the reason in the Context field; tick Cannot Lose for critical talent. The Retention Risk and Cannot Lose summary cards keep running counts for leadership reviews.

  3. Rate performance and escalate. On People Dash, work down the roster setting the Performance rating (an ⓘ hover key defines each level). Setting Active PM automatically surfaces the person on Performance Radar — no separate step.

  4. Run the weekly performance-management cadence. On Performance Radar (/growth/performance-radar), filter Status = Active PM, record each week's check-in date in WW1 → WW3 and keep running Notes. Close the case by setting Status = RT (back on track) or Term (requires confirmation; the person drops off the active list but remains visible under the "Terminated only" filter).

  5. Backfill decision → recruiting handoff. On People Dash, when a person will be replaced, set Backfill = Yes. Signal automatically appends a "Future Backfill" row (role, division, department, geo, salary, name) to the Hiring Pipeline Google Sheet — once per person — so recruiting sees it without an email. The Theoretical Backfill Cap card totals the salary at stake.

  6. Census export for offline analysis. On Performance Radar, filter to the population you need and click Export CSV — the file contains every column with raw values (full salaries, ISO dates) regardless of on-screen column visibility, UTF-8 BOM for Excel. Treat the file as confidential.

  7. Grant a new leader access. A user holding the "manage Growth access" permission opens Access → Growth Access (/growth/access), enters the unlock password, picks the person's email, chooses their division scope (all divisions, whole divisions, or specific departments for Shared Services / G&A / Healthcare), and ticks which tabs they may see (People, Performance Radar) and whether they may manage access themselves.

What users can change here

All People Dash / Performance Radar writes land in the Supabase table employee_performance (Signal's own records; one row per person, upserted through the backend proxy PUT /api/growth/employee-performance/:novaId; the editor's email and a timestamp are recorded on every save; a division-scoped viewer cannot edit anyone outside their divisions):

  • Performance rating — performance_rating: elite, above_average, average, below_average_watch (shown as "Watch"), below_average_act_now (shown as "Active PM"). Side effect: setting Active PM auto-sets radar_status = 'PM' (server-enforced) unless the person already has a radar status, surfacing them on Performance Radar.

  • Backfill (Yes / No / unset) — side effect: the first time a person is set to Yes, Signal appends a "Future Backfill" row to the Hiring Pipeline Google Sheet (server/google-sheets.ts), then stamps backfill_pushed_at so the push never repeats. The push is best-effort — a Sheets failure is logged but does not block the save.

  • Retention Risk flag + Context free-text; Cannot Lose flag; On Leave flag.

  • Career Path score (number); Culture Index URL and Vital 5 goal (manual fallbacks when the Nova platform has no data for the person).

  • Radar workflow (Performance Radar): radar_status (RT and TERM set manually; PM only flows in from the Active PM rating), ww1_date / ww2_date / ww3_date check-in dates, and Notes.

Separately, on the Growth Access screen (/growth/access), users holding can_manage_access (or Sauron super-admins) write to growth_access_grants (division/department scope rows; revoke-before-insert ordering so a partial failure fails toward less access) and growth_access_capabilities (the per-user People / Performance Radar / Manage checkboxes). Revoking everything deletes the capabilities row so the user drops off the access list.

Nothing in the Growth module writes to the Nova platform, ADP, Deel, Siesta, NetSuite, or Vena — all payroll and revenue data is read-only.

Caveats & data quality

  • Data can be up to ~24 hours old by design. The /api/growth/people feed uses stale-while-revalidate caching: served fresh for 15 minutes, then up to 24 hours stale while a background rebuild runs. A persisted snapshot survives server restarts.

  • Raise history fills in gradually. Last-raise computation runs in the background and is persisted per person; the UI polls every 15 seconds and may briefly show "Loading raise history…". This is normal, not a bug.

  • Name-based matching. People are matched to payroll records, and Growth rows are matched to the RPE engine, by normalized name. A mismatch shows "—" (RPE) or counts in the "Unmatched in Signal" card. Admin-only debug endpoints (/api/growth/debug/*, full-access only) exist to diagnose; there is no automated monitoring defined in the app repo.

  • Staging-only table wiring. As of staging @ 98df0c4, the last-raise and Deel-actuals reads point at deel_monthly_actuals_test (and a deel_people_test roster in server/payroll-actuals.ts) with explicit "TEMP — revert before promoting to main" comments. Production behavior should be validated against main.

  • Hardcoded lists. The NetSuite independent-contractor overrides (~22 names with divisions, server/payroll-overrides.ts), the exec-exclusion email/title lists in the roster SQL, and the Sauron super-admin list are code constants updated by developers, not data-team-tunable.

  • Thresholds are code constants (as of Jul 2026): raise detection ≥3%; weekly-hours colors green <45 h / amber 45–49.9 h / red ≥50 h; pulse averages over a 4-week window; 250 h per-submission hours cap; 5-minute background re-lock for the unlock password; 2-minute server-side access-scope cache; 24-hour raise-staleness window; 10-minute compensation-history cache.

  • Ingest cadences. The ADP/Deel monthly gross actuals ingest is defined in the repo: the signal-payroll-sync cron in render.yaml runs nightly at 04:00 UTC (schedule: "0 4 * * *"), executing scripts/sync-payroll-actuals.ts, which upserts the deel_monthly_actuals and adp_monthly_gross timelines the raise and comp-YTD computations read. Only the Siesta monthly xlsx upload is not scheduled in the repo — it is a manual import (scripts/import-siesta.py) whose cadence and owner are not defined here; treat Siesta cadence claims as unconfirmed.

  • Siesta limitations. Siesta (Colombia) is a monthly snapshot: no raise history, no YTD compensation breakout, no compensation change history.

  • Label vs stored value. The bottom two performance ratings display as "Watch" and "Active PM" but are stored as below_average_watch / below_average_act_now. "RT" is never expanded in the UI; "returned to track" is inferred. What "WW" stands for is not defined in the app repo.

  • Column preferences reset on version bumps. Column-visibility choices persist per browser under a versioned key (People Dash people-dash:column-visibility:v3 as of Jul 2026); a future version bump silently resets saved choices.

  • Confidentiality. Every screen shows individual salaries, and the Performance Radar CSV export includes full raw salaries for all columns, including hidden ones.

  • Formal PM policy is not documented in the app. The tool implies the Watch → Active PM → RT/Term process but does not document the policy or its owner (code comments reference "JT's Performance Radar").

Related views

  • Employees view (/employees) — clicking a person's name on People Dash deep-links to their profile (/employees?search=<name>). RPE and # Accounts come from the exact same client-side engine, so numbers match between the Growth module and the Employees view.

  • Performance Radar ← People Dash — the "Active PM" performance rating on People Dash is the only way a person enters Performance Radar; the Radar is where the case is worked (WW1–WW3, Notes) and closed (RT / Term).

  • Hiring Pipeline view (/hiring) — setting Backfill = Yes on People Dash appends a row to the same Google Sheet the Hiring Pipeline view reads, so the two modules share recruiting state.

  • Pulse Coverage / Pulse Submission views — the Sentiment, Bandwidth, and Weekly Hours columns aggregate the pulses employees submit; low pulse coverage means blank cells in the Growth module.

  • Summary view (/summary) — the Growth roster deliberately mirrors the headcount definition used by the Summary view's headcount tile ("active PD employee"), so counts tie out.

  • Divisional P&L view (/divisional-pl) — shares the unified payroll layer (server/payroll.ts) and the same monthly actuals tables used for cost reporting.

  • Growth Access screen (/growth/access, "Access" nav section) — where Growth tab visibility and division scopes are granted.

FAQ

Q: Why can't I see the Growth section in Signal at all? A: The Growth section (People Dash, Performance Radar) is hidden unless you have been explicitly granted at least one Growth tab. Grants are per-person: a division/department scope plus per-tab flags (People and/or Performance Radar), managed on the Growth Access screen by a Growth access manager. Being a Signal admin does not grant Growth access. Ask your division leader or the Growth access owner to add you.

Q: I can see People Dash but not Performance Radar — why? A: As of July 2026 the two Growth tabs are granted separately. Performance Radar requires its own opt-in flag (can_view_radar), which defaults to off — everyone who had Growth access before the per-tab change kept People Dash only. A Growth access manager can enable the Radar tab for you on the Growth Access screen (/growth/access).

Q: Why does Signal keep asking me for a password when I open People Dash? A: The Growth tabs have a second-factor unlock password on top of your personal grant, because the screens show individual salaries. The password is deliberately never saved: a page reload clears it, and it also re-locks after Signal has been in a background tab for about 5 minutes. The backend rejects any Growth data request without the key, so this is real security, not just a hidden screen. If you don't know the password, ask the Growth access owner (it is not the same as your login).

Q: Why is a person's RPE showing "—" on People Dash when they clearly have accounts? A: RPE and # Accounts are joined to the Growth roster by normalized name. If the person's name in the payroll/Nova roster doesn't match their name in Signal's revenue engine (nicknames, married names, accents), the join fails and shows "—". The revenue itself is fine — it's a name-matching gap. Report it to the Signal team; admins have diagnostic endpoints to trace matches.

Q: Why doesn't someone's "Last Raise" show up, or why does it say "Loading raise history…"? A: Raise history is computed in the background from monthly payroll timelines and fills in gradually — the page polls every 15 seconds, so brief "Loading…" states are normal. A raise only registers when base earnings jump at least 3% comparing 3-month median windows on each side (code constants TRAILING_MONTHS = 3 / FORWARD_MONTHS = 3 in server/index.ts — a deliberate filter against bonus/PTO noise), and the month immediately before the candidate must still be closer to the old pay level (an anchor check that pins the raise to the first month at the new level). So a small increase may not appear, and a recent raise won't register until about 3 months of post-raise payroll data exist. Colombia (Siesta) workers never show raise history because Siesta provides monthly snapshots only.

Q: Why is an executive / VP missing from People Dash? A: By design. Per leadership request, the Growth roster excludes VPs, SVPs, EVPs, Presidents, Founders, an explicit C-suite list, and the Corporate department. The caption on People Dash states this ("Execs, VPs and C-Suite excluded"). Roles literally titled "Chief X" outside the explicit C-suite list (for example Chief Product Officer) are kept.

Q: Why doesn't Total Compensation match what finance reports for a person? A: The Growth module's Total Compensation is the current calendar year's actual gross earnings (regular + overtime + bonus + commission) from ADP/Deel payroll timelines — not fully-loaded cost (no employer taxes, benefits, or EOR fees) and not the contracted salary. The pacing arrow compares the projected annual run-rate against annualized base salary. Colombia (Siesta) workers are excluded because their actuals are cost-only.

Q: What happens when I set someone's Backfill to Yes? A: Two things: the flag is saved to Signal's own records with your email and timestamp, and — the first time only — Signal appends a "Future Backfill" row (role, division, geo, salary, name) to the Hiring Pipeline Google Sheet so recruiting picks it up automatically. The push never repeats for the same person, and a Sheets outage won't block your save.

Q: How does someone get onto Performance Radar, and how do they come off? A: The only way onto the Radar is rating them "Active PM" on People Dash — the Radar's own Status dropdown shows Active PM as a disabled option. The case is worked on the Radar (WW1–WW3 weekly check-in dates, Notes) and closed by setting Status = RT (returned to track) or Term (terminated; requires a confirmation, then the person drops off the active list but stays visible under the "Terminated only" filter).

Q: Who can grant Growth access, and can access be limited to one division or department? A: Anyone holding the "manage Growth access" permission (plus the four hardcoded super-admins) can open Access → Growth Access, after entering the unlock password. Access is scoped: all divisions, specific whole divisions, or — for Shared Services, G&A, and Healthcare only — specific departments within the division. A scoped viewer sees only people, salaries, raises, and performance rows inside their scope, and cannot edit anyone outside it. The manager also chooses which tabs (People, Performance Radar) the person sees.

Q: Is the CSV export safe to share? A: No — treat it as confidential HR data. The Performance Radar Export CSV includes every column with raw values, including full salaries and dates of birth where available, regardless of which columns were visible on screen.

Did this answer your question?