# Work Board — `<project>`

> **Experimental pilot.** Released in v1.9.0 for explicit project adoption; not a requirement at
> any scope. Its contract may change on pilot evidence. Pin a release and review changes before
> upgrading; no existing project is automatically migrated.
>
> A **living page** of a project's active work, for projects that have no issue tracker or board of
> their own and want one their delivery agent keeps current. It is the standing answer to "what is
> being done, what is waiting, and on what," the way the [Executive Check-in Desk](https://github.com/solidcitizen/velocity/blob/v1.11.0/templates/executive-checkin-desk.md)
> is the standing answer to "what does the operator need to decide or do." The two are one system:
> operator decisions live only on the desk, and the board names them by `CK` id.
>
> **Build it from the data file and the renderer, never by hand.** The page is derived from
> `board.json` by [`render-work-board.py`](https://github.com/solidcitizen/velocity/blob/v1.11.0/templates/render-work-board.py), which validates the file against
> this contract and marks violations in its best-effort view. Start from
> [`work-board.example.json`](https://github.com/solidcitizen/velocity/blob/v1.11.0/templates/work-board.example.json); the field definitions are in
> [`work-board.schema.json`](https://github.com/solidcitizen/velocity/blob/v1.11.0/templates/work-board.schema.json).

## Contract

1. **Stable IDs, never renumbered.** Every item gets `WI-<n>` once: positive integers, unique,
   contiguous from 1, never reused. An item that will not be done is not deleted; it closes as
   **dropped**, with the reason, and keeps its id.
2. **Seven states, and the state says what the item waits on.** What each state means is defined
   once, for every way of keeping work, in the [work-management contract](https://github.com/solidcitizen/velocity/blob/v1.11.0/templates/work-management.md); this
   rule adds what the JSON board requires. *backlog* (wanted, not scheduled),
   *committed* (named for a week or a date), *doing*, *blocked* (waits on a desk ask or another
   item, named in `waits_on`), *held* (waits on an external event or party, named in `waits_on`,
   with a date when one is known), *done* (closed with proof), *dropped* (closed without doing,
   with a reason). The renderer refuses a blocked or held item that does not name what it waits
   on. An undated hold is legitimate: the page marks it "no date" and leaves it out of the
   due-this-week count, and it is the maintainer's job to give it a date or turn it into an
   operator ask when the wait becomes the operator's to end. "Blocked on the operator" and "held
   until Monday's read-back" are an ask and a calendar; the board keeps them apart. A held item's
   `event` names the observable thing that ends the wait and what brings it, such as "the September
   statement downloads; it arrives in the daily control's new-transactions list". It does not
   describe the work that follows; that is the item's title. An item waiting on two events is two
   items.
3. **Work in hand belongs to bounded work.** A *doing* or *done* item names what bounds it: a
   tranche, an issue record, a control document, an ADR, a decision (`CK` id), or an evidence
   folder. A project that adopts the board with history sets `adopted` to the day its discipline
   began; *done* items closed before that date are history and may omit `belongs_to` and proof,
   and the page marks them **operator-reported**. From `adopted` on, the rule holds.
4. **Done cites proof; it does not judge it.** An item reaches *done* only with a proof artifact
   in Velocity's terms: a review pack, a handoff packet, a closeout disposition, a receipt id, or
   a record path. The board shows the citation and nothing more.
5. **Controls recur.** Recurring assurance work (a reconcile, an audit, a periodic package) is a
   *control* with a cadence, its last completion's proof, and `evidence`, the path pattern where
   every completion's proof lives. A control never leaves the board; its next due date is
   computed, an overdue control is marked, a control built but never run reads "due now", and a
   control that is defined but not yet built (`planned: true`) is shown as such and not counted.
6. **Operator decisions never live on the board.** Anything that needs the operator's decision or
   action is a [Check-in Desk](https://github.com/solidcitizen/velocity/blob/v1.11.0/templates/executive-checkin-desk.md) ask. The board item that waits on it is
   *blocked* with `waits_on.ask` naming `<project> CK-n`. The board never carries a lean, a
   needed-by for the operator, or a ruling.
7. **Peer-originated and automated work is marked.** An item that exists because another entity
   asked carries `origin` (their project and their id). `automated` says how an item's normal path
   runs: by software, by an agent on schedule, or when a person triggers it; the page counts each,
   and the software bucket is the measure of a software-owned function. An optional `size`
   (S, M, L) and an optional `benefit` (its kind: risk retired, cost saved, capability gained, or
   obligation met; its scale S, M, L; and one line) let a backlog be ranked by benefit against
   size instead of by order of entry. Within each initiative the backlog orders by benefit, then
   size, then date. Rolling benefit and size up by initiative is the portfolio view, and it belongs
   to the entity layer over this data. **Benefit and size are always shown as the pair, never
   collapsed into a score.** The ordering implies a ratio; a number would hide the judgment that
   produced it, and the operator's judgment stays visible.
8. **The header is computed.** Five totals: in motion (doing + committed), waiting (blocked +
   held), backlog, due this week (needed-by dates, held-until dates, and non-daily control due
   dates inside seven days), and controls built but never run. Below them, open work by
   initiative, work from peers, and work that runs without an agent. Section headings carry
   their counts; Backlog and Done are grouped by initiative. Nobody types any of it.
9. **Republished the same turn** an item is added, moved, closed, or dropped, with the `updated`
   stamp changed in the same edit.
10. **Provenance has a home; unknown fields do not.** `source` holds where an item came from (a
    queue paragraph, a log line) and is kept, not shown. `uncertain` lists the fields the
    maintainer could not confirm, each exactly `field: why` with a real field name, and the page
    tags the item **unconfirmed** naming only the fields, with the reasons on hover. The source
    shows on hover over the id. Any other field is rejected, so a typo cannot pass silently.
11. **A broken file still shows the board.** Validation lists every violation and the renderer
    exits non-zero, but the page is still produced with a **Needs repair** block at the top, so one
    stale field never hides the other fifty items from the operator. `--check` alone reports and
    stops. The maintainer fixes the file, never the page.
12. **One way to build it.** The board is the renderer's output from the project's data file,
    unmodified. A need the data file cannot express is a gap report to Velocity, not a local
    variant.

## Data file and renderer

| Path | Role |
|---|---|
| `templates/work-board.schema.json` | Field definitions for `board.json`. |
| `templates/work-board.example.json` | Placeholder board: copy it, replace every angle-bracket placeholder. |
| `templates/render-work-board.py` | Validates and renders. Python 3.9+, standard library only. |
| `templates/work-board.html` | What the example renders to. Generated; do not edit. |

```
python3 render-work-board.py board.json --check              # validate; exit code 1 lists every violation
python3 render-work-board.py board.json --out board.html     # standalone page for any static host
python3 render-work-board.py board.json --fragment           # title, style, and main only, for artifact hosting
```

Every date and time in the data file is the operator's local time; `tz_label` names the zone.
Write it without a UTC offset. If a value carries one, the renderer reads the time as written
and ignores the offset, so `2026-10-09T17:00-07:00` and `2026-10-09T17:00` show the same on
the page.

Only these vary per project: `project`, `maintainer`, `tz_label`, `desk`, `adopted`, and the color
tokens under `theme` and `theme_dark`.

## Adoption notes

- **One work binding, not the definition of work management.** The development
  [work-management contract](https://github.com/solidcitizen/velocity/blob/v1.11.0/templates/work-management.md) also permits an authoritative Markdown
  TODO or a mapped external tracker. This JSON renderer does not read Markdown. A richer view
  alone is not a reason to migrate a queue. Inherit the project's
  [decision context](https://github.com/solidcitizen/velocity/blob/v1.11.0/templates/decision-levels.md) through its artifact index; use existing
  `initiative` and `belongs_to` references for more specific scope. Do not add undeclared JSON
  fields. Benefit/size and initiative totals support attention; they do not constitute a
  business case, investment authorization, or gate decision. Unsupported management and control
  lifecycle semantics remain in linked authoritative records, not inferred from board totals.

- **Portable project records.** The optional [shared profile](https://github.com/solidcitizen/velocity/blob/v1.11.0/templates/portable-project-records.md)
  adds project-owned discovery, guarded file updates, history, and export. Its
  [file helper](https://github.com/solidcitizen/velocity/blob/v1.11.0/templates/project-records.md) invokes this renderer; it does not reimplement content rules.
  Empty `items` is valid for a new board. Existing TODO imports preserve their source and
  uncertainty. A [tracker binding](https://github.com/solidcitizen/velocity/blob/v1.11.0/templates/tracker-binding-and-handoff.md) supports the project's chosen
  destination; migration retires this file as a writable queue. `--archive-binding <locator>`
  labels a retained HTML snapshot with its successor authority. Staying in file mode is valid.

- **Pinning the pilot.** Adopt a fixed experimental tag or commit using the
  [experimental adoption guide](https://github.com/solidcitizen/velocity/blob/v1.11.0/docs/PROJECT-ADOPTION-GUIDE.md#pin-a-release). Keep the renderer, schema,
  and contract from the same pin, never another project's working tree or a moving branch.
  Review release notes and requalify affected behavior before upgrading; existing pins do not
  change when the experimental branch advances.
- **Consuming projects are told before a change can fail them.** While this template is
  experimental, Velocity notifies each consuming project, before or with the change, when a change
  lands that could fail a board that validated before, naming the commit and the change.
- **A project's wrapper stamps and publishes; it implements nothing.** A project may automate the
  workflow around the board (set `updated`, run the renderer with `--check` and then `--fragment`,
  publish the output unmodified). It may not validate, order, total, or render on its own; any of
  those in project code is the local variant the standard forbids. Pin the renderer from the
  project's Velocity checkout so the board and the standard move together. Refreshing data from
  evidence is stamping, not a local variant: a wrapper may set a control's `last_completed` and
  `proof` from the newest receipt that matches its `evidence` pattern.
- **`automated` is a cost line and a quality profile, not a taxonomy.** The three values are
  three ways the same work gets done, each with its own cost per occurrence and its own likelihood
  of leaving a receipt: software costs cents and leaves a receipt every time; an agent costs
  dollars and leaves one most of the time; a person costs the operator's attention and often
  leaves none. The field is the leading indicator that a control's transition to software has
  finished. The lagging measures, cost and touches per occurrence and correctness read from the
  receipts, belong to the project's cadence reports and to the [Measured Automation
  Pilot](https://github.com/solidcitizen/velocity/blob/v1.11.0/templates/automation-pilot.md) and [Automation Transition Contract](https://github.com/solidcitizen/velocity/blob/v1.11.0/templates/automation-transition-contract.md),
  not to the board.
- **The board is kept by the delivery agent.** Every move is an edit to the data file and a
  republish the same turn. That cost is small for an agent-kept queue and heavy for a person;
  a project whose queue is kept by hand should keep its existing tracker and adopt only the desk.
- **Controls trust `last_completed` as entered, so let the evidence enter it.** The renderer reads
  the data file and nothing else, and an agent-kept board goes stale exactly where the project is
  most automated: a control that runs unattended advances while no agent session is open to record
  it. The structural fix is a control's `evidence` path. The wrapper refreshes `last_completed`
  and `proof` from the newest matching receipt, and the control calls the wrapper as its last
  step. A pre-flight that compares receipts to `last_completed` and refuses to publish on a
  mismatch remains the backstop for a claimed run with no receipt. Both belong to the project,
  because only the project knows its evidence. One limit stays: an unattended run advances the
  data file and the local page, but publishing to the operator's surface is still an agent step.

## Pilot record

The entries below are maintainer-reported experience from private consuming projects. The
projects are not public reference implementations, and the findings are not independently
reproducible evidence.

- 2026-10-04, two weeks in, three boards. The first consuming project grew from 52 items to 102
  (91 work, 11 controls). 49 items closed, 47 of them with proof; the other 2 closed before its
  `adopted` date and show as operator-reported. 10 were dropped with reasons, and 6 controls run
  by software. 20 items still carry `uncertain`, so extraction gaps stay visible instead of being
  guessed away. The second project has 26 items, and all 9 of its closes carry proof. Velocity's
  own board has 42 items, and all 25 of its closes carry proof. The validator caught every
  malformed entry its maintainer wrote. The first project's page showed 29 items in motion against
  5 in backlog, and its maintainer found that the count misread the real load. Several "doing"
  items had finished or been superseded, while a week's substantial new work never reached the
  board, because the agent moving items did not republish in the same turn. The project names that
  its own lapse of rule 9, not a template defect. It groomed the board the same day, using the
  evidence: 2 items closed with proof, 1 dropped as superseded, and 7 added for the week's
  unrecorded work, all done with commit proof. That left 27 in motion, mostly genuinely open or
  awaiting evidence. Its suggestion, held for a later release because it needs a new field: show
  each in-motion item's age in its state, and count in-motion items untouched for more than seven
  days.
- 2026-09-24, Velocity's own board crashed the renderer with one malformed `waits_on`, despite rule
  11's promise. Both renderers now degrade a single bad entry, never the page, and are tested
  against every single-field fault (v1.9.0). The same day, the first project found that a time
  written with a UTC offset crashed the page (fixed in v1.9.1).
- 2026-09-23, first project, an operator reading the page: "held, but not clear what it is waiting
  on." Each `event` had mixed the trigger, the value expected, and the work that would follow.
  Rewritten so the title carries the work and `event` carries only the observable trigger, and
  one item that waited on two events was split in two. Taken into rule 2.
- 2026-09-23, first project: two unattended days of a daily control advanced three controls with
  no agent session open; the wrapper's pre-flight refused to publish the stale board. Fixed in the
  project's wrapper by refreshing control completions from receipts, with the control calling the
  wrapper as its last step. Taken into the adoption notes, with the limit that publishing is
  still an agent step.

- 2026-09-21, second consuming project adopts the pilot (19 items: 15 work, 4 controls) as a
  recorded pin exception. Its first gap report: an external party with no return date. The code
  already allowed a hold without a date; the contract text said "with a date". Text corrected.
- 2026-09-21, from the Operator: "a portfolio view of knowing size of work and benefit of work
  is a generalizable thing that makes sense in the template." `benefit` added beside `size`; the
  backlog orders by the two; the roll-up by initiative is left to the entity layer. First reading
  by the pilot: the ranking put an architecture decision and an audit above a diagram and a
  consolidation, which its flat queue had inverted.
- 2026-09-21, first hours live: `size` added for ranking; `automated` split into software / agent /
  person because the boolean could not show the software-owned measure; the wrapper rule, the
  maintenance-cost note, and the evidence pre-flight moved from messages into the adoption notes.
  Reported by the pilot: the seven states fit finance work without bending; controls that never
  leave the board and the proof-to-close rule with `adopted` forced an honest split between
  evidenced and remembered work.
- 2026-09-21, round 2, same project, live on the data file: ten findings from the rendered page,
  nine taken into the contract the same day (section counts, grouping by initiative, the due-week
  rules and the never-run count, planned controls, dependencies shown from both sides, unproven
  closes shown lighter, peer and automation counts, source on hover, the strict `uncertain` form);
  the tenth, a by-initiative allocation of committed effort, belongs to the entity layer above the
  board. The board surfaced two stale backlog items the flat queue had hidden; they were dropped
  with reasons.
- 2026-09-21, round 1, one consuming project, 52 items (43 work, 9 controls): the first draft
  failed 8 items and refused the page. Findings taken into the contract the same day: `adopted`
  and history closes (rule 3), `evidence` on controls (rule 5), `source` and `uncertain` with
  unknown fields rejected (rule 10), best-effort rendering with a repair block (rule 11), and the
  schema now declares every rule the renderer enforces. Under the revised draft the same data
  validates with two changes on the project's side (set `adopted`; rename its provenance field to
  `source`) and none to its substance.
