# Dev Board System

The `/en-gb/dev/` sub-site renders project-status boards — Kanban workstreams, issue
lists, agent rosters, release logs — entirely client-side from JSON. This guide is the
complete reference for that system: the schemas, how a board is registered and rendered,
and a step-by-step for creating your own, either as a static file or from an encrypted
vault.

It's aimed at any agent or person who wants to create or maintain a board in the dev
dashboard. The most useful parts are the schema reference and the static-versus-vault
decision near the end.

---

## How a board renders

Every board is a nav entry plus a JSON file. The dashboard shell reads the nav, and when
a board is selected it fetches the JSON, picks a renderer by schema, and draws the board —
all in the browser.

```mermaid
flowchart TD
  A[nav.json entry] --> B[sg-side-nav: nav:select]
  B --> C{render == json?}
  C -- "content_object_id: obj-cas-imm-…" --> D[vault client: fetch + AES-GCM decrypt]
  C -- "content_object_id: /path/to.json" --> E[plain fetch, no encryption]
  D --> F[renderJsonBoard by schema]
  E --> F
  F --> G[project-workstreams-v2 → Kanban + drill-down]
  F --> H[project-issues-v1 → card list]
  F --> I[project-agents-v2 → roster]
  F --> J[project-releases-v1 → version groups]
  F --> K[generic fallback → card list]
```

The key branch is in `content_object_id`: a value starting with `obj-cas-imm-` loads from
an encrypted vault; a value starting with `/` is fetched as a plain static file. Same
render path otherwise.

---

## Registering a board in nav

Boards are nav articles in the dev sub-site's `nav.json`. The fields:

| Field | Required | Description |
|---|---|---|
| `title` | yes | Display name in sidebar and breadcrumbs |
| `slug` | yes | URL segment — the board appears at `/en-gb/dev/{slug}/` |
| `render` | yes | Must be `"json"` to trigger the board renderer |
| `schema` | yes | Which renderer to use (see below) |
| `content_object_id` | yes | A vault blob ID (`obj-cas-imm-…`) **or** a root-relative path (`/en-gb/dev/boards/foo.json`) |
| `vault_id` | if encrypted | Vault containing the blob (omit for a static path) |
| `read_key` | if encrypted | The vault's public read key (omit for a static path) |
| `description` | no | One-line description shown in the landing card grid |

A static (repo-stored) board entry looks like this:

```json
{
  "title": "Code Review Items",
  "slug": "code-review",
  "content_object_id": "/en-gb/dev/boards/code-review.json",
  "render": "json",
  "schema": "project-workstreams-v2",
  "description": "10 items from the 2026-06-09 code review."
}
```

An encrypted-vault board adds `vault_id` and `read_key`, and points
`content_object_id` at a blob ID instead of a path.

---

## The schemas

Five schemas, each with its own renderer. Set `schema` at the top level of the JSON.

### project-workstreams-v2 — Kanban (recommended for task tracking)

The most capable schema. Renders a four-column Kanban board; clicking a workstream card
drills into its tasks.

```json
{
  "schema": "project-workstreams-v2",
  "title": "Optional board title",
  "workstreams": [
    {
      "id": "WS-01",
      "title": "Human-readable workstream name",
      "description": "Shown under the title in drill-down.",
      "color": "#6366f1",
      "status": "in-progress",
      "tasks": [
        { "id": "WS-01a", "title": "What needs doing", "status": "done", "owner": "@Dev" }
      ]
    }
  ]
}
```

Workstream `status` maps to a column and badge colour:

| `status` | Column | Badge colour |
|---|---|---|
| `queued` | Queued | slate |
| `next` | Up Next | amber |
| `in-progress` | In Progress | blue |
| `done` | Done | green |

If `status` is omitted, it's derived from the tasks: all tasks done → `done`; any
in-progress → `in-progress`; any next → `next`; otherwise `queued`. An explicit `status`
always wins. A progress bar is computed from `done / total` tasks. The `color` is any CSS
colour string and is used for the card's left stripe, the progress fill, and the
drill-down heading — pick one per area for visual grouping.

### project-issues-v1 — flat issue list

A simple list of issue cards; no columns, no drill-down.

```json
{
  "schema": "project-issues-v1",
  "issues": [
    {
      "id": "ISSUE-007",
      "title": "Short issue title",
      "status": "resolved",
      "priority": "high",
      "owner": "@Dev",
      "description": "Optional detail line."
    }
  ]
}
```

