# Decisions — September 17, 2026 Three items. Like `D11`, `D15`, `D16` and `D17`, these are new scope raised after R2 closed out (see `docs/reference/completion.md`, T9.7), not a reopening of anything already decided there. `CR-019` and `CR-020` get `CR` ids because they are field/product-facing feature requests — the same kind of thing `CR-001`-`CR-018` were — not internal engineering calls made mid-build. `D18` gets a `D` id because it is exactly that: an internal security/architecture call, the same category as `D15` (Okta vs. LDAPS), not a feature a user asked for. Requested/raised by Matt Mabrey, 2026-09-17. --- ## CR-019 — Usage and activity metrics (admin console) - **Area:** Admin Console / Monitoring - **Priority:** proposed High — the current admin console has no reliable way to answer "who is using this and how much," which is the same visibility gap the original UX review found in `B4` (numbers that look authoritative but are not). - **Source:** Matt Mabrey, 2026-09-17. ### Why this is not already built `D5`/`T7.10` already shipped a usage-analytics feature — `html/wp-usage.js`, with a report at `admin.js:666-699` — but it reads `localStorage`, which is scoped to one browser. The report's own empty state says exactly this: *"No usage recorded in this browser yet."* It cannot show who across the team is active or what they use, because each person's activity exists only on their own machine. This is the same class of defect `B4` named for the pipeline strip — a number that looks authoritative but is not — just never generalized to this feature. `CR-019` is therefore new server-side work, not a UI addition on top of what exists. Two things already in the schema are relevant and reused rather than duplicated: `User.last_login_at` (one timestamp, no history) and `AuditLog` (append-only, already records business mutations — WP created, status changed, role changed — per user, with a timestamp). Neither captures navigation or feature-open events, which is the actual gap. ### Intent Give admins a real, server-backed picture of who is using the suite, what parts of it they use, and how active they are — replacing the per-browser report as the thing anyone actually looks at. ### Decisions, made 2026-09-17 1. **Retention: indefinite.** No automatic purge of usage-event data. (Separate from `AuditLog`'s own retention, which this does not change.) 2. **Scope: suite-wide, filterable.** Not per-project by default; the console provides filters (date range, project, user, tool/page) rather than scoping the data itself. 3. **Export: raw view shows real identities; export supports sanitization.** The admin console's own tables and the CSV export both default to real usernames — this is an internal audit tool, not a public one. But the export also offers a "sanitize" toggle that replaces the actor with a **stable pseudonymous id** (a per-user hash, consistent across rows and across export runs) rather than dropping the identity field outright — so an external system (Power BI or similar) can still group and trend "by user" without ever receiving a real name. Flagged here as the recommended approach rather than the only one Matt confirmed in so many words: if a fully-anonymous (no stable id at all) export turns out to be what's actually wanted, that is a one-line change to the same feature, raise it in the PR rather than treating it as blocking. 4. **The old per-browser report is retired**, not kept alongside the new one. Once `CR-019` ships, `admin.js`'s existing `usage-admin` panel (reading `WPUsage.load(...)` per browser) is removed rather than left next to the real report, where it would show a smaller, misleading number for whoever happens to have it open. `html/wp-usage.js` and its two call sites (`html/work-package-suite-app.js`'s wizard dwell-tracking, and the creator's equivalent) are a separate question — the *recording* code can stay or go independent of the *admin report* being retired, since dwell events were never reliably tied to a real identity anyway. Default to removing both unless a task finds a reason to keep the recorder; log that reason rather than deciding it here. ### Acceptance criteria - A new admin-only tab in `admin.html` (same role gate as the User Directory) shows: active users over a selectable date range (day/week/month), each user's last-active timestamp, and a breakdown of which tools/pages get opened and how often. - Server-side event capture, keyed to the authenticated session (real identity, not a browser-local guess) — a new table, not an extension of `AuditLog`, since page-open/navigation events are not business mutations and mixing them in would make `AuditLog` noisy for its existing, narrower purpose. - The console's filters cover date range, project, user, and tool/page, and combine (e.g., "user X, last 30 days, field view only"). - CSV export from the console, in both raw (real usernames) and sanitized (stable pseudonymous id per user) modes. - Retention is indefinite; nothing in this item purges data. - View-only, refresh-on-load. No alerting — matches how the rest of the admin console works today; if that changes later it is new scope, not a rider on this item. - The old per-browser "Usage" report and its admin-console panel are removed in the same wave, not left running alongside the new one. - Accessible per `CLAUDE.md`'s standing `C1` rules (this is a new component, not a legacy one — it ships accessible or it is not done, same as everything else built since wave 7). ### Frontend/backend boundary This needs server work, the same way `CR-004`/`CR-018`/`B4` did: a real table, a capture endpoint, an aggregation endpoint, and an export endpoint. If a task under this item is being built by writing to `localStorage`, it is rebuilding the exact defect this item exists to replace — stop and say so, per `CLAUDE.md`. ### Scheduling New wave. Wave 10 (Okta) is merged, so this does not wait on anything. Task breakdown: `docs/waves/wave-11.md`. --- ## CR-020 — Bulk editing of users in the admin console - **Area:** Admin Console / User Directory - **Priority:** proposed High — three of the four actions below touch access control (role, project assignment, active/disabled) and the fourth is a hard delete; getting the guardrails right matters more than getting it built fast. - **Source:** Matt Mabrey, 2026-09-17. ### Why this is not already built Every user-editing action in `html/users.js` today is one row, one action: a role `