# Mirius roadmap export format

This file describes the JSON produced by **Export JSON** on the roadmap admin page. The export is a snapshot of the
planning database plus the schedule the planner computed from it at export time. It is intended to be handed to an
LLM or a script for analysis, so nothing in it needs to be inferred: every field is listed below.

Format identifier: `"format": "mirius-roadmap-export/1"`. If the identifier changes, this document is out of date.

## Conventions

- All dates are ISO `YYYY-MM-DD` strings with no time zone. The team works in the UK; dates are calendar days.
- **Effort is in person-days**: one person working one full working day. Working days are Monday to Friday only.
- A **working day** for a person is a weekday that is not one of the shared `closure_days` and not one of that
  person's own `leave` spans.
- Every date range (`leave`, `closure_days`, `scheduled`) is **inclusive** at both ends. A single day has `start == end`.
- `null` means "not set". Empty string `""` means "set to nothing" for free text fields.
- Integer `id` values are database keys. They are stable within one database but are not meaningful across
  databases. `items[].depends_on` and `items[].person_id` refer to these ids.

## Top level

| Field | Type | Meaning |
|---|---|---|
| `format` | string | Always `mirius-roadmap-export/1`. |
| `format_doc` | string | URL of this document on the server that produced the export. |
| `exported_at` | string | ISO 8601 timestamp (UTC) of when the export was built in the browser. |
| `settings` | object | Global plan settings. See below. |
| `people` | array | The people who do the work, one lane each, with their own leave. |
| `closure_days` | array | Days nobody works (bank holidays, company closures). |
| `milestones` | array | Named dates drawn as lines on the chart. Milestones do not affect scheduling. |
| `items` | array | The work. One row per feature or piece of work. |
| `summary` | object | Totals computed at export time. Derived from `items`; included for convenience. |

## `settings`

| Field | Type | Meaning |
|---|---|---|
| `title` | string | Chart title. |
| `baseline` | date | The day scheduling starts. No open work is scheduled before this date. |
| `target` | date or null | The date promised to stakeholders. Used to show how many person-days fit before it. |
| `jira` | string | Base URL that an item's `epic` key is appended to. May be empty. |

## `people[]`

| Field | Type | Meaning |
|---|---|---|
| `id` | integer | Key referenced by `items[].person_id`. |
| `name` | string | Display name. |
| `days_per_week` | number | How many of the five weekdays this person gives the roadmap. 5 is full time, 2.5 is half time. Range 0.5 to 5. |
| `leave` | array of `{start, end, note}` | This person's own days off, inclusive ranges. On top of `closure_days`. |

People are listed in lane order (the order they appear on the chart).

## `closure_days[]`

`{start, end, note}` inclusive ranges on which nobody works. Bank holidays imported from gov.uk carry a note
ending in `(bank holiday)`.

## `milestones[]`

| Field | Type | Meaning |
|---|---|---|
| `id` | integer | Key. |
| `name` | string | Label drawn on the chart. |
| `date` | date | Where the line is drawn. |
| `colour` | string | Hex colour such as `#EE1952`, or empty string for the default brand blue. |

## `items[]`

Items are in **queue order**: for each person, earlier rows are worked before later rows unless a fixed start or a
dependency says otherwise. `queue_position` is that order, zero based, across all items.

| Field | Type | Meaning |
|---|---|---|
| `id` | integer | Key referenced by other items' `depends_on`. |
| `area` | string | Functional area, for example `Orders`, `Inventory`, `M2M`. Used to group and colour the chart. |
| `feature` | string | Short name of the work. |
| `detail` | string | Longer description. May be empty. |
| `epic` | string | Jira epic key such as `MIR-3020`. Empty if not yet known. |
| `epic_url` | string | `settings.jira` + `epic`. Empty if either is empty. |
| `days_remaining` | integer | **Person-days of effort still to do.** This is the number the scheduler uses. For a delivered item it is what was left when it was marked done, usually 0. |
| `sheet_size_days` | number or null | The original estimate from the MVP scope spreadsheet in "size-days" (the old T-shirt sizing scale). Not person-days. Kept for reference only; do not add it to `days_remaining`. |
| `sheet_state` | string | State recorded at the last spreadsheet review, for example `DONE`, `IN PROGRESS`, `OLD/3.62`. Free text. |
| `evidence` | string | Free-text note on where the estimate or status came from. |
| `person_id` | integer or null | Owner. `null` means unassigned. |
| `person` | string or null | Owner's name, resolved from `people`. `null` if unassigned or the person no longer exists. |
| `fixed_start` | date or null | If set, the item is pinned to start on the first working day on or after this date, and other work for that person flows around it. |
| `continuous` | boolean | If true the item runs from its start to the end of all other work (for example CI/CD). Its `days_remaining` are spread evenly over the project and reduce the owner's weekly capacity for other items. |
| `done` | boolean | Delivered. Delivered items are not scheduled and are hidden on the chart by default. |
| `depends_on` | array of integers | Ids of items that must finish before this one starts. Only applies to items that are not pinned with `fixed_start`. |
| `queue_position` | integer | Zero-based position in the overall queue. |
| `status` | string | One of `delivered`, `backlog`, `continuous`, `scheduled`. See below. |
| `scheduled` | `{start, end}` or null | Computed dates from the planner. `start` is the first working day of the item, `end` the last working day, both inclusive. `null` for `delivered` and `backlog` items. |

### `status` values

| Value | Meaning |
|---|---|
| `delivered` | `done` is true. Not scheduled. |
| `backlog` | Open but no valid owner. Not scheduled and not drawn as a bar. Its days still count in `summary.backlog_person_days`. |
| `continuous` | Open, owned, flagged continuous. `scheduled` spans from its start to `summary.projected_end`. |
| `scheduled` | Open, owned, normal work. `scheduled` gives its computed dates. |

### How `scheduled` was computed

The planner is deterministic and the dates are reproducible from the other fields:

1. Duration in working days = ceil(`days_remaining` × 5 ÷ owner's effective `days_per_week`). Effective capacity is
   `days_per_week` less the owner's continuous items' days spread over the project weeks.
2. Pinned items (`fixed_start` set) are placed first, at their date, skipping non-working days.
3. Remaining items are placed in queue order, each starting when the owner's previous item ends and every item in
   `depends_on` has ended, flowing around the pinned reservations.
4. Weekends, `closure_days` and the owner's `leave` are skipped, never worked through.

All scheduled dates are **build complete**, not go-live.

## `summary`

| Field | Type | Meaning |
|---|---|---|
| `items` | integer | Count of all items including delivered. |
| `delivered` | integer | Count with `done` true. |
| `open_person_days` | integer | Sum of `days_remaining` over open items, including backlog and continuous. |
| `backlog_person_days` | integer | Sum of `days_remaining` over `status == "backlog"` items. Work that has no owner and no dates yet. |
| `projected_end` | date or null | Last working day of the latest scheduled or continuous item. `null` if nothing is scheduled. |

## Things the export does not contain

- Users, roles and sessions.
- Chart layout or colours other than milestone colours.
- History. The export is a snapshot; there is no audit trail.
