Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/roadmap-closure.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Roadmap closure guard

on:
issues:
types: [closed]

permissions:
contents: read
issues: write
pull-requests: read

jobs:
verify-closure:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Verify roadmap closure evidence
id: verify
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: python tools/check_roadmap_closures.py issue-event
- name: Reopen an invalid roadmap closure
if: steps.verify.outcome == 'failure'
env:
GH_TOKEN: ${{ github.token }}
ISSUE_URL: ${{ github.event.issue.html_url }}
run: |
gh issue reopen "$ISSUE_URL"
gh issue comment "$ISSUE_URL" --body "This roadmap item was reopened automatically because its committed closure evidence contract was not satisfied. See the failed Roadmap closure guard run for the exact missing evidence."
exit 1
8 changes: 8 additions & 0 deletions .github/workflows/xevious-project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ name: Xevious project

on:
pull_request:
types: [opened, synchronize, reopened, edited, labeled, unlabeled, ready_for_review]
push:
branches:
- main

permissions:
contents: read
issues: read
pull-requests: read

jobs:
verify:
Expand All @@ -29,6 +32,11 @@ jobs:
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
run: python tools/check_mechanics_record.py "$BASE_SHA"
- name: Verify roadmap issue closures
if: github.event_name == 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
run: python tools/check_roadmap_closures.py pr

runtime-harness:
# A pre-playtest regression tripwire, NOT a gameplay gate: it runs the shipped build
Expand Down
19 changes: 18 additions & 1 deletion docs/BUILD_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,23 @@ preserving the lifecycle and collision interfaces.
What must be built is normatively grouped in the spec's [build order](spec/build-plan.md); this table
stages it.

### GitHub delivery model

The committed [dependency-aware roadmap](roadmap/README.md) projects this plan into GitHub without
making the Project board a second source of truth. Capability issues are milestone-free parents;
their native sub-issues are the independently closable component leaves. Cross-capability work is
composed by one delivery slice and one pull request closing each completed leaf separately.

A leaf cannot close while its specification or another declared blocker is open, while its required
mechanics evidence is absent, or before the operator approves the playable build at the exact tested
commit. Work that cannot be exercised until a consumer exists stays open and joins the consumer's
delivery slice. Draft-spec work is visible but provisional and non-closable. Imported history records
the original delivering pull request without retroactively certifying it under today's controls.

[`docs/roadmap/manifest.json`](roadmap/manifest.json) is the complete criterion-to-leaf mapping and
owns the derived milestone, blocker, slice, and proof metadata. The table below remains the readable
engineering sequence.

