Skip to content

Commit 2a9c239

Browse files
Merge pull request #115 from StarshipSuperjam/claude/roadmap-less-leaky
2 parents 1281392 + 865e2df commit 2a9c239

6 files changed

Lines changed: 621 additions & 932 deletions

File tree

‎docs/roadmap/PR34_HANDOFF.md‎

Lines changed: 0 additions & 13 deletions
This file was deleted.

‎docs/roadmap/README.md‎

Lines changed: 69 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -68,69 +68,76 @@ pull request. They are not retroactively certified under today's evidence
6868
contract. Incomplete foundations—including the live entity, collision, random,
6969
and dispatch paths formerly hidden by closed issue #14—remain open leaves.
7070

71-
## Recording delivery after a slice merges
72-
73-
Merging a slice's pull request closes its leaf issues on GitHub, but nothing
74-
back-propagates into the manifest — the manifest is the source of truth, and a
75-
merge never edits it. Until you record the delivery, those leaves still read
76-
`status: "planned"`, so `reconcile` reports drift (a closed issue whose manifest
77-
status is not `history`) and — critically — the next `apply` would force those
78-
merged issues **back open** and reset their board cards to Backlog.
79-
80-
After a slice PR merges, record it and re-project:
81-
82-
```bash
83-
python3 tools/roadmap.py deliver --pr <PR#> # manifest: its leaves → history + delivered_by
84-
python3 tools/roadmap.py apply # GitHub: keep issues closed, board Status → Done
85-
python3 tools/roadmap.py reconcile # verify live state matches the manifest
86-
```
87-
88-
Then commit the changed `manifest.json` and `migration.json`.
89-
90-
`deliver` reads the pull request's computed closing issues, maps each to a leaf
91-
through the journal, and sets that leaf `status: "history"` with
92-
`delivered_by: <PR#>` — a minimal per-line edit that preserves the manifest's
93-
style. It refuses a pull request that is not merged, skips leaves already
94-
recorded, and re-validates before writing.
95-
96-
**Order matters: record delivery first.** Running `apply` while a merged leaf is
97-
still `planned` reopens its issue. Always `deliver` before `apply`.
98-
99-
## Migration and recovery
100-
101-
[`migration.json`](migration.json) is the resumable journal. The migration:
102-
103-
1. validates the manifest and stops before writes on any mismatch;
104-
2. snapshots milestones, Project fields/items/views, and every protected field
105-
of active PR #34;
106-
3. creates or converts parents and leaves by stable `roadmap-key`, journaling
107-
each returned issue and Project identity;
108-
4. attaches native parent relationships, milestones, and derived Project
109-
fields in separate passes;
110-
5. closes imported-history leaves only after their evidence and relationships
111-
exist;
112-
6. reads the complete live state back and proves exact parentage, milestones,
113-
state, uniqueness, and that PR #34 did not change.
114-
115-
An interrupted migration is resumed by rerunning `apply`; it rolls forward from
116-
stable keys and journaled IDs. Issue deletion or silent closure is never used as
117-
rollback because GitHub history and notifications cannot be undone. If PR #36
118-
is abandoned, the journal and `roadmap-migration: PR #36` markers identify the
119-
incomplete projection to resume or explicitly supersede.
120-
121-
Useful commands:
71+
## Recording delivery: the slice PR carries it
72+
73+
`manifest.json` is the source of truth and GitHub is a projection, so a slice's
74+
delivery is recorded **in the same pull request that delivers it** — the manifest
75+
can never lag `main`.
76+
77+
1. In the slice PR, once it closes its leaf issues (one `Closes #N` per delivered
78+
leaf), record the delivery and commit the edit into the PR:
79+
80+
```bash
81+
python3 tools/roadmap.py deliver --pr <this PR#>
82+
```
83+
84+
This flips each closed leaf from `status: "planned"` to `"history"` with
85+
`delivered_by: <PR#>` — a minimal per-line manifest edit. `deliver` accepts an
86+
**open** PR precisely so this rides the same PR; the required closure check then
87+
refuses the merge unless the manifest at the PR head records every leaf the PR
88+
closes. At merge, GitHub closes the issues and the same merge lands the manifest
89+
that records them, so the two always agree.
90+
91+
2. After it merges, project the manifest onto GitHub from an updated `main`:
92+
93+
```bash
94+
python3 tools/roadmap.py apply # keep the delivered issues closed, board Status → Done
95+
python3 tools/roadmap.py reconcile # verify the live projection matches the manifest
96+
```
97+
98+
This second step is **manual by design**: GitHub Actions' own token cannot read
99+
or write the org Project board and no personal-access token is provisioned, so
100+
nothing pushes the board automatically. If you forget it nothing breaks and no
101+
issue reopens — the next `reconcile` simply reports the drift and names the leaf,
102+
and running `apply` fixes it. A converged `apply` writes nothing
103+
(`patched 0 issues, 0 board fields`). If `apply` changes the `migration.json`
104+
journal, land that change on `main` through a small pull request, like any other
105+
change (nothing reaches `main` except by pull request).
106+
107+
**Dropping a leaf.** There is no `dropped` status yet, and the manifest has no field
108+
that expresses a cancelled leaf, so there is no clean edit to make one stick. Until the
109+
`dropped` status is added (tracked as its own roadmap issue), **leave the leaf's issue
110+
open** — the closure guard reopens a hand-closed `planned` leaf — and record the
111+
cancellation as a comment on that issue. Do not delete the leaf's manifest line either:
112+
its criteria are part of the exact criterion roster `validate` enforces, so removing it
113+
would need the roster updated in the same edit and is a change to make deliberately under
114+
review, not a quick cancellation.
115+
116+
## Archived board cards
117+
118+
GitHub auto-archives a card about two weeks after its issue closes, so most
119+
delivered leaves' cards are archived. The tooling is archive-aware: `apply` and
120+
`reconcile` read archived cards too and accept an archived Done card as correct, so
121+
a delivered leaf never reads as a missing or drifted card.
122+
123+
## Commands
122124

