diff --git a/README.md b/README.md index 1223113..b8ab26c 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,40 @@ Local **solo EM workbench** for evidence-backed performance reviews and promotion cases. -ICs never log in. Self/peer input arrives as JSON import bundles. Your vault stays on disk — **Lock & encrypt** seals SQLite + files at rest. +ICs never log in. Self and peer input arrives as JSON import bundles. Your vault stays on disk: **Lock & encrypt** seals SQLite and dossier files at rest.

Command center

+## Why this exists + +Most review tools are either a shared HR portal (everyone logs in) or a blank Google Doc. This app is for one engineering manager who wants: + +1. **A year-round dossier** so writing season is not a scavenger hunt +2. **Role frameworks as the bar** so ratings map to expectations, not vibes +3. **Evidence-linked drafts** so extreme scores and promo claims stay grounded +4. **Local custody** so private notes and underperformance plans stay on your machine until you export + +The product is intentionally single-user. Peers and ICs participate by filling exported forms that you import back. That keeps anonymity and trust simple for v1. + +## Core loop + +``` +upload role R&R → assign people → capture dossier evidence + → open a cycle → import self/peer bundles → writing desk (+ AI) + → consistency + team performance check → share export → history / promo packets +``` + +| Stage | Intent | +|-------|--------| +| Roles + people | Define what "good" means before you score anyone | +| Dossier | Accumulate achievements, goals, feedback, docs, and concerns all year | +| Cycle + bundles | Time-box the review without building a multi-user portal | +| Writing desk | Draft, cite, lint, finalize, then share a subject packet | +| Consistency / team performance | Catch clustering, thin evidence, and odd rating shapes before you ship | +| Promotions | Reuse the same role bar for next-level / promo cases | + ## Quick start ```bash @@ -24,6 +52,8 @@ pnpm dev First load: **Create & seed demo team** (8-person org), then unlock. Between sessions use **Lock & encrypt**. +Requires **pnpm 10.33.3** (see [`AGENTS.md`](./AGENTS.md) if Corepack hands you pnpm 11). + ### Desktop (Tauri) ```bash @@ -31,11 +61,11 @@ pnpm desktop:dev # native window (needs WebView + Rust) pnpm desktop:build ``` -See [`apps/desktop/README.md`](./apps/desktop/README.md). +Packaged builds on `main` CI upload installers as Actions artifacts (`desktop-windows-x86_64`, `desktop-macos-aarch64`, `desktop-linux-x86_64`). Details: [`apps/desktop/README.md`](./apps/desktop/README.md). ### Cursor MCP (bulk load) -`@prm/mcp` unlocks the vault and writes dossier evidence — pair with Jira/Google MCPs for cold-start backfill. +`@prm/mcp` unlocks the vault and writes dossier evidence. Pair it with Jira, Bitbucket, or other MCPs when you need a cold-start backfill instead of typing every note by hand. ```bash pnpm mcp @@ -51,49 +81,65 @@ Regenerate with `pnpm screenshots` (UI + API running; default password `workbenc ### Unlock -Encrypted vault entry. Seed password `workbench`. +**Intent:** Keep the workspace sealed when you walk away. The unlock gate is the trust boundary for local data. + +**How it works:** On first run you create a workspace password and can seed a demo org. Later sessions decrypt the vault with that password. **Lock & encrypt** re-seals SQLite and files so a stolen laptop disk is not a readable review archive. ![Unlock workbench](./docs/screenshots/01-unlock.png) -### Home — command center +### Home (command center) + +**Intent:** Answer "who is blocked, and what should I do next?" without hunting through every page. -Who to unblock next: roles, self bundles, then writing. +**How it works:** Pick a cycle to see pipeline status (self / peer / manager), deadline nudges for missing self reviews, and a compact cycle dashboard (writing time, evidence coverage, thin dossiers). Links out to writing desks, backfill, and team performance when the numbers look thin. ![Command center](./docs/screenshots/02-home.png) ### Team -Directs roster. Open a dossier to capture year-round evidence. +**Intent:** Maintain the roster of directs you actually manage reviews for. + +**How it works:** List, add, or CSV-import people, then open a person to work their dossier. This is the directory, not the evaluation surface. Role assignment and cycle participation hang off these records. ![Team](./docs/screenshots/03-team.png) ### Person dossier -Achievements, goals, feedback, and documents — the citation pool for writing. +**Intent:** Build a citation pool all year so the writing desk is not empty in week one of reviews. + +**How it works:** Capture achievements, goals, feedback (with optional 1:1 theme tags), documents, concern / underperformance notes, and external links (for example Jira or Bitbucket sync). Visibility controls keep EM-only material out of subject exports. AI digests and drafts pull from this pool (and from concerns only when you opt them in). ![Person dossier](./docs/screenshots/04-person.png) ### Roles -Assign current (+ next) RoleDefinitions — the evaluation bar. +**Intent:** Make the evaluation bar explicit. Ratings should map to role expectations, not a private mental model. + +**How it works:** Author or import RoleDefinitions (ladder levels, competency keys, expectations). Assign each person a current role and optionally a next role for promo / gap work. Uploaded R&R docs can be structured manually or with AI extract. Writing desks and promotion packets read from these definitions. ![Roles](./docs/screenshots/05-roles.png) ### Cycles -Export/import self & peer JSON bundles outside the app. +**Intent:** Time-box a review season and collect self / peer input without live IC login. + +**How it works:** Create a cycle with a window and soft phases. Export JSON bundles for self and peer forms, collect them outside the app, then import responses. Waive with a reason when someone is not returning a form. Participants snapshot who was on the team for that cycle. ![Cycles](./docs/screenshots/06-cycles.png) ### Writing desk -Competency scores + evidence links → bias lint → finalize (citation snapshots) → share HTML/PDF. +**Intent:** Produce an evidence-backed manager review in a bounded amount of time (target ≤45 minutes per person). + +**How it works:** Score competencies and overall against the assigned role, link dossier evidence to non-middle scores, draft narrative sections (optionally with AI from an allowlisted citation set), run bias / faithfulness lint, log decision reasons when ratings move, then finalize. Finalize freezes citation snapshots. Share exports HTML (print to PDF) with private notes and EM-only sources stripped. ![Writing desk](./docs/screenshots/07-writing-desk.png) ### Promotions -Next-role packets for skip-level / promo cases. +**Intent:** Reuse the same role bar for next-level and promotion cases instead of a parallel doc pile. + +**How it works:** Open a promotion packet against a target RoleDefinition. Mark gaps / demonstrating signals, draft the case (optionally with AI), and keep it alongside the cycle review history so skip-level conversations stay evidence-backed. ![Promotions](./docs/screenshots/08-promotions.png) @@ -101,41 +147,59 @@ Next-role packets for skip-level / promo cases. ### Consistency -Rating distribution and side-by-side drafts before you finalize. +**Intent:** Catch unfair shape in your own drafts before anyone else sees them (everyone a 4, extreme scores with no cites, missing roles). + +**How it works:** For a selected cycle, show overall rating distribution and a card per person with competencies, evidence link counts, and flags (thin evidence, extreme overall, unassigned role). Jump into each writing desk to fix gaps. This is single-EM calibration, not a multi-manager room. ![Consistency](./docs/screenshots/10-consistency.png) +### Team performance + +**Intent:** See the team's rating and coaching shape as graphs, not only as a queue of drafts. + +**How it works:** Cycle-scoped charts for overall distribution, evidence vs overall, competency averages, promo readiness, writing-time buckets, level mix, and top 1:1 themes. The people table adds competency sparklines and prior-cycle overall deltas when history exists. Use it with Consistency: graphs for pattern, cards for per-person fixes. + +![Team performance](./docs/screenshots/11-team-performance.png) + ### Backfill -Fill thin dossiers (or pair with MCP bulk load) so cold-start writing isn’t empty. +**Intent:** Unblock writing when dossiers are still empty after you first adopt the tool. -![Backfill](./docs/screenshots/11-backfill.png) +**How it works:** Lists people below the thin-dossier threshold (few achievements / feedback / docs / goals). Walk a short wizard to add starter evidence, or use MCP / integrations for bulk load. The goal is a usable citation pool, not perfect history on day one. + +![Backfill](./docs/screenshots/12-backfill.png) ### AI Chat -Grounded Q&A over workspace/dossier data — cites ids, doesn’t invent evidence. +**Intent:** Ask grounded questions over the workspace or a single dossier without inventing evidence. + +**How it works:** Chat is scoped to workspace or person context, cites allowlisted ids from real dossier rows, and respects private-tier egress settings. It is for synthesis and retrieval, not a substitute for the writing desk finalize path. -![AI Chat](./docs/screenshots/12-chat.png) +![AI Chat](./docs/screenshots/13-chat.png) ### Settings -AI provider, egress gates (including document extracts), theme. +**Intent:** Configure providers, privacy gates, appearance, and vault maintenance in one place. + +**How it works:** Set AI provider and models (Anthropic, OpenAI, Cursor gateway, Ollama), toggle egress for private notes / meetings / document extracts, manage Jira and Bitbucket credentials, run encrypted backup / restore, verify the activity log, and switch dark / light theme. -![Settings](./docs/screenshots/13-settings.png) +![Settings](./docs/screenshots/14-settings.png) --- ## What’s in this slice -- **Encrypted vault** (AES-256-GCM) for DB + dossier files on lock -- **People / roles / cycles** with self & peer JSON bundles -- **Writing desk** — competencies, evidence links, citation freeze, HTML/PDF subject packets -- **Consistency** view + **bias/tone lint** before share -- **Promotions** against next-role expectations -- **Backfill** wizard + MCP bulk load for thin dossiers -- **AI** — digests, section draft, peer synthesize, chat (Anthropic / OpenAI / Ollama) with citation allowlist -- **Dark / light** theme preference +- **Encrypted vault** (AES-256-GCM) for DB and dossier files on lock +- **People / roles / cycles** with self and peer JSON bundles +- **Writing desk**: competencies, evidence links, citation freeze, HTML subject packets +- **Consistency** and **team performance** views before you share +- **Bias / faithfulness lint** (advisory) on drafts +- **Promotions** against next-role expectations +- **Backfill** wizard plus MCP / Jira / Bitbucket paths for thin dossiers +- **AI**: digests, section draft, peer synthesize, chat, with citation allowlist +- **Desktop** packaging (Tauri) with CI installers on `main` +- **Dark / light** theme preference ## Docs -Product & architecture under [`/docs`](./docs). Core loop evaluation: [`docs/CORE_LOOP_EVAL.md`](./docs/CORE_LOOP_EVAL.md). Cursor cloud notes: [`AGENTS.md`](./AGENTS.md). +Product and architecture live under [`/docs`](./docs). Start with [`docs/PRODUCT_BRIEF.md`](./docs/PRODUCT_BRIEF.md). Core loop evaluation: [`docs/CORE_LOOP_EVAL.md`](./docs/CORE_LOOP_EVAL.md). Cursor cloud notes: [`AGENTS.md`](./AGENTS.md). diff --git a/docs/screenshots/02-home.png b/docs/screenshots/02-home.png index 1c7080d..36d3136 100644 Binary files a/docs/screenshots/02-home.png and b/docs/screenshots/02-home.png differ diff --git a/docs/screenshots/03-team.png b/docs/screenshots/03-team.png index c7b5c97..3a7165d 100644 Binary files a/docs/screenshots/03-team.png and b/docs/screenshots/03-team.png differ diff --git a/docs/screenshots/04-person.png b/docs/screenshots/04-person.png index 03304b7..d218a3f 100644 Binary files a/docs/screenshots/04-person.png and b/docs/screenshots/04-person.png differ diff --git a/docs/screenshots/05-roles.png b/docs/screenshots/05-roles.png index 88c80d4..64335ef 100644 Binary files a/docs/screenshots/05-roles.png and b/docs/screenshots/05-roles.png differ diff --git a/docs/screenshots/06-cycles.png b/docs/screenshots/06-cycles.png index 8bf0e18..ae91fe5 100644 Binary files a/docs/screenshots/06-cycles.png and b/docs/screenshots/06-cycles.png differ diff --git a/docs/screenshots/07-writing-desk.png b/docs/screenshots/07-writing-desk.png index 990fa53..90a87dd 100644 Binary files a/docs/screenshots/07-writing-desk.png and b/docs/screenshots/07-writing-desk.png differ diff --git a/docs/screenshots/08-promotions.png b/docs/screenshots/08-promotions.png index 8e6f687..71915cf 100644 Binary files a/docs/screenshots/08-promotions.png and b/docs/screenshots/08-promotions.png differ diff --git a/docs/screenshots/09-promotion-detail.png b/docs/screenshots/09-promotion-detail.png index 3b7489a..b705b12 100644 Binary files a/docs/screenshots/09-promotion-detail.png and b/docs/screenshots/09-promotion-detail.png differ diff --git a/docs/screenshots/10-consistency.png b/docs/screenshots/10-consistency.png index 2781d6a..8205457 100644 Binary files a/docs/screenshots/10-consistency.png and b/docs/screenshots/10-consistency.png differ diff --git a/docs/screenshots/11-backfill.png b/docs/screenshots/11-backfill.png deleted file mode 100644 index 98b012c..0000000 Binary files a/docs/screenshots/11-backfill.png and /dev/null differ diff --git a/docs/screenshots/11-team-performance.png b/docs/screenshots/11-team-performance.png new file mode 100644 index 0000000..d423408 Binary files /dev/null and b/docs/screenshots/11-team-performance.png differ diff --git a/docs/screenshots/12-backfill.png b/docs/screenshots/12-backfill.png new file mode 100644 index 0000000..2e75724 Binary files /dev/null and b/docs/screenshots/12-backfill.png differ diff --git a/docs/screenshots/12-chat.png b/docs/screenshots/13-chat.png similarity index 54% rename from docs/screenshots/12-chat.png rename to docs/screenshots/13-chat.png index cae09f3..71cb8b0 100644 Binary files a/docs/screenshots/12-chat.png and b/docs/screenshots/13-chat.png differ diff --git a/docs/screenshots/13-settings.png b/docs/screenshots/13-settings.png deleted file mode 100644 index f5bda82..0000000 Binary files a/docs/screenshots/13-settings.png and /dev/null differ diff --git a/docs/screenshots/14-settings.png b/docs/screenshots/14-settings.png new file mode 100644 index 0000000..1f75e0b Binary files /dev/null and b/docs/screenshots/14-settings.png differ diff --git a/docs/screenshots/README.md b/docs/screenshots/README.md index 133ed06..0ba15b0 100644 --- a/docs/screenshots/README.md +++ b/docs/screenshots/README.md @@ -10,3 +10,5 @@ pnpm screenshots ``` Requires system Chrome (`google-chrome-stable`) and root `puppeteer-core`. + +Numbering matches the README Screens section (`01` unlock through `14` settings), including `11-team-performance`. diff --git a/scripts/capture-screenshots.mjs b/scripts/capture-screenshots.mjs index 8482b67..3212cd4 100644 --- a/scripts/capture-screenshots.mjs +++ b/scripts/capture-screenshots.mjs @@ -1,14 +1,15 @@ #!/usr/bin/env node /** - * One-off gallery capture for README. Usage: - * node scripts/capture-screenshots.mjs - * Expects UI on localhost:5175 (or UI_URL) and API unlocked via password. + * Gallery capture for README. Usage: + * pnpm screenshots + * UI_URL=http://localhost:5173 PRM_PASSWORD=workbench pnpm screenshots + * Expects UI + API running (defaults: UI :5173, API :8787). */ import fs from "node:fs"; import path from "node:path"; import puppeteer from "puppeteer-core"; -const UI = process.env.UI_URL || "http://localhost:5175"; +const UI = process.env.UI_URL || "http://localhost:5173"; const API = process.env.API_URL || "http://127.0.0.1:8787"; const PASSWORD = process.env.PRM_PASSWORD || "workbench"; const OUT = path.resolve("docs/screenshots"); @@ -87,9 +88,10 @@ async function main() { ["07-writing-desk", `/cycles/${cycle.id}/write/${person.id}`], ["08-promotions", "/promotions"], ["10-consistency", "/consistency"], - ["11-backfill", "/backfill"], - ["12-chat", "/chat"], - ["13-settings", "/settings"], + ["11-team-performance", "/team-performance"], + ["12-backfill", "/backfill"], + ["13-chat", "/chat"], + ["14-settings", "/settings"], ]; if (promo?.id) routes.splice(6, 0, ["09-promotion-detail", `/promotions/${promo.id}`]); @@ -102,6 +104,9 @@ async function main() { document.querySelector(".chat-composer")?.scrollIntoView({ block: "end" }); }); } + if (route === "/team-performance") { + await page.waitForSelector(".tp-metrics, .tp-grid, h2", { timeout: 15000 }); + } await new Promise((r) => setTimeout(r, 450)); await shot(page, name); }