LiveDesign previewsUnified Platform · Roles & Dashboards1 · The plan2 · Ideas (ce-ideate)

Live in the platform

Nancy's OR board — shipped 2026-09-30

This one is real. The OR-scoped manager lands on a board built from her own data, in the fuller layout — OR strip, the day's slate, the call as a table by role, then OR week, block-time usage and the open-shift pool. Nobody else's dashboard changed.

Verified in her own session: the case count on screen equals what /api/v1/or-schedule/board returns, and the controls — a manager without the OR scope, and the finance officer — render exactly as before. Her hero line now reads OR Supervisor rather than the category word "Manager".

Live design previews

Nancy's OR board — previews

Standalone pages built from her live data. These are previews only — the block-week surfaces below are not in the app yet; the board above is.

1 · The plan


title: Role-differentiated dashboards and access points (Unified Platform)

type: feat

status: active

date: 2026-09-30

origin: owner brief 2026-09-30 (chat) — "think about what each user should really have access to and what should be on their landing dashboard page"


Role-Differentiated Dashboards and Access Points

Overview

The platform is close to done. What remains is not features but fit: each person should land on a

dashboard that matches their job, and hold exactly the rights that job needs — no more.

Today the platform has five dashboard branches and three nav allow-lists, and the fit is wrong in

three ways: ten leadership accounts across four different jobs (OR, finance, staff scheduling,

faculty scheduling) share one dashboard; the big boss cannot see the money; and residents land on

a page with no evaluations on it. This plan assigns each role its own landing surface and closes the

access gaps that the audit below verifies.

Implementation happens on the live tree, not the git mirror. See Unit 0.

Problem Frame

Verified against the live platform on 2026-09-30 (see Current State). The dashboard chooses its layout

from role booleans in app/src/pages/Dashboard.tsx:

BranchWho lands hereWhat they see
isAdminsfrasierHero band + 4 admin metrics + recent activity + admin focus + program links
isFinanceCrystalFinanceOfficerDashboard (KPI tape, cost centres, funds, transactions, exceptions, reports)
`isManager \\isStaff`10 managers + 29 staffHero band + self tape + CallWeek / OutToday / PendingDecisions / CoverageAlerts / StaffAbsence / OperationsActivity / RecentActivity
isFaculty24 attendingsFaculty schedule + commitments + recent activity + faculty tiles
isResident16 residentsGME fund + upcoming shifts + recent activity + resident tiles
fallbackFellow, medical students, 6 contact-less rowsrecent activity + program links

So Nancy Solano (manager of the OR) and Winnie Wint (staff schedules) and ~29 staff land on the same

page, and nothing on that page is about the OR or about a staff roster.

Status — landed live 2026-09-30

Deployed to /var/www/unified and verified against the running app (not by description).

What a person now gets, rendered from a real session token — not the admin view-as preview:

PersonSidebar
Nancy Solano (or)Dashboard · View · Block Schedule · Edit · Pool · Activity History · Coverage Center · Reimbursement — and /schedule shows her View · Blocks · Edit · Pool
Kelly Bottger (exec)Dashboard · View · Edit · Pool · Activity History · Coverage Center · Reimbursement · Review Claims · Reporting · Reimbursement Profiles — and she LANDS on the finance board ("Kelly's finance desk")
Crystal Santos (unchanged)Finance Dashboard · All Schedules · Call Schedule · Reimbursement · Review Claims · Reporting · Reimbursement Profiles
Winnie Wint (control, no scope)Dashboard · View · Edit · Pool · Activity History · Coverage Center · Reimbursement — byte-identical to before

Evidence: /api/v1/me returns managerScope for all four; SPA + server tsc clean; 11/11

roles.test.ts (incl. the negative and ordering cases); 9/9 dashboard/schedule smoke tests; 7 files

md5-verified after upload. Revert: _bak-rolescope-20260930-192542 (code),

_bak-dist-rolescope-20260930-192809 (built frontend).

