diff --git a/.github/workflows/core-ci.yml b/.github/workflows/core-ci.yml
index a6b97acf8..603ceb7e0 100644
--- a/.github/workflows/core-ci.yml
+++ b/.github/workflows/core-ci.yml
@@ -13,9 +13,24 @@ jobs:
- name: Guardrail
run: echo "CORE repo guardrail active"
- lint:
+ test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - name: Lint placeholder
- run: echo "Add lint/test here"
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 10
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: pnpm
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+ - name: Run root tests (server + design tokens)
+ run: pnpm test
+ - name: Run API tests
+ run: pnpm --filter @blackroad/api test
+ - name: Run UI tests
+ run: pnpm --filter @blackroad/ui test
diff --git a/TEST_COVERAGE_ANALYSIS.md b/TEST_COVERAGE_ANALYSIS.md
new file mode 100644
index 000000000..a2cffdd1a
--- /dev/null
+++ b/TEST_COVERAGE_ANALYSIS.md
@@ -0,0 +1,263 @@
+# Test Coverage Analysis
+
+## Current State: No Tests Exist
+
+The BlackRoad monorepo currently has **zero test files, zero test configuration, and zero CI test execution**. No testing framework (Jest, Vitest, Mocha, Testing Library, Playwright, etc.) is installed in any workspace. The CI pipeline (`core-ci.yml`) contains only a placeholder: `echo "Add lint/test here"`.
+
+This means every module described below has **0% test coverage**.
+
+---
+
+## Codebase Inventory
+
+| Module | Location | Language | Lines | Test Files | Coverage |
+|---|---|---|---|---|---|
+| API server | `apps/api/index.js` | JS | 49 | 0 | 0% |
+| Standalone server | `server_full.js` | JS | 44 | 0 | 0% |
+| Homework portal | `apps/homework/pages/index.tsx` | TSX | 63 | 0 | 0% |
+| RoadBook site | `apps/roadbook/pages/*.tsx` | TSX | ~135 | 0 | 0% |
+| UI: Button | `packages/ui/src/components/Button.tsx` | TSX | 76 | 0 | 0% |
+| UI: Input | `packages/ui/src/components/Input.tsx` | TSX | 53 | 0 | 0% |
+| UI: Tabs | `packages/ui/src/components/Tabs.tsx` | TSX | 60 | 0 | 0% |
+| UI: Badge | `packages/ui/src/components/Badge.tsx` | TSX | 61 | 0 | 0% |
+| UI: Card | `packages/ui/src/components/Card.tsx` | TSX | 37 | 0 | 0% |
+| UI: Dialog | `packages/ui/src/components/Dialog.tsx` | TSX | 85 | 0 | 0% |
+| UI: Drawer | `packages/ui/src/components/Drawer.tsx` | TSX | 93 | 0 | 0% |
+| UI: DataTable | `packages/ui/src/components/DataTable.tsx` | TSX | 92 | 0 | 0% |
+| UI: Toast | `packages/ui/src/components/Toast.tsx` | TSX | 78 | 0 | 0% |
+
+---
+
+## Proposed Testing Strategy
+
+### Priority 1 (High) — API Server Unit/Integration Tests
+
+**Why:** The API layer (`apps/api/index.js` and `server_full.js`) is the highest-risk code. It handles user input, manages in-memory state, and bridges to external services. Bugs here affect every consumer.
+
+**Recommended framework:** [Vitest](https://vitest.dev/) + [supertest](https://github.com/ladakh/supertest) for HTTP assertions.
+
+**What to test:**
+
+1. **`GET /api/health`** — returns `{ ok: true }` with status 200.
+2. **`GET /api/homework`** — returns an empty array initially; returns items after POST.
+3. **`POST /api/homework`** — with valid body creates a homework item and returns 201.
+4. **`POST /api/homework`** — with missing `title` returns 400 with error message.
+5. **`POST /api/homework`** — with missing `description` returns 400 with error message.
+6. **`POST /api/homework`** — with empty body returns 400.
+7. **CORS middleware** — `OPTIONS` requests return 200 with correct headers; `Access-Control-Allow-Origin` is `*`.
+8. **`server_full.js` — `GET /api/hello`** — returns expected JSON.
+9. **`server_full.js` — `GET /health`** — returns `{ status: 'ok', service: 'blackroad-api' }`.
+10. **`server_full.js` — `POST /api/llm/chat`** — with missing/empty message returns 400; with valid message and unreachable upstream returns 502.
+
+**Example test file location:** `apps/api/__tests__/api.test.ts`
+
+---
+
+### Priority 2 (High) — UI Component Library Tests
+
+**Why:** The `@blackroad/ui` package is a shared dependency consumed by multiple apps. Regressions here cascade across the entire platform. The components contain variant logic, conditional rendering, keyboard event handling, and timer-based behavior that are all easy to break silently.
+
+**Recommended framework:** Vitest + [@testing-library/react](https://testing-library.com/docs/react-testing-library/intro/) + jsdom.
+
+**What to test per component:**
+
+#### Button (`Button.tsx`)
+- Renders children text.
+- Applies correct CSS classes for each `variant` (primary, secondary, accent, neutral, info, danger, warning, success, outline).
+- Applies correct CSS classes for each `size` (sm, md, lg).
+- Defaults to `variant="primary"` and `size="md"` when no props are passed.
+- Forwards native button attributes (`disabled`, `onClick`, `type`).
+- Merges custom `className` with generated classes.
+
+#### Input (`Input.tsx`)
+- Renders an `` element.
+- Renders a `