From e368b4f73e6ffdf6d538fb1fc5c6a7317492965a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 9 Aug 2026 11:53:21 +0000 Subject: [PATCH 1/2] Make workspace typecheck green and gate it in CI Fix residual @prm/api TypeScript errors (ReviewDTO mapping, logActivity null ids, director briefing narrowing, P1 assignmentFor helper) and run `pnpm -r typecheck` before tests on every PR to main. Co-authored-by: Damon --- .github/workflows/ci.yml | 1 + AGENTS.md | 2 +- apps/api/src/index.ts | 21 +++++++++++++-------- apps/api/src/store.ts | 7 ++++++- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63bd285..55affc0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,7 @@ jobs: cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm rebuild better-sqlite3 + - run: pnpm -r typecheck - run: pnpm test desktop: diff --git a/AGENTS.md b/AGENTS.md index 1877ba7..6c16946 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,7 +21,7 @@ Performance Review Manager — a local, single-user ("solo EM") pnpm monorepo. S - `.session` stores the auth token only (not the AES key). After an API process restart you must unlock again even if the UI still has a token. ### Known pre-existing issues (not environment problems) -- `pnpm build` (production) and `pnpm -r typecheck` may still fail on residual TypeScript strictness issues in the UI/API; Dev servers (`tsx`/`vite`) run fine because they do not strict-typecheck. +- `pnpm build` (production) may still fail on residual packaging issues; prefer `pnpm -r typecheck` + `pnpm test` as the merge gate. Dev servers (`tsx`/`vite`) run fine because they do not strict-typecheck. - Core loop evaluation & capture gaps: `docs/CORE_LOOP_EVAL.md`. ### Cursor MCP (`@prm/mcp`) diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 62f622c..89ab93a 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -50,6 +50,7 @@ import type { IntegrationSyncResult, PersonExternalLinkDTO, TeamPerformanceDTO, + ReviewDTO, } from "@prm/shared"; import { COMPETENCY_CATALOG, MIDDLE_BAND, competencyLabel } from "@prm/shared"; import { seedWorkspace } from "./seed.js"; @@ -1395,10 +1396,10 @@ app.get("/api/packets/:cycleId/:personId", (c) => { const currentRole = loadRole(participant?.roleDefinitionId ?? a?.roleDefinitionId); const nextRole = loadRole(participant?.targetNextRoleDefinitionId ?? a?.targetNextRoleDefinitionId); const revs = db.select().from(reviews).where(and(eq(reviews.cycleId, cycleId), eq(reviews.subjectPersonId, personId))).all(); - const mapRev = (r: typeof reviews.$inferSelect) => ({ + const mapRev = (r: typeof reviews.$inferSelect): ReviewDTO => ({ id: r.id, - type: r.type as PacketDTO["selfReview"] extends infer T ? NonNullable["type"] : never, - status: r.status as PacketDTO["managerReview"] extends infer T ? NonNullable["status"] : never, + type: r.type as ReviewDTO["type"], + status: r.status as ReviewDTO["status"], subjectPersonId: r.subjectPersonId, authorName: r.authorName, payload: JSON.parse(r.payloadJson) as Record, @@ -4375,15 +4376,14 @@ app.post("/api/exports/readonly-bundle", async (c) => { }; if (include.has("director_briefing")) { - parts.directorBriefing = buildDirectorBriefing(db, { + const briefing = buildDirectorBriefing(db, { cycleId: cycle.id, personId: body.personId, loadRole, assignmentFor, }); - parts.directorBriefingHtml = parts.directorBriefing - ? renderDirectorBriefingHtml(parts.directorBriefing as NonNullable) - : null; + parts.directorBriefing = briefing; + parts.directorBriefingHtml = briefing ? renderDirectorBriefingHtml(briefing) : null; } if (include.has("calib_room")) { // Reuse JSON shape from calib-room endpoint logic via briefing distribution + consistency-like cards @@ -4429,7 +4429,12 @@ app.post("/api/exports/readonly-bundle", async (c) => { registerP1Routes(app, { loadRole, - assignmentFor, + assignmentFor: (personId) => { + const a = assignmentFor(personId); + return a + ? { roleDefinitionId: a.roleDefinitionId, frameworkVersionId: a.frameworkVersionId } + : null; + }, getPublishedVersion: () => { const v = getPublishedVersion(); if (!v) return null; diff --git a/apps/api/src/store.ts b/apps/api/src/store.ts index f939a87..98efd60 100644 --- a/apps/api/src/store.ts +++ b/apps/api/src/store.ts @@ -596,7 +596,12 @@ export function checksum(payload: string | Buffer) { return createHash("sha256").update(payload).digest("hex"); } -export function logActivity(action: string, entityType?: string, entityId?: string, detail: Record = {}) { +export function logActivity( + action: string, + entityType?: string | null, + entityId?: string | null, + detail: Record = {}, +) { const db = getDb(); const ws = db.select().from(workspace).limit(1).all()[0]; const prevHash = ws?.lastActivityHash ?? "genesis"; From 58a28bea16132a328c6de455335718f5627d1498 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 9 Aug 2026 11:53:31 +0000 Subject: [PATCH 2/2] Document typecheck as a CI merge requirement Co-authored-by: Damon --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48732a3..7906ce2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,12 +32,12 @@ Product context lives under `docs/`. Cloud-agent notes: `AGENTS.md`. ## Before you open a PR 1. Keep diffs surgical — touch only what the change needs -2. Run `pnpm test` (API unit tests; this is what CI gates on `main`) +2. Run `pnpm -r typecheck` and `pnpm test` (CI gates both on `main`) 3. For API behavior changes, add or extend tests under `apps/api/src/*.test.ts` 4. Do not commit `data/`, `.env`, secrets, or real PII 5. Align docs with shipped behavior (prefer honesty over aspirational claims) -`pnpm -r typecheck` / production `pnpm build` may still fail on residual strictness issues; prefer fixing types you touch rather than widening `any`. +Production `pnpm build` may still have packaging rough edges; typecheck + tests are the merge bar. ## Desktop