Every slice extracts only the costumes it needs and updates its catalog rows
plus a `docs/mechanics/` record. A slice closes only when its declared
behavior, provenance, automated validation, deterministic build, and
Expand All @@ -91,7 +108,7 @@ operator-runnable Scratch check agree.
| 2. Game director and state reset — current | Stage-owned title, ready, playing, player-dead, respawning, and game-over states; serialized reset scopes and cancellation. Depends on slice 1 only for the current canonical source. | Only Stage writes director variables; every normal state change uses one transition procedure; cleanup finishes before entry; reset handlers terminate; obsolete `begin`/`death` control is absent. | Exercise the state/input/reset contract recorded in the spec's core game systems document. Record `003-game-director-and-state-reset.md` and mark SYS-01 present. |
| 2a. Regression recovery — next | Restore the ten regressed behaviors and remove the two invented presentations recorded in the fidelity audit (tracked as issue #13); supersedes the abandoned recovery branch. Depends on slice 2 and the settled spec. | Every audit item B1-B10 restored per its spec section or preserved-baseline marker; A1/A2 removed; the operator playtest gate passes. | Play the playtest checklist end to end. Update affected mechanics records. |
| 3. Entity pool and collision foundation | Shared clone lifecycle, collision groups, single-hit resolution, off-screen cleanup, and air/ground/bullet/effect fixtures. Depends on SYS-01. | Repeated worst-case spawn/removal leaks no state; groups cannot cross-hit or double-resolve; measured clone load stays responsive. | Run the clone spike and each collision fixture in Scratch 3. Record SYS-02 and SYS-03. |
| — built as issue #14 | Delivered SYS-02 (64-slot array, reset-clear, player-shot 3-cap), the SYS-04 centralized ordered walk + `tick`, and the SYS-03 single-hit path + five-group vocabulary — with **SYS-04 pulled forward from slice 7** per #14's scope. All but the shot cap are dormant, foundation-only: no enemy participant or stream consumer exists yet, so the clone spike, live collision fixtures, entity-leak, and seeded-determinism playtests run when the first participants land. Records 004–006. | | |
| — foundation delivered by PR #20; capability parent #14 remains open | Delivered the 64-slot array, reset-clear, player-shot 3-cap, centralized ordered walk + `tick`, and the single-hit path + five-group vocabulary. The live entity-lifecycle, collision-resolution, and shared-random-consumer obligations remain open component leaves in slice 8 because they require an enemy participant. Records 004–006 are prior evidence, not completion of those live criteria. | | |
| 4. Score, HUD, lives, death, and respawn | Score/high-score cap, object awards, HUD, lives, bonus thresholds, collision death, safe respawn, and game over. Depends on slices 2–3. | Awards occur once; HUD matches state; life loss, bonus awards, respawn, and last-life game over repeat deterministically. | Run score/life fixtures through ordinary and last-life deaths. Record ECO-01–04 and PLY-02. |
| — built as issue #16 (Part of #15) | Delivered the life economy: ECO-01 (single scoring path, 9,999,990 cap, running high score), ECO-02 (digit + life-icon HUD from the CC BY 3.0 HUD font), ECO-03 (starting craft, per-setting bonus thresholds/increments, the disable sentinel, and the at-cap grant quirk from the committed tables), the ECO-04 GAME OVER presentation (128-frame hold) + best-five verdict, and PLY-02's lives-driven respawn-vs-game-over decision, no-invulnerability respawn, and terrain-restart-on-death. A debug **S** fixture drives scoring and the D/G keys trigger death until a real attacker lands, so in-play object awards and real collision death (slice 8), the stop-after-two DIP setting (index unpinned), the Bonus Flag (a ground secret), and the initials-entry screen (slice 19) are deferred — ECO-01/02 are present (operator-playtest-gated), ECO-03/04 and PLY-02 partial. Both #16 and #15 stay open. Records 009–014. | | |
| 5. Area clock and scheduler foundation | Monotonic terrain position, table representation, ordered event dispatch, area boundary seam, and one small schedule fixture. Depends on slices 2–3. | Position never rewinds during a life; fixture events fire once in order; transitions leave no old-area work. | Accelerate and pause the fixture around its boundaries. Record AREA-01 and AREA-02 foundation. |
Expand Down
13 changes: 13 additions & 0 deletions docs/roadmap/PR34_HANDOFF.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# PR #34 roadmap handoff

PR [#34](https://github.com/StarshipSuperjam/xevious/pull/34) remains the active slice-7 build in its existing Claude session. The roadmap migration did not change its branch, commits, draft state, body, labels, milestone, or Project fields.

Before that session submits the PR for merge:

1. Replace the stale `Part of #17` reference with `Closes #56` and `Part of #18`.
2. Do not close a slice-8 integration leaf. Enemy-dependent gameplay acceptance stays open until enemies exist in the playable build.
3. Reconcile the branch onto current `main` so the roadmap closure check runs.
4. After the operator tests the exact head commit, first add the required commit-specific playtest record, then apply `playtest-approved`. The label event refreshes the required closure check against the current comment and head.
5. Verify GitHub's computed closing-issue list contains only [#56](https://github.com/StarshipSuperjam/xevious/issues/56).

Issue #56 is the leaf for difficulty models and live state. Parent capability [#18](https://github.com/StarshipSuperjam/xevious/issues/18) remains open until all of its native sub-issues close.
107 changes: 107 additions & 0 deletions docs/roadmap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Dependency-aware Xevious roadmap

The roadmap is designed to keep a delivery from claiming completion before it
can be exercised. Its committed desired state is
[`manifest.json`](manifest.json). GitHub Issues, milestones, native sub-issue
links, and the Xevious Project board are projections of that file.

## Ownership

- `docs/spec/build-plan.md` owns the capability phases and their order.
- `docs/BUILD_PLAN.md` owns the engineering delivery sequence.
- `manifest.json` decomposes that sequence into capability parents, atomic
component leaves, blockers, milestones, proof levels, and delivery slices.
- `criteria.json` is the independent exact obligation roster; validation fails
if a criterion is dropped, invented, or assigned twice.
- GitHub Issues own discussion and delivery evidence for each projected item.
- Project #4 is a replaceable operational view. Its delivery fields never
become the only record of scope or dependency.

A capability parent has no milestone and never closes directly from a pull
request. Its native sub-issues are the independently closable units. A pull
request may compose leaves from several capability parents, but it uses one
`Closes #N` line for every leaf it actually completes.

## Closure evidence

The required project check reads the manifest and migration journal before a
pull request may close a roadmap leaf. It rejects:

- a capability parent as a close target;
- a provisional leaf whose specification is not settled;
- a leaf with an open blocker, unless the same pull request closes it;
- a gameplay or operator-tested leaf without the `playtest-approved` label
and a repository-owner comment identifying the exact tested head commit;
- a mechanics-bearing leaf without matching updated mechanics evidence;
- a leaf without changed automated success and failure evidence;
- a leaf whose prerequisite delivery slices still contain open work.

The marker recorded after a successful operator test is:

```text
<!-- xevious-playtest:v1 commit=<40-character-head-sha> -->
```

Automated tests added by a delivering PR identify both sides of every atomic
obligation with comments such as:

```text
# roadmap-evidence: SYS-02 success
# roadmap-evidence: SYS-02 failure
```

Only markers on lines newly added by that PR count. Existing comments or
unrelated tests cannot satisfy the closure gate.

The issue-closure workflow applies the same contract to a manually closed
roadmap item and reopens an invalid closure. The protected-branch merge and the
operator's review remain the binding gate.

## Provisional work and imported history

Leaves under a `draft` specification are visible so the full plan is visible,
but they carry `spec:draft`, remain non-executable, and cannot close. Settling
their owning specification is an explicit blocker.

Completed slices are imported as history leaves with their original delivering
pull request. They are not retroactively certified under today's evidence
contract. Incomplete foundations—including the live entity, collision, random,
and dispatch paths formerly hidden by closed issue #14—remain open leaves.

## Migration and recovery

[`migration.json`](migration.json) is the resumable journal. The migration:

1. validates the manifest and stops before writes on any mismatch;
2. snapshots milestones, Project fields/items/views, and every protected field
of active PR #34;
3. creates or converts parents and leaves by stable `roadmap-key`, journaling
each returned issue and Project identity;
4. attaches native parent relationships, milestones, and derived Project
fields in separate passes;
5. closes imported-history leaves only after their evidence and relationships
exist;
6. reads the complete live state back and proves exact parentage, milestones,
state, uniqueness, and that PR #34 did not change.

An interrupted migration is resumed by rerunning `apply`; it rolls forward from
stable keys and journaled IDs. Issue deletion or silent closure is never used as
rollback because GitHub history and notifications cannot be undone. If PR #36
is abandoned, the journal and `roadmap-migration: PR #36` markers identify the
incomplete projection to resume or explicitly supersede.

Useful commands:

```bash
python3 tools/roadmap.py validate
python3 tools/roadmap.py plan
python3 tools/roadmap.py snapshot
python3 tools/roadmap.py apply
python3 tools/roadmap.py reconcile
python3 tools/roadmap.py handoff
```

Existing Project views and the Engine-owned summary fields are immutable to the
migration. It adds manifest-owned views for delivery leaves, capability
parents, and imported history, plus the derived `Roadmap role`, `Delivery
slice`, and `Proof level` fields.
21 changes: 21 additions & 0 deletions docs/roadmap/criteria.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"version": 1,
"criteria": [
"AIR-01.toroid", "AIR-02.torkan", "AIR-03.zoshi", "AIR-04.jara", "AIR-05.kapi", "AIR-06.terrazi",
"AIR-07.zakato", "AIR-08.brag-zakato", "AIR-09.sheonite", "AIR-10.spario", "AIR-11.bacura",
"AIR-12.pool", "AIR-12.radiating", "AIR-12.standard",
"AREA-01.clock", "AREA-01.visual-terrain", "AREA-02.air-dispatch", "AREA-02.data", "AREA-02.ground-dispatch", "AREA-03.tables", "AREA-04.loop",
"BOSS-01.lifecycle", "BOSS-02.defenses", "BOSS-03.core", "BUILD-01.sprite-proof",
"CAB-01.attract", "CAB-02.one-player", "CAB-02.two-player", "CAB-03.alternation", "CAB-04.high-scores", "CAB-05.presentation",
"DIF-01.model", "DIF-01.play", "DIF-02.model", "DIF-02.play", "DIF-03.model", "DIF-03.play",
"ECO-01.air-awards", "ECO-01.ground-awards", "ECO-01.path", "ECO-02.one-player", "ECO-02.two-player",
"ECO-03.bonus-flag", "ECO-03.stop-after-two", "ECO-03.tables", "ECO-04.high-score-route", "ECO-04.presentation",
"FORM-01.model", "FORM-01.play",
"GND-01.barra", "GND-01.variants", "GND-02.zolbak", "GND-03.logram", "GND-04.derota", "GND-05.boza", "GND-06.grobda", "GND-07.domogram",
"PLY-01.movement", "PLY-02.air-trigger", "PLY-02.decision", "PLY-RECOVERY.audit",
"PRES-01.framing", "PRES-02.hud-yellow", "RELEASE-01.full-soak", "RELEASE-02.audit",
"SEC-01.sol-tower", "SEC-02.bonus-flag", "SEC-03.hidden-credit",
"SYS-01.director-reset", "SYS-02.foundation", "SYS-02.live", "SYS-03.foundation", "SYS-03.live", "SYS-04.consumer", "SYS-04.stream",
"WPN-01.bacura-bounce", "WPN-01.flight", "WPN-02.air-hit", "WPN-03.crosshair", "WPN-03.target-lock", "WPN-04.bomb-flight", "WPN-05.ground-hit"
]
}
Loading
Loading