# Workspace Page Blueprint

How to add a custom page to the workspace using the operator extension system. This blueprint covers the full lifecycle from project creation through to a pull request.

This is the **extension-page** blueprint — it never touches core. If you are adding a page that belongs in core itself (because every deployed PA should have it), follow the same steps but target `core/tools/workspace/app/` + `core/tools/workspace/nav.mjs` instead of `operator/extensions/workspace/`, and add a test alongside `core/tools/workspace/regression.test.mjs`.


## What it does

Creates a new workspace page that displays data in the workspace UI. The page is added via the extension mechanism — no core files are touched. Includes an API endpoint for dynamic data, an HTML page following the workspace design system, navigation registration, Playwright verification, and a clean PR.


## When to use

- The operator asks for a new workspace page, dashboard, or view
- The operator asks to display information in the workspace
- Any request that maps to "show me X in the workspace"


## Prerequisites

- Workspace server is running (`node core/tools/workspace/ws.mjs status`)
- The data source is identified (existing tool, API, OS data, or project data)
- The `operator/extensions/workspace/` directory exists


## Pre-flight

Before starting, check for leftover state from a previous attempt:

1. If `operator/extensions/workspace/{name}.html` exists, delete it
2. If `operator/extensions/workspace/api.mjs` has routes for `/__api/{name}`, remove them
3. If `operator/extensions/workspace/nav.json` has an entry with `"id": "{name}"`, remove it
4. If `projects/{page-name}/` exists, delete the directory
5. Delete any work items associated with the previous attempt
6. Restart workspace if any extension files were removed: `node core/tools/workspace/ws.mjs restart --no-open`


## Architecture

The workspace extension system is convention-based:

| File | Purpose |
|------|---------|
| `operator/extensions/workspace/{name}.html` | Page HTML (served at `/{name}.html`) |
| `operator/extensions/workspace/api.mjs` | API routes (export `register(addRoute)`) |
| `operator/extensions/workspace/nav.json` | Navigation entries (merged with core nav) |

Extension files take precedence over core files. The server auto-reloads on file changes — no restart needed for content updates, only for `api.mjs` changes.

### API handler signature

```javascript
function handler(req, res, url, repoRoot) {
  const body = JSON.stringify(data);
  res.writeHead(200, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) });
  res.end(body);
}
```

### Nav entry schema

```json
{
  "id": "unique-id",
  "label": "Display Label",
  "href": "/{name}.html",
  "section": "Extensions"
}
```

### Design system

Pages use the workspace dark theme. Key CSS variables:

```
--bg: #1a1b26  --bg-surface: #1f2335  --bg-highlight: #292e42
--border: #3b4261  --text: #c0caf5  --text-dim: #565f89
--text-bright: #e0e6ff  --accent: #7aa2f7  --green: #9ece6a
--yellow: #e0af68  --red: #f7768e  --purple: #bb9af7
--cyan: #7dcfff  --orange: #ff9e64
```

Common patterns: `.card` for panels, `.stat-row` for label/value pairs, `.bar-container`/`.bar-fill` for progress bars, `.grid` for responsive two-column layout. Use `esc()` for XSS prevention. Use `cache: 'no-store'` on fetch calls.


## Steps

### Step 1 — Create project

Create a project to track this work:

```
projects/{page-name}/project.json
```

Fields: `id` = page name slug, `category` = `"feature"`, `status` = `"planned"`, `priority` = `1`, `lead` = the PA's name, `owner` = the operator's name. Summary should describe what the page displays and why.


### Step 2 — Research data source

Before building, identify the data:

1. Check if an existing tool, report, or API already produces the required data
2. Check `operator/extensions/tools/` for relevant tools
3. Check `projects/*/reports/` for existing reports
4. Determine what data structure the page needs
5. Note any data that requires shell commands, file reads, or external calls

Create a work item: `"Research data sources for {page-name} page"` — status `in-progress`.

Output: a clear list of data fields, their sources, and the JSON structure the API will return.

Mark the work item `done` and set `completedAt` when research is complete.


### Step 3 — Plan implementation

Create work items for the remaining steps. All work items should have `projectId` set to the project ID and `owner`/`assignee` set appropriately.

Typical breakdown:

| # | Work item | Assignee |
|---|-----------|----------|
| 1 | Implement API endpoint | tool-developer (via delegation) |
| 2 | Implement HTML page | tool-developer (via delegation) |
| 3 | Register navigation entry | PA |
| 4 | Test page with Playwright | PA |
| 5 | Create PR | PA |

Update project status to `in-progress`.

Create a feature branch before beginning implementation:

```bash
git checkout -b feature/{name}-page
```

All implementation work happens on this branch.


### Step 4 — Implement API endpoint

**Delegate to tool-developer role** using the 8-section briefing contract (Goal, Trace, Context, Tools and sources, Boundaries, Output format, Done when, Escalation rule — use these exact headings).

The API endpoint goes in `operator/extensions/workspace/api.mjs`. If this file already exists (other extension pages), the worker must ADD the new route to the existing `register()` function — not replace the file.