123125
```bash
124-
python3 tools/roadmap.py validate
125-
python3 tools/roadmap.py plan
126-
python3 tools/roadmap.py snapshot
127-
python3 tools/roadmap.py apply
128-
python3 tools/roadmap.py reconcile
129-
python3 tools/roadmap.py deliver --pr <PR#>
130-
python3 tools/roadmap.py handoff
126+
python3 tools/roadmap.py validate # the manifest is internally consistent
127+
python3 tools/roadmap.py plan [--live] # counts; --live adds a read-only GitHub diff
128+
python3 tools/roadmap.py deliver --pr N # record a PR's leaves delivered (run in the slice PR)
129+
python3 tools/roadmap.py apply # project the manifest onto Issues + the board
130+
python3 tools/roadmap.py reconcile # verify the live projection matches the manifest
131131
```
132132

133-
Existing Project views and the Engine-owned summary fields are immutable to the
134-
migration. It adds manifest-owned views for delivery leaves, capability
135-
parents, and imported history, plus the derived `Roadmap role`, `Delivery
136-
slice`, and `Proof level` fields.
133+
`apply` is safe to re-run: it writes the journal incrementally and skips any issue or
134+
board field already correct, so if a run dies partway (a network blip or a rate limit)
135+
just run it again — it rolls forward from the journal and the stable `roadmap-key`s
136+
rather than duplicating issues or cards.
137+
138+
[`migration.json`](migration.json) is the identity journal: it caches the GitHub
139+
issue and card ids for each stable `roadmap-key` plus the project header `apply`
140+
bootstraps from. Existing Project views and the engine-owned summary fields are the
141+
operator's, not the manifest's; the tool owns only the delivery-leaf,
142+
capability-parent, and imported-history views and the derived `Roadmap role`,
143+
`Delivery slice`, and `Proof level` fields.

0 commit comments

Comments
 (0)