Status values: `open`, `in-progress`, `resolved`, `blocked`. Priority: `high`, `medium`,
`low`.

### project-agents-v2 — agent roster

```json
{
  "schema": "project-agents-v2",
  "agents": [
    {
      "alias": "@Dev",
      "id": "dev.sgraph",
      "session_status": "active",
      "model": "Claude Sonnet",
      "role": "Website code, PRs, QA",
      "location": "the website repo"
    }
  ]
}
```

Session status: `active`, `idle`.

### project-releases-v1 — release log

```json
{
  "schema": "project-releases-v1",
  "releases": [
    {
      "version": "v1.2.0",
      "status": "released",
      "date": "2026-06-06",
      "tasks":  [ { "id": "T-42", "title": "…", "owner": "@Dev" } ],
      "issues": [ { "id": "ISSUE-007", "title": "…" } ]
    }
  ]
}
```

### Generic fallback

If the schema is unknown or omitted, the renderer finds the first top-level array in the
JSON and renders each item as a card from its `title`/`name`/`id`, a `status` badge, and
`description`. Handy for quick prototyping.

---

## Creating a board, step by step

There are two storage options. For most project-tracking boards, the static option is
simpler and equally appropriate.

### Option A — static (repo-stored), no vault access needed

1. Create the JSON file under the dev sub-site's `boards/` folder, e.g.
   `en-gb/dev/boards/my-board.json`, using one of the schemas above.
2. Add a nav entry in the dev `nav.json` with `content_object_id` set to the
   root-relative path (`/en-gb/dev/boards/my-board.json`), `render: "json"`, and the
   matching `schema`.
3. Commit and push. The board is live at `/en-gb/dev/my-board/` — no vault keys required.
4. To update it, edit the JSON and commit. Served with `Cache-Control: no-store`, so
   changes are live as soon as the deploy completes.

### Option B — encrypted vault, requires vault write access

1. Write the JSON into the dev status vault with `sgit`, and note the object ID from the
   commit output.
2. Add a nav entry with that `content_object_id`, plus the `vault_id` and the vault's
   public `read_key`.
3. Commit the nav change and push.
4. To update, push a new version to the vault and update `content_object_id` in nav to the
   new object ID. Because vault objects are content-addressed and immutable, every update
   produces a new ID and so requires a nav commit.

### Which to choose

```comparison
left:
  title: Static (Option A)
  subtitle: Repo-stored JSON
  color: accent
  items:
    - label: Vault write key needed
      value: No
    - label: To update
      value: Edit JSON, commit
    - label: Content visibility
      value: Public JSON served as a file
    - label: Best for
      value: Project tracking, code review, non-sensitive data
right:
  title: Encrypted vault (Option B)
  subtitle: Vault blob + read key
  color: warning
  items:
    - label: Vault write key needed
      value: Yes
    - label: To update
      value: Vault push + nav change
    - label: Content visibility
      value: Public read key for the dev vault — same as static
    - label: Best for
      value: Agent-authored content at the same privacy level
```

For most project-management boards, Option A is the simpler choice — the dev vault's read
key is public anyway, so there's no privacy difference between the two for this sub-site.

---

## Updating item statuses

Column assignment is automatic. For a static board, edit the JSON and commit:

```json
{ "id": "WS-01a", "title": "Vendor a dependency", "status": "done" }
```

A workstream card moves to "Done" once all its tasks are `done`, or to "In Progress" the
moment one task becomes `in-progress`. The typical edit cycle is: edit the board JSON,
commit, push — and the deploy makes it live within about a minute.

---

## Known limitations

A few things to know before you build on this:

- **Boards are read-only in the browser.** Editing means a commit. A future edit mode could
  support authenticated inline editing via a vault write key.
- **Renderer output isn't HTML-escaped.** The card helpers interpolate `title` and
  `description` as raw strings. For static boards you control the data, so it's safe; for
  vault-sourced boards, escape at every interpolation point. (This is a tracked code-review
  item, referenced as CR-01 in the team's internal review backlog — escape before using
  untrusted board data.)
- **No real-time updates.** Boards don't auto-refresh; a reload picks up changes.
- **Schema is self-declared.** The JSON's top-level `schema` overrides the nav entry's
  `schema`, so a board file can change its own renderer by editing that key.

---

This article is published from the team's content vault. The source of truth for the board
system lives in the website repository; if a detail here drifts from the code, the repo
wins — flag it and it'll be corrected at the source.
