Phase 1
Teams & Users v2

Source: c81 phase1-pages — Teams & Users (PRO-20); teams-ideas-v7/v8 roster + person expand card, users-devices-v1 coverage-vs-denominator header, re-derived from the phase1 world

v2 — second iteration from the 2026-09-03 Mark Peterson walkthrough (cycles/c81.phase1-pages/teams-v2-plan.md): Intensity dropped (subjective; the roster's existing uncovered→stalled→tokens order already carries the leaderboard), a lack-of-signal note, the person summary promoted into the expand card, and a stated web-AI coverage gap. Team-level comparison stays a separate design pass.

fixture state

18 people known to Okta, 15 covered by a sensor — 3 unaccounted for.

3 without a sensor (Alexis Romero holds a seat — investigate) · Wes Okafor started 21d ago with no sessions

27 of 28
installs with an owner
1 install with no identity — attribution debt, Inventory →
Okta connected Jamf connected18 people known to Okta21 devices known (15 via Jamf, 5 via Okta, 1 manual)

Teams

click scopes the verdict, band and roster · spend by team lives on Cost Center

People

one row per person · ranked uncovered first, then the stalled joiner, then by tokens · 30d
Team
Devices
Agents
Tokens · 30d
Last active
Coverage
AR
Alexis Romero
Backend engineer
Engineering
alexis-mbp
none
0never uncovered
holds a seat — investigate
MT
Miguel Torres
SDR
GTM
miguel-mbp
none
0never uncovered
no sensor, nothing observed
OH
Omar Haddad
Finance
Ops
omar-mbp
none
0never uncovered
no sensor, nothing observed
WO
Wes Okafornew · 21d
Engineer (new)
Engineering
wes-mbp
Claude Code
1 install
0never covered
TB
Tyler Brooks
Staff engineer
Engineering
tyler-mbp
Claude Code
Codex
2 installs
58M3h ago covered
KR
Kamal Reddy
Senior engineer
Engineering
kamal-mbp
kamal-win-01win
Claude Code
Codex
Gemini
3 installs
52M4h ago partial
1 of 2 · Windows gap
DW
Dana Whitfield
Account executive
GTM
dana-mbp
Codex
2 installs
42M2h ago covered
MC
Marcus Chen
Platform engineer
Engineering
marcus-mbp
Claude Code
Codex
Grok
3 installs
34M7h ago covered
RC
Rob Castellano
Solutions lead
GTM
rob-mbp
rob-win-01win
Claude Desktop
Codex
Gemini
3 installs
26M5h ago partial
1 of 2 · Windows gap
PN
Priya Natarajan
Frontend engineer
Engineering
priya-mbp
Claude Code
Codex
2 installs
24M5h ago covered
KN
Kai Nakamura
Head of operations
Ops
kai-mbp
kai-win-01win
Claude Desktop
Grok
2 installs
22M8h ago partial
1 of 2 · Windows gap
HL
Hana Lindqvist
Product engineer
Engineering
hana-mbp
Claude Code
1 install
12M3h ago covered
JF
Jonah Fitz
Tooling engineer
Engineering
jonah-mbp
Claude Code
Hermes
2 installs
9.0M8h ago covered
EW
Erin Walsh
Product marketing
GTM
erin-mbp
Codex
1 install
8.0M27h ago covered
LG
Leah Goldman
IT administrator
Ops
leah-mbp
Codex
Gemini
2 installs
7.0M6h ago covered
SA
Sofia Alvarez
Field marketing
GTM
sofia-mbp
Claude Desktop
1 install
6.0M7h ago covered
NP
Natalie Pierce
Office & facilities
Ops
natalie-mbp
Claude Desktop
1 install
5.0M27h ago covered
BA
Ben Ashworth
Customer success
GTM
ben-mbp
Claude Desktop
1 install
4.0M4h ago covered

18 people · zero-usage rows stay visible — who is not using AI is still interesting to management. ● sensor installed · ○ no sensor · dimmed agent mark = detected-only or never run · tokens are the measured 30-day total; dollars are estimates and live on Cost Center. Ranked uncovered first, then the stalled joiner, then by tokens descending — the ranking is the leaderboard; no separate intensity label. Last active = last session on record, else the latest monitored heartbeat when 30-day usage exists, else never. Coverage is a fraction of the Okta/Jamf denominator, not of what the sensor happens to see — and covers local CLI/desktop agents only; browser-based AI chat (e.g. claude.ai) isn't sensor-observed yet.

Composition notes

  • Question: who is using AI, and is anyone unaccounted for? Two accountings against the identity source's denominator (Okta/Jamf), never the sensor's own sightings: people with no sensor on any device, and covered people who have never run an agent. The inverse — AI use with no person — is Inventory's row; the band names it and links.
  • Verdict line is verdicts.teams(world) verbatim at fleet scope; under ?team= the page's own teamsVerdict(world, team) produces the same sentence shape over the team so it cross-foots with the rows on screen.
  • Framing band = four claims (Phase1StatBlock): people covered (the one attention device, a filter), devices sensored (deployable vs Windows platform gap, a filter), using AI · 30d (the stalled joiner rides its sub — adoption folds into the claim rather than a lever; the c67 strip stays gated), installs with an owner (a link — one number, one home). All computed from the rows in the table.
  • Amber ruling applied: c67 said amber = attribution debt only; the c81 chassis says amber = attention debt only. This page spends its one amber on the coverage gap and states the attribution debt (an install with no identity) in neutral tone with its link. Personal accounts are the red class, in the expand only.
  • Team drill-down = filter in place: the strip writes ?team=; verdict, band and roster scope to it. People and measured tokens per team from teamRollup; spend by team is Cost Center's (c67 decision 1).
  • Table is the raw UxTableList family so ?id= can pre-open a row through refs. Rows from personRoster(world, team), ranked investigate → uncovered → stalled joiner → tokens; sorters on the rest; search on the Person header. One attention device per row: the Coverage column, except the stalled joiner whose row keeps Coverage quiet and carries amber on Last active = never.
  • Expand = the c67 person expand card rebuilt on the world: identity & attribution (team chip filters in place, devices and accounts as Phase1EntityChips with sensor/licensing subs, "user detail →"), attention (derived items, each with a verb-link; absent when clean), agents & usage (one row per install with monitored pill, licensing badge, 7d sessions and tokens, "view details →"; then the 30d usage line with models, repos, MCP servers). Sessions stay on User detail — the card prints the count.
  • Derivation: every number traces to coverageStats / teamRollup / stalledJoiners / monitoredStats through derived/teams.ts. The Okta/Jamf integration state is a world fact (world.integrations, folded in at reconcile) — the denominator line reads it, the page asserts nothing.
  • Open: whether the stalled joiner deserves its own band claim; "partial" vs "1 of 2" as the coverage word for a Windows-gap person; the page name (Linear's "Teams & Users" kept; c21 floated "Users / Devices").
  • v2 (2026-09-03, Mark Peterson walkthrough): Intensity column and the expand card's heavy/moderate/light meter both dropped — Tyler's call: "let's not try to define the intensity because it's always gonna be subjective... you can just sort by tokens." The roster's existing base order (uncovered → stalled joiner → tokens descending) already is that leaderboard; no default-sort change needed. The expand card's Agents & usage section now always leads with person.summary (already-authored fixture data, previously shown only as the zero-instances fallback) — this is the "human readable summary" TODO, just promoted rather than newly built. Added a page-local lackOfSignal() (covered, has run an agent, quiet > SIGNAL_GAP_DAYS = 5d) on Last active — distinct from "not-using" (never run) and from the stalled-joiner case. The footer now states the web-based-AI coverage gap (claude.ai etc. not sensor-observed) as a named limitation. Team-level cost/tokens comparison (validated in-call, using the composed-portal-strip pattern) stays out — it's its own design pass, not folded in here. See mark-peterson-design-brief.md.

Qpoint Brand Style Guide