Two things this turned up:

  • 1. Angela Alaimo cannot use the platform at all — she is one of the ten designated managers but
  • her app_access row has no ez_id, so the app renders "Email Not Linked" and no sidebar. This is

    pre-existing and unrelated to this change (touched nothing about identity resolution; her scope

    is null), but a manager locked out of the platform is worth fixing. Needs her EZ ID.

  • 2. The admin view-as rail cannot preview a scoped nav. The preview ANDs the previewed flags
  • against the admin's own, so previewing Nancy yields managerScope = null and renders the generic

    manager nav. Use a real token to review a scope-dependent surface.

    Still open: Unit 3 (faculty eval management — gated on the /eval/:token submission proof),

    Unit 5 (the differentiated dashboard *boards*: Nancy's OR board is still the operations dashboard),

    Units 6–9. Access points are done; the landing *pages* are next.

    Requirements Trace

  • R1 — Nancy is the manager of the OR: OR-specific surfaces + an OR landing dashboard.
  • R2 — Crystal is the finance manager: finance-only access + finance dashboard. (Shipped 2026-09-28; verify and keep.)
  • R3 — Admin manages everything, including residents' evaluations. (Holds today.)
  • R4 — The other managers own staff schedules and faculty OR / Clinic / Call schedules.
  • R5 — Kelly Bottger is the big boss and should see what Crystal sees.
  • R6 — Residents land on their schedules and evaluations.
  • R7 — Tell the owner who was missed in the brief.
  • Scope Boundaries

  • No new RBAC engine, no policy DSL, no per-user dashboard builder, no widget drag-and-drop. The
  • explicit-column + allow-list pattern already in the codebase is the pattern; extend it.

  • No change to the five existing dashboards' internals beyond splitting the shared branch and adding
  • the resident evaluations card.

  • The off-menu /finance Infor portal stays off-menu (its own header documents this as deliberate).
  • No new external services. No email.
  • Context & Research

    Source of truth (verified 2026-09-30 — this one bit us)

    TreeWhat it is
    /var/www/unified (VPS)LIVE. Bind-mounted to /app in hermes-webui-gsga-unified-platform-1. This is where code must land.
    /workspace/projects/unifiedGit mirror in the agent workspace. Lags live.

    md5sum differed on every file this plan touches:

    DRIFT src/components/Layout.tsx  local=1ade75d1… live=df507c31…
    DRIFT src/pages/Dashboard.tsx    local=2f36d7f4… live=0f475d98…
    DRIFT src/lib/roles.ts           local=98e6a3dc… live=30b1bacf…
    DRIFT src/pages/Admin.tsx        local=1a4f3fa7… live=5755295c…

    Editing the mirror and building it would ship a regression of the finance-officer work. **Unit 0 fixes

    this before anything else.**

    Relevant code and patterns

  • app/src/components/Layout.tsx — MANAGER_SECTIONS (l.133), FULL_SECTIONS (l.172),
  • FINANCE_SECTIONS (l.261); selection order at l.295: isFinanceOfficerOnly ? FINANCE : isManagerOnly ? MANAGER : FULL.

    Every item in an allow-list needs always: true or sectionHasItems() drops the whole section.

  • app/src/lib/roles.ts — canonicalRole, isManagerOnly, isFinanceOfficerOnly, canFileForOthers,
  • isCallOutReporter, canRequestSwap, canViewProgress.

  • app/src/pages/Dashboard.tsx — the branch logic above. Bug: isManager (l.115) is
  • category === 'Manager' || a.canEditSchedule — raw category, not canonicalRole(). Same for

    isFaculty / isResident / isStaff. Fix while splitting the branch.

  • app/src/pages/Admin.tsx — FINANCE_TABS = ['review','reports','profiles']; allowedTabs at l.121.
  • server/routes/admin.ts — requireFinance (l.120), requireFinanceOrReviewer (l.133),
  • requireAdminOrEditorOrFinance (l.146).

  • server/db/migrations/042_finance_officer.sql — the template for adding a designation column.
  • app/src/components/dashboard/FinanceOfficerDashboard.tsx (456 l.) — the model for a rich role dashboard.
  • Current state (live DB, 2026-09-30)

    unified.app_access: 93 rows, 12 flag columns (incl. is_manager, is_finance_officer).

    unified.role_defaults: 8 categories. Precedence is row.col ?? defaults.col — a NULL in the row

    inherits the category default, which is how the faculty eval gap below is hiding.

    Categorynadminmgrfineff. editeff. evalseff. revieweff. rostereff. scl
    Admin110111111
    Manager11010110001110
    Staff290000002629
    Faculty24000024243
    Resident160000001613
    Fellow100000011
    Medical Student300000000
    Archived300000022
    *(no CRM contact)*600000066

    Gaps found (this is the answer to "did I miss anyone?")

  • G1 — Managers are undifferentiated. One is_manager boolean covers OR, finance, staff scheduling
  • and faculty scheduling; there is no scope column anywhere in the codebase (grep: no manager_scope

    / manager_domain). Nancy and Winnie get byte-identical navs. This is the root cause of R1 + R4.

  • G2 — Kelly cannot see the money. is_finance_officer = 0; she gets the generic manager nav. R5 lost.
  • G3 — Nancy has no OR surface. No OR grid, block usage or pool ownership on her landing page.
  • G4 — Managers and staff share one dashboard. 40 people, one page, mostly about neither.
  • G5 — Residents get no evaluations on landing. My Evaluations / My Progress are nav-only.
  • G6 — Every attending holds eval management. All 24 faculty resolve can_manage_evals = true
  • through role_defaults.Faculty (their rows are NULL), so /eval-admin, /eval-tracker,

    /eoy-admin, /eval-campaigns are reachable by all of them. Contradicts R3 as stated.

    Verify before changing — see Risk R-2.

  • G7 — Six access rows have no CRM contact (fconcepcion, acaceres, alopez, maleman,
  • jsilva, jrodriguez). Real people with explicit flags, but no name/category → catch-all dashboard,

    and they are hard to find in /admin users.

  • G8 — Test accounts still hold access rows (test-faculty, test-manager, test-resident
  • @montefiore.org).

  • G9 — Who was missed in the brief: faculty (24, own dashboard, needs eval question settled),
  • fellows (1 — Ariel Allen, currently fallback), medical students (3, all-false, nothing today),

    the 6 NPs/staff who report absences (29 staff incl. Yvette Duchein, Paula Keatley, Rosa Heredia,

    Tashana Ferguson, Sindhu Kanneth), archived (3 — one, sreese, still has a row), and the

    /finance Infor portal (off-menu by design — confirm it stays that way).

    Target model (proposed)

    RoleWhoNavLanding dashboard
    Owner / Adminsfrasierfull (unchanged)admin command (unchanged)
    ExecutiveKelly Bottgermanager nav + Finance (review / reports / profiles)Exec roll-up: money snapshot (Crystal's KPIs), coverage, eval completion, headcount
    Finance officerCrystal Santosfinance-only (shipped)FinanceOfficerDashboard (shipped)
    OR managerNancy SolanoOR group: schedule view, OR grid, block usage, pool, coverage, activity, own reimbOR board: today's OR slate, block utilisation, open/uncovered cases, pool
    Staff-schedule manager*to be named*staff schedule, schedule view, coverage, activityStaff board: roster today, absences, coverage gaps
    Faculty-schedule manager*to be named*schedule view/edit, call editor, call schedule, reportsScheduler board: unfilled call/clinic slots, swap requests, publish state
    Faculty24 attendingsunchanged, evals narrowed per R-2 decisionfaculty tiles (unchanged)
    Resident16unchanged+ My Evaluations card (due/completed), next shift, on-call, GME fund
    NP / Staff29unchangedstaff self view: my absences, clinic today, coverage
    Fellow1resident-likeresident-like
    Medical student3minimalminimal read-only
    Archived3noneblocked

    Implementation Units

  • [x] Unit 0: Re-baseline the git mirror against live
  • Goal: eliminate the drift so nothing built from the mirror regresses live work.

    Files: whole repo (/var/www/unified → /workspace/projects/unified); note in docs/runbooks/.

    Approach: rsync live → mirror excluding node_modules, dist, .env*, .pylibs; commit as a

    baseline; record the md5 of every file this plan touches.

    Verification: md5sum of Layout.tsx, Dashboard.tsx, roles.ts, Admin.tsx match on both trees.

  • [x] Unit 1: Designation columns (manager_scope, is_eval_lead)
  • Goal: make "which manager" expressible in data, exactly as is_manager / is_finance_officer are.

    Files: Create server/db/migrations/043_manager_scope.sql; model on 042_finance_officer.sql.

    Approach: manager_scope text (null | or | staff | faculty_schedules | exec) and

    is_eval_lead boolean not null default false.

    > Shipped 2026-09-30: manager_scope only. is_eval_lead was cut — it belongs to Unit 3,

    > which is still gated, and an unused column is a promise the system does not keep. Add it with

    > Unit 3. manager_scope also carries a CHECK constraint so a mistyped scope fails loudly

    > instead of silently falling back to the generic manager surface. Backfill: Kelly exec, Nancy or; leave the other

    eight managers null (= generic, today's behaviour). Print a before/after select in the migration

    comment, as 042 does.

    Test seam: migration is data-only; assert with a SQL query listing scope by email.

    Verification: select email, manager_scope, is_eval_lead from unified.app_access order by 2 nulls last;

    returns the expected four values with no unintended non-null scopes.

  • [x] Unit 2: Plumb both columns through the four places
  • Goal: access.managerScope / access.isEvalLead reach the SPA without breaking the build.

    Files: server/db/migrations/ (done in U1) · server/middleware/auth.ts (AccessFlags, ALL_FALSE,

    SELECT list, row mapping) · app/src/types/index.ts · app/src/App.tsx GuardUser inline type.

    Approach: the documented four-place rule — miss the App.tsx inline type and tsc fails with

    TS2339. Predicates must return real booleans.

    Test seam: /api/me shape — assert the two keys present for a scoped user.

    Verification: npm run build in the container exits 0; /api/me returns both fields.

  • [ ] Unit 3: Close the faculty evaluation over-grant (gated on Risk R-2)
  • Goal: eval management belongs to the admin and designated eval leads, not 24 attendings.

    Files: server/db/migrations/044_eval_scope.sql.

    Approach: set role_defaults.Faculty.can_manage_evals = false, then grant explicitly to the eval

    lead(s) + admin. Precondition: prove faculty submit evaluations through /eval/:token, which is

    public and does not read canManageEvals — otherwise this removes a workflow.

    Verification: effective-evals query from Current State returns only admin + eval leads; a faculty

    account still submits an evaluation end-to-end via its token link.

  • [x] Unit 4: Nav per role
  • Goal: R1, R4, R5 in the sidebar.

    Files: app/src/components/Layout.tsx (MANAGER_SECTIONS keyed by scope; add EXEC_SECTIONS),

    app/src/lib/roles.ts (isOrManagerOnly, isExecOnly, managerScope helper).

    Approach: keep the allow-list pattern and selection order (finance → exec → manager → full); every

    item keeps always: true. Do not switch to flag filtering.

    Test seam: extract the section builders as pure functions so a test asserts item labels per role

    without rendering.

    Verification: rendered nav per role (playwright, /opt/webcapture): Nancy sees the OR group, Kelly

    sees Finance, Crystal unchanged, sfrasier unchanged.

  • [ ] Unit 5: Split the shared manager/staff dashboard
  • Goal: R1, R4, R6 — give each of the 40 people a page about their job.

    Files: app/src/pages/Dashboard.tsx (split the isManager || isStaff branch; replace raw

    category === tests with canonicalRole()), plus new

    app/src/components/dashboard/OrManagerBoard.tsx, StaffBoard.tsx, SchedulerBoard.tsx,

    ExecBoard.tsx, MyEvaluationsCard.tsx.

    Approach: one variant selector dashboardVariantFor(user) returning a discriminated union, one

    component per variant, each composed from existing cards + one or two new ones. Reuse ViewHeroBand,

    ViewMetricTape, Card.

    Test seam: dashboardVariantFor is pure — table-driven test over the 12 role shapes.

    Verification: each role renders its own variant; Dashboard.smoke.test.tsx extended and green.

  • [ ] Unit 6: Resident evaluations on landing
  • Goal: R6.

    Files: app/src/components/dashboard/MyEvaluationsCard.tsx (new), app/src/pages/Dashboard.tsx.

    Approach: read the resident's own eval status from the existing eval surface (resolve the exact

    endpoint during implementation — /api/v1/eval-tracker is admin-gated, so a resident-scoped read is

    needed). Empty state must be honest ("no evaluations due").

    Test seam: card renders from a fixture; no embedded names.

    Verification: a resident sees due/completed counts that match MyEval; numbers derive from the DB

    at render time, never hardcoded.

  • [ ] Unit 7: Surface the hidden accounts
  • Goal: G7, G8 — nobody is invisible.

    Files: app/src/pages/admin/ users tab; app/src/pages/Admin.tsx as needed.

    Approach: show manager_scope + effective flags (row ?? default) in the users grid; list

    access rows with no CRM contact instead of silently dropping them.

    Verification: the six contact-less emails appear with their flags; an admin can see why each

    person has what they have without reading SQL.

  • [ ] Unit 8: Server guards for the new scopes
  • Goal: the API never grants what the nav hides.

    Files: server/routes/admin.ts (add requireOrManager / requireExec next to requireFinance),

    server/routes/or-schedule.ts, server/routes/roster.ts.

    Approach: only add a guard where a scoped role needs a read the current guard denies; leave every

    admin-only route admin-only.

    Verification: the 2×2 proof per role — scoped endpoint answers 200 for the scoped role, admin-only

    endpoints 403, plus a control request that is not 403.

  • [ ] Unit 9: Verification pass (nav + API + view-as) per role
  • Goal: evidence, not assertions.

    Files: extend /workspace/briefs/verify-finance-officer.py pattern to a per-role script.

    Approach: for each of Nancy / Kelly / Crystal / a manager / a resident / a faculty / a staff:

    rendered nav, dashboard variant, and the API matrix. Use the admin view-as rail for read-only

    previews (admin must hold the designation being previewed).

    Verification: one script, all roles, all green, output pasted into the handoff.

    Worked example (what "done" looks like for Nancy)

  • Login → lands on OR board: this week's OR slate by day, block utilisation vs allocated,
  • uncovered/open cases, pool depth. Not the staff-ops page.

  • Sidebar: Dashboard · OR Schedule · Block Usage · Pool · Coverage Center · Activity History · Reimbursement.
  • She can still do everything she does today (schedule view, coverage, her own claims) — nothing removed.
  • Curling /api/v1/admin/users as her returns 403; the OR reads return 200.
  • Risks & Dependencies

    RiskMitigation
    R-1 Editing the stale mirror ships a regression of the shipped finance workUnit 0 re-baselines first; md5-verify after every deploy
    R-2 Removing can_manage_evals from faculty breaks evaluation *submission*Verify /eval/:token is independent of canManageEvals before Unit 3; if faculty need the admin surface, narrow only /eval-admin and /eoy-admin instead
    R-3 A new flag that isn't in all four places fails tsc and blocks the buildUnit 2 checklist; build in container before restart
    R-4 role_defaults change needs a container restart; app_access row changes do notRestart after migrations; verify /api/me per role
    R-5 Scope creep into a dashboard builderScope boundary above; Ponytail ruling below
    R-6 Naming the staff-schedule / faculty-schedule managers is an owner decisionU4 ships the capability; assigning people is a data change the owner can make in /admin

    Dependencies: Unit 0 before all; U1 before U2/U3; U2 before U4/U5; U8 after U4 (needs to know the

    visible surfaces); U9 last.

    Ponytail scope ruling

  • Cut: per-user dashboard customisation, a widget/tile layout model, new DB tables (two columns, not
  • a table), a separate OR analytics product, and any change to the /finance Infor portal.

  • Simplified: one manager_scope text column instead of four booleans; one variant selector function
  • instead of branching in JSX; new boards composed from existing cards.

  • Survived: the three real asks — Nancy gets an OR surface, Kelly gets the money view, residents get
  • evaluations on landing — plus the two data integrity fixes (faculty eval over-grant, hidden accounts).

    Open decisions (owner)

  • 1. Kelly: finance-only nav identical to Crystal, or manager nav + a finance section? (Plan assumes the latter — she is the boss, not a bookkeeper.)
  • 2. Evaluation management: stays with all 24 faculty, or narrows to a named eval lead + admin? (Plan assumes narrow; needs the R-2 check.)
  • 3. Names: who are the staff-schedule manager and the faculty-schedule manager? (Plan builds the capability; the owner assigns.)
  • 2 · Ideas (ce-ideate)


    date: 2026-09-30

    project: unified-platform

    total_ideas: 5

    picked_up: 1


    Ideation: Unified Platform — dashboards and access

    Context: the plan docs/plans/2026-09-30-001-feat-role-dashboards-and-access-plan.md covers the asks

    the owner already made. These are the ideas *around* that work — grounded in the live state verified

    2026-09-30 (93 access rows, row ?? role_defaults resolution, five dashboard branches, three nav

    allow-lists).

    Generated Ideas

    1. Access ledger — "who can do what", with provenance

  • Effort: small
  • Benefit: one admin screen: every person × every flag, showing the effective value and whether
  • it came from an explicit app_access row or was inherited from role_defaults. This is exactly

    how the faculty over-grant hid: 24 rows were NULL, so nothing on screen looked wrong while

    can_manage_evals resolved true for every attending. It also directly serves the standing complaint

    about data you can't see the source of.

  • Risk: one more admin surface to keep honest. Mitigated by making it read-only — a view over the
  • two tables, no write path.

  • Critique: the honest version shows provenance, not just the boolean. A ledger that flattens
  • NULL and false is worse than nothing, because it recreates the bug it was built to catch.

  • Status: picked up → part of Unit 7 of the plan
  • Next step: implement directly with the plan
  • 2. Nav as a tested module, with a golden snapshot per role

  • Effort: small
  • Benefit: the nav is currently five allow-lists inside a 718-line component, verified by logging in
  • as people. Move the builders into a pure module and assert a snapshot per role (12 shapes). Then

    "managers must not see evals" is a failing test, not a bug report from a manager who found the

    button.

  • Risk: snapshots churn and get rubber-stamped. Keep them small — item labels only, no icons.
  • Critique: highest ratio of prevented-future-pain to effort in the whole list. The permission work
  • here has repeatedly regressed through exactly the layer this covers.

  • Status: deferred → recommended as the first follow-on after the plan lands
  • Next step: ce-plan (small) or implement directly
  • 3. Role preview in the view-as rail

  • Effort: small–medium
  • Benefit: the view-as rail already renders the platform as any *person*. Add a row of roles so
  • the owner can flip through all twelve dashboards in about a minute without hunting for a representative

    account. The owner judges visuals on the live artifact; this collapses the review loop.

  • Risk: a role preview must not confer a right the admin lacks — narrowToActor already ANDs flags,
  • so this is safe if the preview reuses the same contract.

  • Critique: genuinely useful only if it lands *after* the dashboards differ. Right now most roles
  • render the same page, so it would show nothing. Sequence it after Unit 5.

  • Status: deferred
  • Next step: implement after the dashboards diverge
  • 4. Evaluations at a glance (program-wide completion matrix)

  • Effort: medium
  • Benefit: cycle × person grid — due / submitted / overdue — with the pair rule (faculty–resident
  • max 2×/AY, fresh pairs preferred) visible next to each cell. Eval season is the department's biggest

    recurring deadline and chasing it is manual today.

  • Risk: duplicates /eval-tracker. Extend that page, do not add a sixth eval surface — the
  • codebase already has Evaluations / Evaluation Cycles / EOY / Campaigns and a seventh would be a

    maintenance tax.

  • Critique: worth doing, but only as a summarised view on top of existing data, and only once
  • R-2 (who owns eval management) is settled. Building it first would enshrine the wrong audience.

  • Status: deferred, gated on the R-2 decision
  • Next step: ce-brainstorm once eval ownership is decided
  • 5. "Since you were last here" delta on the landing page

  • Effort: medium
  • Benefit: the hero band already knows today's state. Add the *change*: "2 swaps requested · 1
  • coverage gap opened · 3 claims awaiting you · 1 eval due". This is what makes a dashboard worth a

    second look — the owner's own test for a good page — and it makes daily login pay off for the

    forty people who currently have no reason to return.

  • Risk: noise. Needs a per-person watermark (last seen) or it repeats the same numbers forever.
  • Also needs a decision on what counts as notable per role, or it becomes a second activity feed.

  • Critique: the strongest *product* idea here and the riskiest. Do it after the role split, because
  • "notable" differs per role — an OR manager and a resident care about different deltas.

  • Status: deferred
  • Next step: ce-brainstorm after Unit 5
  • Rejected Ideas (for reference)

  • Per-user dashboard customisation / drag-and-drop tiles: a layout engine, a preferences table and a
  • support surface, to solve a problem nobody reported. The role split is the fix.

  • A dedicated OR analytics product (separate app, own DB views): the OR data already exists in the OR
  • pipeline and the roster studio. Nancy needs a page, not a product.

  • Bringing the /finance Infor portal into the nav: its own header documents the off-menu decision
  • as deliberate — the department is not running it yet. Revisit when the Infor export lands.

  • Explicit "no access yet" page for archived/student accounts: tempting for tidiness, but it is
  • cosmetic; the catch-all already renders something harmless.

  • Emailing role digests: email is halted stack-wide and this is a dashboard task.
  • Picked-Up Summary

  • 1 idea picked up and folded into the plan (access ledger → Unit 7)
  • 4 deferred with explicit gates: nav snapshot tests, role preview rail, eval completion matrix
  • (gated on eval-ownership decision), landing-page delta feed

  • 5 rejected as over-scope for the current focus