docs(format): define the portable Macroplan TOML format v1
Document the schema, value types, and render semantics so a Macroplan .toml can be produced and consumed outside this app's renderer. Ships a JSON Schema (served from public/ for editor use) and a reference file.
This commit is contained in:
13
README.md
13
README.md
@@ -30,9 +30,10 @@ Dashboard ┣━━━━━◯ 🔴 n
|
||||
|
||||
## Documentation
|
||||
|
||||
| Document | What it covers |
|
||||
| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [CONTEXT.md](CONTEXT.md) | **Ubiquitous language** — the glossary: Macroplan, Feature, Original Estimate, Re-estimate, Delivery, Milestone, Week, Now line, Learning, Status, and the symbol legend |
|
||||
| [DESIGN.md](DESIGN.md) | **Goal-driven design (QFD)** — goals, functions, the Goal→Function→How→Component cascade, the House/Roof matrices + rendered House of Quality, critical performance budget, trade-offs, and watched tensions |
|
||||
| [docs/adr/0001-original-estimate-as-baseline.md](docs/adr/0001-original-estimate-as-baseline.md) | Why on-time/late is judged against the **Original Estimate**, never a re-estimate |
|
||||
| [docs/adr/0002-local-first-no-backend.md](docs/adr/0002-local-first-no-backend.md) | Why the app is **local-first with no backend**, and TOML is the portable source of truth |
|
||||
| Document | What it covers |
|
||||
| ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [docs/format.md](docs/format.md) | **The Macroplan TOML format (v1)** — the portable, tool-independent schema: fields, value types, the `macroplan_version` marker, and the render semantics a consumer must honor. Ships a [JSON Schema](public/macroplan.schema.json) and a [reference `.toml`](docs/macroplan.example.toml). |
|
||||
| [CONTEXT.md](CONTEXT.md) | **Ubiquitous language** — the glossary: Macroplan, Feature, Original Estimate, Re-estimate, Delivery, Milestone, Week, Now line, Learning, Status, and the symbol legend |
|
||||
| [DESIGN.md](DESIGN.md) | **Goal-driven design (QFD)** — goals, functions, the Goal→Function→How→Component cascade, the House/Roof matrices + rendered House of Quality, critical performance budget, trade-offs, and watched tensions |
|
||||
| [docs/adr/0001-original-estimate-as-baseline.md](docs/adr/0001-original-estimate-as-baseline.md) | Why on-time/late is judged against the **Original Estimate**, never a re-estimate |
|
||||
| [docs/adr/0002-local-first-no-backend.md](docs/adr/0002-local-first-no-backend.md) | Why the app is **local-first with no backend**, and TOML is the portable source of truth |
|
||||
|
||||
154
docs/format.md
Normal file
154
docs/format.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# Macroplan TOML Format — v1
|
||||
|
||||
The portable, tool-independent definition of a **Macroplan**: a week-by-week
|
||||
delivery plan that records what a team promised, every time an estimate slipped,
|
||||
and when work actually shipped — judged against the **first** estimate, never a
|
||||
moved goalpost.
|
||||
|
||||
This document is the contract. Any tool can produce or consume a `.toml` file
|
||||
that conforms to it and render the same plan. The [macroplan app](../README.md)
|
||||
is one such consumer; nothing here is specific to it.
|
||||
|
||||
- **Machine-readable schema:** [`macroplan.schema.json`](../public/macroplan.schema.json),
|
||||
served at `https://macroplan.apoena.dev/macroplan.schema.json` (JSON Schema
|
||||
2020-12; validates the TOML-decoded structure).
|
||||
- **Reference file:** [`macroplan.example.toml`](macroplan.example.toml)
|
||||
(exercises every field and state; a conformance sample).
|
||||
- **Serialization:** [TOML v1.0.0](https://toml.io). Values use TOML's native
|
||||
types — dates are **bare date literals**, not quoted strings (see _Dates_).
|
||||
|
||||
## Versioning
|
||||
|
||||
A file MAY declare the format version at the top level:
|
||||
|
||||
```toml
|
||||
macroplan_version = 1
|
||||
```
|
||||
|
||||
- **Optional.** An absent marker means the current version (`1`). Every existing
|
||||
file therefore stays valid.
|
||||
- **Integer**, monotonically increasing. This document defines version **1**.
|
||||
- A consumer that understands version _N_ **MUST reject** a file declaring a
|
||||
version **greater than** _N_ (rather than silently mis-rendering it), and
|
||||
SHOULD accept any version ≤ _N_ it still supports.
|
||||
- The version is bumped only for a **breaking** change to the schema or render
|
||||
semantics below — a new optional field does not require a bump.
|
||||
|
||||
Producers SHOULD emit `macroplan_version` so a file self-describes its target.
|
||||
|
||||
## Top-level fields
|
||||
|
||||
| Key | Required | Type | Default | Meaning |
|
||||
| ------------------- | -------- | ------------- | ---------------------- | ------------------------------------------------------------------------------------- |
|
||||
| `macroplan_version` | no | integer (`1`) | `1` | Format version (see above). |
|
||||
| `title` | no | string | `"Untitled Macroplan"` | Display name of the plan. |
|
||||
| `start` | no | date | auto-fit | Left edge of the plan's week span. **Only extends** the span; never clips a feature. |
|
||||
| `end` | no | date | auto-fit | Right edge of the plan's week span. **Only extends** the span; never clips a feature. |
|
||||
|
||||
Zero or more `[[feature]]` blocks and zero or more `[[milestone]]` blocks follow.
|
||||
|
||||
## `[[feature]]`
|
||||
|
||||
A unit of work, read left-to-right as a story: where it started, what was first
|
||||
promised, every slip, and when it shipped.
|
||||
|
||||
| Key | Required | Type | Default | Meaning |
|
||||
| ------------- | -------- | -------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `name` | **yes** | non-empty string | — | Identifies the feature; the **join key** for a milestone's `requires`. |
|
||||
| `start` | **yes** | date | — | The week work began. |
|
||||
| `original` | **yes** | date | — | The **Original Estimate** — the immovable baseline all lateness is judged against ([ADR-0001](adr/0001-original-estimate-as-baseline.md)). |
|
||||
| `reestimates` | no | array of dates | `[]` | Each later week the estimate slipped to (a slip, `△`). Order-independent. |
|
||||
| `delivered` | no | date | — | The week the feature actually shipped. Absent → still in flight. |
|
||||
| `learning` | no | string | — | A post-delivery takeaway. |
|
||||
| `status` | no | `"on-track"` \| `"at-risk"` \| `"off-track"` | — | In-flight confidence. Meaningful only while undelivered. |
|
||||
| `note` | no | string | — | A short note accompanying `status`. |
|
||||
|
||||
**Constraint — unique names:** feature `name` values MUST be unique within a
|
||||
plan. Because milestone `requires` resolves by name, a duplicate would make a
|
||||
requirement ambiguous. Conforming consumers reject duplicates.
|
||||
|
||||
## `[[milestone]]`
|
||||
|
||||
A dated checkpoint that depends on a set of features being delivered by then.
|
||||
|
||||
| Key | Required | Type | Default | Meaning |
|
||||
| ---------- | -------- | ---------------- | ------- | ------------------------------------------------------------------- |
|
||||
| `name` | **yes** | non-empty string | — | Display name of the milestone. |
|
||||
| `week` | **yes** | date | — | When the milestone falls due. |
|
||||
| `requires` | no | array of strings | `[]` | Feature `name`s that must be delivered on/before `week` to meet it. |
|
||||
|
||||
A `requires` entry that names no existing feature counts as **unmet** (it can
|
||||
never be satisfied) — useful for catching typos.
|
||||
|
||||
## Value types
|
||||
|
||||
### Dates → weeks
|
||||
|
||||
Every date field is a **calendar date**, written as a TOML bare date literal:
|
||||
|
||||
```toml
|
||||
original = 2026-06-15
|
||||
reestimates = [2026-06-29, 2026-07-13]
|
||||
```
|
||||
|
||||
- **Canonical form is the bare date literal.** A quoted `"2026-06-15"` string is
|
||||
_tolerated_ on input for convenience, but producers SHOULD emit bare literals
|
||||
so the same plan always serializes identically.
|
||||
- **Any day snaps to its ISO-week Monday.** A Macroplan's unit is the week, keyed
|
||||
by the Monday (yyyy-mm-dd) of the ISO week containing the date. `2026-06-17`
|
||||
(a Wednesday) and `2026-06-15` (that Monday) denote the **same week**. Every
|
||||
conforming renderer MUST apply this snapping, or dates land in the wrong column.
|
||||
|
||||
### Status
|
||||
|
||||
The enum `on-track` \| `at-risk` \| `off-track`. No other value is valid. Lateness
|
||||
is **derived, never authored** — there is deliberately no "late" status.
|
||||
|
||||
## Render semantics (the contract a renderer must honor)
|
||||
|
||||
Two tools should draw the same plan from the same file. These rules are as much
|
||||
a part of the format as the fields:
|
||||
|
||||
1. **On-time vs. late is judged only against `original`** — never a re-estimate
|
||||
([ADR-0001](adr/0001-original-estimate-as-baseline.md)). `delivered ≤ original`
|
||||
(by week) is on time; `delivered > original` is late.
|
||||
2. **Marker vocabulary** per feature:
|
||||
- `◯` **original** — the Original Estimate week, while undelivered.
|
||||
- `△` **re-estimate** — one per `reestimates` entry.
|
||||
- `◉` **delivered on time** — a delivery on/before `original`. It **subsumes**
|
||||
the `◯` (no separate original marker is drawn).
|
||||
- `▲` **delivered late** — a delivery after `original`; the `◯` baseline is
|
||||
**kept** alongside it.
|
||||
3. **Bar extent.** A feature's bar runs from `start` to its furthest marker. A
|
||||
delivered bar ends at its delivery. An **overdue** feature (undelivered, and
|
||||
already past its furthest estimate relative to "now") keeps running to the
|
||||
current week.
|
||||
4. **Plan span.** The rendered week range runs from the earliest to the latest
|
||||
week among all features' markers and all milestones. Optional `start`/`end`
|
||||
only widen this range with lead-in / trailing weeks; a marker outside them is
|
||||
never clipped.
|
||||
5. **Milestone met/unmet.** A required feature is **unmet** at a milestone if it
|
||||
is undelivered, delivered _after_ the milestone `week`, or names no existing
|
||||
feature.
|
||||
6. **"Now".** The current week (Monday of today) drives overdue extension and the
|
||||
"now" line. It is contextual, not stored in the file.
|
||||
|
||||
## Notes for implementers
|
||||
|
||||
- **Unknown keys.** The JSON Schema is strict (`additionalProperties: false`) so
|
||||
editors flag typos like `titel`. The reference parser is more lenient — it
|
||||
ignores unrecognized top-level keys. Producers SHOULD NOT emit unknown keys.
|
||||
- **Empty plan.** A file with no features and no milestones is valid; with an
|
||||
authored `start`/`end` it renders as an empty grid across that span.
|
||||
- **Editor integration.** Point [Taplo](https://taplo.tamasfe.dev)-based editors
|
||||
at the schema with a directive on the first line of a plan file:
|
||||
`#:schema https://macroplan.apoena.dev/macroplan.schema.json`.
|
||||
|
||||
## See also
|
||||
|
||||
- [CONTEXT.md](../CONTEXT.md) — the ubiquitous language (Feature, Original
|
||||
Estimate, Re-estimate, Delivery, Milestone, Week, Status, Learning).
|
||||
- [ADR-0001](adr/0001-original-estimate-as-baseline.md) — why lateness is judged
|
||||
against the Original Estimate.
|
||||
- [ADR-0002](adr/0002-local-first-no-backend.md) — why the `.toml` file is the
|
||||
portable source of truth.
|
||||
79
docs/macroplan.example.toml
Normal file
79
docs/macroplan.example.toml
Normal file
@@ -0,0 +1,79 @@
|
||||
#:schema https://macroplan.apoena.dev/macroplan.schema.json
|
||||
#
|
||||
# Reference Macroplan — a conformance sample exercising every field and state.
|
||||
# The authoritative definition of this format is docs/format.md.
|
||||
|
||||
# Format version. Optional; the current version is 1. Omitting it means "1".
|
||||
macroplan_version = 1
|
||||
|
||||
title = "Q3 — Checkout revamp"
|
||||
|
||||
# Optional plan span: pad the plan with lead-in / trailing weeks.
|
||||
# Rule: `start` only ever extends the span left, `end` extends it right;
|
||||
# neither clips a feature. Omit both to auto-fit the columns.
|
||||
start = 2026-05-25
|
||||
end = 2026-08-03
|
||||
|
||||
# Dates are TOML date literals. Any day snaps to that week's Monday.
|
||||
|
||||
# On-time delivery: delivered on/before the Original Estimate → ◉ (subsumes ◯).
|
||||
[[feature]]
|
||||
name = "Auth"
|
||||
start = 2026-06-01
|
||||
original = 2026-06-15
|
||||
delivered = 2026-06-15
|
||||
learning = "Spiking the OAuth flow first paid off — do discovery spikes earlier."
|
||||
|
||||
# Late delivery with slips: ◯ baseline kept, one △ per re-estimate, then ▲.
|
||||
# Late because delivery (07-20) is after `original` (06-15) — the re-estimates
|
||||
# never move the goalpost (ADR-0001).
|
||||
[[feature]]
|
||||
name = "Payments"
|
||||
start = 2026-06-01
|
||||
original = 2026-06-15
|
||||
reestimates = [2026-06-29, 2026-07-13]
|
||||
delivered = 2026-07-20
|
||||
learning = "Vendor lead time was the real constraint — derisk vendors up front."
|
||||
|
||||
# In-flight and overdue: undelivered, `original` already in the past → ◯ only,
|
||||
# bar runs to "now". `status` + `note` express confidence while it's open.
|
||||
[[feature]]
|
||||
name = "Dashboard"
|
||||
start = 2026-06-01
|
||||
original = 2026-06-08
|
||||
status = "off-track"
|
||||
note = "No recovery plan yet — needs an owner."
|
||||
|
||||
# In-flight, re-estimated once: ◯ + △, still open.
|
||||
[[feature]]
|
||||
name = "Search"
|
||||
start = 2026-06-08
|
||||
original = 2026-06-22
|
||||
reestimates = [2026-07-06]
|
||||
status = "at-risk"
|
||||
note = "Third-party search API is flaky; spike a fallback."
|
||||
|
||||
# In-flight, on track: the minimal feature — only the three required fields
|
||||
# plus a status.
|
||||
[[feature]]
|
||||
name = "Notifications"
|
||||
start = 2026-06-22
|
||||
original = 2026-07-06
|
||||
status = "on-track"
|
||||
|
||||
# Milestones tie a due week to the features that must ship by then.
|
||||
[[milestone]]
|
||||
name = "Code freeze"
|
||||
week = 2026-06-29
|
||||
requires = ["Auth", "Payments"]
|
||||
|
||||
[[milestone]]
|
||||
name = "MVP go-live"
|
||||
week = 2026-07-06
|
||||
requires = ["Auth", "Payments", "Dashboard"]
|
||||
|
||||
# A milestone with no requirements is valid too.
|
||||
[[milestone]]
|
||||
name = "Beta launch"
|
||||
week = 2026-07-13
|
||||
requires = ["Search", "Notifications"]
|
||||
108
public/macroplan.schema.json
Normal file
108
public/macroplan.schema.json
Normal file
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://macroplan.apoena.dev/macroplan.schema.json",
|
||||
"title": "Macroplan",
|
||||
"description": "A week-by-week delivery plan. Portable TOML format v1 — see docs/format.md. Validates the TOML-decoded structure; dates are TOML date literals, surfaced to JSON Schema as date-formatted strings.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"macroplan_version": {
|
||||
"description": "Format version. Omit for the current version (1). A newer version must be rejected by a consumer that only understands 1.",
|
||||
"type": "integer",
|
||||
"const": 1
|
||||
},
|
||||
"title": {
|
||||
"description": "Display name of the plan. Defaults to \"Untitled Macroplan\" when absent.",
|
||||
"type": "string"
|
||||
},
|
||||
"start": {
|
||||
"description": "Optional left edge of the plan's week span. Only extends the span; never clips a feature. Snapped to its ISO-week Monday.",
|
||||
"type": "string",
|
||||
"format": "date"
|
||||
},
|
||||
"end": {
|
||||
"description": "Optional right edge of the plan's week span. Only extends the span; never clips a feature. Snapped to its ISO-week Monday.",
|
||||
"type": "string",
|
||||
"format": "date"
|
||||
},
|
||||
"feature": {
|
||||
"description": "The features of the plan. Written as [[feature]] blocks. Names must be unique.",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/feature" }
|
||||
},
|
||||
"milestone": {
|
||||
"description": "Dated checkpoints. Written as [[milestone]] blocks.",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/milestone" }
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"date": {
|
||||
"description": "A calendar date (TOML date literal, e.g. 2026-06-15). Snapped to its ISO-week Monday.",
|
||||
"type": "string",
|
||||
"format": "date"
|
||||
},
|
||||
"feature": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "start", "original"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Identifies the feature; the join key for a milestone's `requires`. Must be unique within the plan.",
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"start": {
|
||||
"description": "The week work began.",
|
||||
"$ref": "#/$defs/date"
|
||||
},
|
||||
"original": {
|
||||
"description": "The Original Estimate — the immovable baseline all lateness is judged against (ADR-0001).",
|
||||
"$ref": "#/$defs/date"
|
||||
},
|
||||
"reestimates": {
|
||||
"description": "Each later week the estimate slipped to. Order-independent.",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/date" }
|
||||
},
|
||||
"delivered": {
|
||||
"description": "The week the feature actually shipped. Absent means still in flight.",
|
||||
"$ref": "#/$defs/date"
|
||||
},
|
||||
"learning": {
|
||||
"description": "A post-delivery takeaway.",
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"description": "In-flight confidence. Meaningful only while undelivered. Lateness is derived, never authored.",
|
||||
"enum": ["on-track", "at-risk", "off-track"]
|
||||
},
|
||||
"note": {
|
||||
"description": "A short note accompanying `status`.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"milestone": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "week"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Display name of the milestone.",
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"week": {
|
||||
"description": "When the milestone falls due.",
|
||||
"$ref": "#/$defs/date"
|
||||
},
|
||||
"requires": {
|
||||
"description": "Feature names that must be delivered on/before `week` to meet the milestone.",
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user