Key requirements for the brief:
- Handler must return JSON with proper Content-Type and Content-Length headers
- Handler must catch errors and return `{ error: message }` with status 500
- Use only Node.js built-in modules (no external dependencies)
- Shell commands via `execSync` must have a timeout (3000ms)
- The response shape must match the data structure from Step 2

Update the work item to `in-progress` before dispatching, `done` with `completedAt` when complete.


### Step 5 — Implement HTML page

**Delegate to tool-developer role** using the 8-section briefing contract. Can be dispatched in parallel with Step 4 if the API contract is defined.

The page goes at `operator/extensions/workspace/{name}.html`.

Key requirements for the brief:
- Standalone HTML file with embedded `<style>` and `<script>` (no external deps)
- Use the workspace design system CSS variables (provided in Architecture section above)
- Fetch data from `/__api/{name}` with `cache: 'no-store'`
- Include an `esc()` function for XSS prevention — all dynamic text must be escaped
- Include loading state and error handling
- Include a refresh button and last-updated timestamp
- Responsive layout: `.grid` with 2 columns on desktop, 1 on mobile
- Use `.card`, `.stat-row`, `.bar-container`/`.bar-fill` patterns as appropriate
- Title format: `{Page Name} — Workspace`

Update the work item to `in-progress` before dispatching, `done` with `completedAt` when complete.


### Step 6 — Register navigation

Create or update `operator/extensions/workspace/nav.json`:

```json
[
  {
    "id": "{name}",
    "label": "{Display Name}",
    "href": "/{name}.html",
    "section": "Extensions"
  }
]
```

If the file already exists, merge the new entry with existing entries (preserve existing, append new).

Update the work item `done` with `completedAt`.


### Step 7 — Restart workspace and verify

```bash
node core/tools/workspace/ws.mjs restart --no-open
```

Verify the server started successfully and the page is accessible:

- Check server status: `node core/tools/workspace/ws.mjs status`
- Verify the API endpoint returns data: `curl -s http://127.0.0.1:{port}/__api/{name} | head -c 200`
- Verify the page is served: `curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:{port}/{name}.html`


### Step 8 — Test with Playwright

Use the Playwright MCP browser tools to verify the page through the full workspace framework:

1. Navigate to `http://127.0.0.1:{port}/core/tools/workspace/app/index.html` (the framework shell, not the bare page)
2. Verify the navigation sidebar contains the new page link (look for the label in the "Extensions" section)
3. Click the navigation link to load the page inside the framework
4. Wait for data to load (the loading message should disappear)
5. Optionally take a screenshot for verification — **do not commit screenshots**; they are ephemeral
6. Check for console errors (ignore favicon 404)
7. Verify key data elements are present on the page

If tests fail, fix the issue and re-verify. Do not proceed until the page renders correctly within the framework.

Update the testing work item `done` with `completedAt`.


### Step 9 — Open for the operator

Open the page in the browser via the full workspace framework (with sidebar and shell), not the bare iframe URL:

```bash
open http://127.0.0.1:{port}/core/tools/workspace/app/index.html
```

Never use the direct page URL (e.g. `/{name}.html`) — always go through the framework shell so navigation and chrome are visible.


### Step 10 — Create PR

1. Stage only the extension files and project/work files (branch was created in Step 3):
   - `operator/extensions/workspace/{name}.html`
   - `operator/extensions/workspace/api.mjs`
   - `operator/extensions/workspace/nav.json`
   - `projects/{name}/project.json`
   - `work/w-*.json` (relevant work items only)
2. Commit with a descriptive message
3. Push and create PR via `gh pr create`
4. Update project status to `done` and set `shipped` date

Update the PR work item `done` with `completedAt`.


## Verification

The blueprint is complete when:

- [ ] Project exists in `projects/` with status `done`
- [ ] All work items have status `done` and `completedAt` set
- [ ] The page loads at `/{name}.html` in the workspace
- [ ] The API returns valid JSON at `/__api/{name}`
- [ ] Navigation shows the new page in the sidebar
- [ ] Playwright verification confirms correct rendering within the framework shell
- [ ] PR is open with all changes
- [ ] No core files were modified


## Rollback

If the blueprint fails mid-execution:

1. Delete the extension files: `{name}.html`, entries from `api.mjs` and `nav.json`
2. Restart workspace: `node core/tools/workspace/ws.mjs restart --no-open`
3. Delete work items and project directory
4. The extension system is stateless — removing files fully reverts the change


## Adapting for core pages

If you are shipping a page as part of core (rather than an operator extension), the structure mirrors this blueprint with three differences:

1. **File locations** — page HTML lives under `core/tools/workspace/app/`, API routes go in `core/tools/workspace/api.mjs`, navigation registration happens in `core/tools/workspace/nav.mjs` (not `nav.json`).
2. **Tests** — core pages must ship a regression test alongside `core/tools/workspace/regression.test.mjs` and a Playwright e2e spec in `core/tools/workspace/e2e/`.
3. **PR target** — PR goes to the core repo (Stravica/dave upstream), not the operator fork. Respect the no-leak rule: zero operator content in a core PR.

Everything else — design system, handler signature, API shape, verification — is identical.
