docs/superpowers/plans/2026-05-29-ws8e-fork-pr-checkpoint.md
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Open PR #1 from Dacode45:6.0-rc to Dacode45:unstable so the owner can review every WS1–WS8 diff before any work advances. Merge with "Create a merge commit" preserving all per-WS commits and backlog-PR --author headers. Update the CLAUDE.md status line to ✅ WS8, and write docs/superpowers/notes/ws8-complete.md as the WS8 done-note.
Architecture: GitHub PR opens for owner review. Review feedback lands as new commits on 6.0-rc (force-push avoided where additive commits suffice; never amend a commit already in PR history). Final merge is via the GitHub UI selecting "Create a merge commit" (--no-ff semantics).
Tech Stack: gh CLI, GitHub UI.
Spec reference: docs/superpowers/specs/2026-05-29-ws8-release-prep-checkpoint-design.md §4 WS8e.
6.0-rc and Dacode45:unstableFiles: none (precondition check)
6.0-rc at the WS8d HEADRun: git rev-parse --abbrev-ref HEAD && git log -1 --pretty='%h %s'
Expected: branch 6.0-rc; HEAD commit is the last WS8d commit (Fold-in 3 — samples removal).
6.0-rc is pushed to fork and CI is greenRun: git fetch fork && git log fork/6.0-rc..HEAD --oneline 2>&1 | head -5
Expected: zero output (fork is up to date with local 6.0-rc). If non-empty, push: git push fork 6.0-rc.
Run: gh run list -R Dacode45/ms-raylib-rs -L 5 --json status,conclusion,workflowName,headSha --jq '.[] | "\(.workflowName): \(.status) / \(.conclusion)"'
Expected: latest 5 runs (or however many ran for the most recent push) all show completed / success.
Dacode45:unstable exists as the target branchRun: git ls-remote fork unstable 2>&1
Expected: prints the current Dacode45:unstable SHA. If the branch doesn't exist on the fork, create it from canonical's unstable:
git fetch origin unstable
git push fork origin/unstable:refs/heads/unstable
Run: git log --oneline fork/unstable..fork/6.0-rc 2>&1 | wc -l
Expected: a number of commits ahead — the WS1–WS8 commits we're shipping. (This is the size of PR #1.)
Files:
Create: /tmp/ws8e-pr-body.md (transient — only used by gh pr create)
Step 1: Write the PR body to a temp file
Create /tmp/ws8e-pr-body.md with this content:
## raylib-rs 6.0 — release prep checkpoint
This PR bundles **WS1–WS8** of the raylib 5.x → 6.0 upgrade for owner review.
It is a **checkpoint, not a release** — no crates.io publish, no canonical
merge, no `v6.0.0` tag. After this PR merges, the fork's `unstable` HEAD is
the snapshot the owner evaluates before WS9 (showcase rewrite) and any
further work they choose to add.
### Workstream summary
- **WS0** ✅ Baseline + roadmap — `docs/superpowers/specs/2026-05-25-raylib-rs-6.0-roadmap-design.md`.
- **WS1** ✅ raylib-sys at 6.0 parity (3-OS CI green) — `docs/superpowers/notes/ws1-config-reconcile.md`.
- **WS2a** ✅ Native math types (bindgen-generated `Vector2/3/4`, `Matrix`, distinct `Quaternion`).
- **WS2b** ✅ Math-ecosystem adapters opt-in only; `mint`/`glam`/`serde` no longer default.
- **WS3** ✅ Safe crate green vs 6.0; `ModelAnimations` RAII; Tier-1 tests — `docs/superpowers/notes/ws3-complete.md`.
- **WS4** ✅ Software-renderer + headless render-test harness — `docs/superpowers/notes/ws4b-complete.md`.
- **WS5** ✅ raygui 6.0 parity (57/57 fns) + safe `rlgl` immediate-mode module — `docs/superpowers/notes/ws5-complete.md`.
- **WS6** ✅ Platform matrix + layered CI/CD + quality hard-gates + MSRV 1.85 — `docs/superpowers/notes/ws6b-complete.md`.
- **WS7** ✅ 28-chapter mdBook + ~25 rustdoc enrichments + CHANGELOG 6.0.0 entry + backlog #284/#273 folded — `docs/superpowers/notes/ws7-complete.md`.
- **WS8** ✅ Version bump + draft release workflows (`release-sys.yml`, `release-safe.yml` — workflow_dispatch-only, dry_run-gated) + `act` validation + release-hygiene fold-ins (Node.js 24 actions, `paste` advisory acceptance, `samples/` removal) — `docs/superpowers/notes/ws8-complete.md` (added in this PR).
### What's deliberately NOT in this PR
- **No publish to crates.io.** `release-sys.yml` and `release-safe.yml` are authored but never auto-triggered. Real publish is the future final-release step.
- **No CHANGELOG date flip.** `## 6.0.0 (unreleased)` heading stays; the date is set at real publish time.
- **No `v6.0.0` tag.** Tagging is part of the final-release step.
- **No PR to `raylib-rs/raylib-rs`.** The canonical merge happens only after WS9 and any added work.
### Review focus areas
- Per-WS commit grouping is preserved. Each WS lands as a sequence of small commits with `Co-Authored-By: Claude Opus 4.7` attribution. Backlog-PR cherry-picks (PR #284 LBreede, PR #273 AmityWilder, PR #272 AmityWilder, etc.) keep the original `--author` headers.
- Locked WS8 decisions are recorded in the spec §3.
- Tracked-deferred items (see WS8 spec §8 + each `wsN-complete.md`) carry forward.
### CI surface
All 5 existing workflows + 2 new release workflows present on this branch. The 5 auto-running ones are green at the PR HEAD. The 2 new release workflows are `workflow_dispatch`-only and never auto-trigger.
### How to merge
Select **"Create a merge commit"** in the GitHub UI (`--no-ff` semantics). Do **not** select squash or rebase-and-merge — those destroy the per-WS commit groupings and backlog-PR attribution headers.
(If /tmp isn't writable on this platform, use a repo-local path that's gitignored, e.g. .git/ws8e-pr-body.md.)
Run: head -30 /tmp/ws8e-pr-body.md
Expected: starts with the H2 title and the leading paragraph.
Dacode45:6.0-rc → Dacode45:unstable)Files: none (uses gh pr create)
Run:
gh pr create \
--repo Dacode45/ms-raylib-rs \
--base unstable \
--head 6.0-rc \
--title "raylib-rs 6.0 — release prep checkpoint" \
--body-file /tmp/ws8e-pr-body.md
Expected: prints the PR URL. Capture the URL for downstream steps.
Run: gh pr list -R Dacode45/ms-raylib-rs --head 6.0-rc --json number,title,url
Expected: one entry showing the new PR with title raylib-rs 6.0 — release prep checkpoint.
Files: none (owner activity)
This is the checkpoint's primary purpose: the owner reviews every diff. No automated work happens here. Mark this task complete when the owner signals (in conversation or PR comments) that review is done and approved.
Run: gh pr view <PR-number> -R Dacode45/ms-raylib-rs --json state,reviews,mergeable --jq '{state, mergeable, reviews: .reviews | length}'
Expected: state: OPEN, mergeable: MERGEABLE (assuming CI is green; if MERGEABLE_UNKNOWN, re-check after CI completes).
If owner requests no changes: skip Task 5, go to Task 6. If owner requests changes: do Task 5 first.
Files: depends on review feedback
6.0-rc# After making the edit:
git add <specific files>
git commit -m "fix(ws8e-review): <one-line description of what was changed>
<more detail if needed>
Co-Authored-By: Claude Opus 4.7 <[email protected]>"
Preference: additive commits over amend / force-push. PR review threads anchor to commits; amending breaks the anchors. The exception is a typo or trivial fix to the most recent commit that has no review comments — that's safe to amend.
Run: git push fork 6.0-rc
Expected: clean push. The PR auto-updates.
Wait for the fork's CI to re-run on the new HEAD; confirm all 5 workflows are green before re-requesting review.
Either in conversation or via the GitHub UI's "Re-request review" button.
Repeat Task 5 until the owner has no more change requests.
Files: none (uses gh pr merge or the GitHub UI)
Run: gh pr view <PR-number> -R Dacode45/ms-raylib-rs --json state,mergeable,reviewDecision,statusCheckRollup --jq '{state, mergeable, reviewDecision, checks: [.statusCheckRollup[].conclusion]}'
Expected: state: OPEN, mergeable: MERGEABLE, reviewDecision: APPROVED, all checks SUCCESS.
Option A — via gh:
gh pr merge <PR-number> -R Dacode45/ms-raylib-rs --merge --body "Merge raylib-rs 6.0 release prep checkpoint (WS1-WS8)"
(--merge selects "Create a merge commit". --squash and --rebase are the alternatives we explicitly reject.)
Option B — via the GitHub UI:
Run: git fetch fork && git log fork/unstable --oneline -5
Expected: top commit is a merge commit (two parents), with a message like "Merge pull request #N from Dacode45/6.0-rc". Below it are the WS8 commits (samples removal, paste advisory, Node.js bump, etc.) — they're reachable from unstable now.
Run: git show fork/unstable --no-patch --pretty='%h %p %s'
Expected: the merge commit shows TWO parents (%p prints two SHAs). Confirms --no-ff semantics.
Files:
Modify: CLAUDE.md (the status-line text in the "raylib 6.0 upgrade" section)
Step 1: Switch back to 6.0-rc if needed and pull the merge
Run: git checkout 6.0-rc && git pull fork unstable 2>&1 | tail -5
Expected: fast-forward or merge — 6.0-rc now contains the merge commit too (or its parent state is identical to what's on fork/unstable minus the merge commit). The cleanest move is to do the next edits ON the merged unstable directly; see Step 2 alternative.
Alternative: do the next edits ON fork/unstable directly.
git checkout -B post-ws8 fork/unstable # working branch from the merge
Run: grep -n '^\*\*Workstreams:\*\*' CLAUDE.md
Expected: one line, likely near the end of the "raylib 6.0 upgrade" section, listing WS0 ✅ · WS1 ✅ · ... · WS7 ✅ ... · WS8 release ← NEXT · WS9 showcase → ...
Change the WS8 entry from **WS8 release ← NEXT** (or whatever the current pending phrasing is) to:
WS8 ✅ — **release prep checkpoint done: version bumped 5.7.0 → 6.0.0, release-sys.yml + release-safe.yml drafted (workflow_dispatch-only, dry_run-gated), act-validated, samples/ removed in favor of showcase/, Node.js 24 actions, paste advisory accepted (see `docs/superpowers/notes/ws8-complete.md`; tracked-deferred: paste rewrite/swap, plus the WS6/WS7 carryover list)**
Update the trailing entries so the new pending workstream is **WS9 showcase ← NEXT** (or whatever the canonical next-step phrasing is — match the prior ← NEXT style).
Run: grep -A1 'WS8' CLAUDE.md | head -5
Expected: shows the new ✅ WS8 line.
docs/superpowers/notes/ws8-complete.mdFiles:
Create: docs/superpowers/notes/ws8-complete.md
Step 1: Inspect the WS7 done-note for shape reference
Run: head -40 docs/superpowers/notes/ws7-complete.md
Expected: shows the WS7 done-note structure (title, status line, spec reference, per-sub-step sections, "Done-criteria sign-off", "Tracked-deferred follow-ups").
Create docs/superpowers/notes/ws8-complete.md modeled on ws7-complete.md. Outline:
# WS8 complete — release prep + checkpoint
**Status:** DONE on branch `Dacode45:unstable` (fork only; canonical merge deferred). PR #<N> merged with merge-commit at <SHA>.
Spec: `docs/superpowers/specs/2026-05-29-ws8-release-prep-checkpoint-design.md`.
Plans: `docs/superpowers/plans/2026-05-29-ws8a-version-bump.md`, `…ws8b-release-workflows.md`, `…ws8c-act-validation.md`, `…ws8d-release-hygiene.md`, `…ws8e-fork-pr-checkpoint.md`.
Builds on WS7 (`docs/superpowers/notes/ws7-complete.md`) — 28-chapter mdBook + CHANGELOG 6.0.0 entry + 5 CI workflows green.
---
## WS8a — version bump
**Commit:** `<SHA>` — `feat(ws8a): bump raylib + raylib-sys to 6.0.0 + sync book snippets`.
What shipped:
- `raylib/Cargo.toml` version 5.7.0 → 6.0.0; inter-crate dep pin synced.
- `raylib-sys/Cargo.toml` version 5.7.0 → 6.0.0.
- `book/src/getting-started/quickstart.md` snippet `raylib = "5.7"` → `"6.0"`.
- CHANGELOG heading deliberately stays `## 6.0.0 (unreleased)` — date flip is the future final-release step's job.
---
## WS8b — release workflows
**Commit:** `<SHA>` — `feat(ws8b): draft release-sys.yml + release-safe.yml + sync-check helper`.
What shipped:
- `.github/workflows/release-sys.yml` and `release-safe.yml` — both `workflow_dispatch`-only with `dry_run: bool` input (default `true`).
- `scripts/check-release-sync.sh` — enforces version/CHANGELOG consistency; `--require-date` gate is the safety net for real publish (only fired when `dry_run: false`).
- Real publish gated behind `if: ${{ inputs.dry_run == false }}` so `CARGO_REGISTRY_TOKEN` is only consumed during intentional real publishes.
---
## WS8c — act validation
**Commit:** `<SHA>` — `docs(ws8c): validate release workflows via act + record runbook notes`.
What shipped:
- `docs/superpowers/notes/ws8c-validation.md` — three documented `act` runs (`release-sys + dry_run=true` → SUCCESS; `release-safe + dry_run=true` → fails at cargo step, documented limitation until raylib-sys 6.0.0 exists; `release-sys + dry_run=false` + dummy token → fails at sync-check gate, real publish step never reached).
- Fallback direct-validation sequence for maintainers without `act`.
- Final-release runbook teaser.
---
## WS8d — release-hygiene fold-ins
**Commits:**
- `<SHA-1>` — `chore(ws8d): bump Node.js-20 actions to Node.js-24 versions` (actions/checkout@v4 → @v5 across 5 legacy workflows).
- `<SHA-2>` — `chore(ws8d): accept paste cargo-deny advisory with rationale` (deny.toml ignore entry + CHANGELOG note).
- `<SHA-3>` — `feat(ws8d)!: remove samples/ — superseded by showcase/` (samples/ deletion + structopt dev-dep drop + README/CLAUDE.md/CHANGELOG updates).
---
## WS8e — fork PR checkpoint
PR #<N> opened `Dacode45:6.0-rc` → `Dacode45:unstable`. Owner-reviewed line-by-line. Merged with "Create a merge commit" (`--no-ff`) preserving per-WS commit groupings and backlog-PR `--author` headers.
CLAUDE.md status line flipped: WS7 ✅ → WS8 ✅; WS9 showcase ← NEXT.
---
## Final CI inventory (fork's unstable at merge HEAD)
| Workflow | Status |
|----------|--------|
| `check.yml` | ✅ |
| `test.yml` | ✅ |
| `web.yml` | ✅ |
| `sanitizers.yml` | ✅ |
| `book.yml` | ✅ |
| `release-sys.yml` | ⚪ workflow_dispatch-only; act-validated |
| `release-safe.yml` | ⚪ workflow_dispatch-only; act-validated with known limitation |
---
## WS8 done-criteria sign-off (spec §10)
- ✅ `raylib/Cargo.toml` + `raylib-sys/Cargo.toml` at 6.0.0; dep pin matches.
- ✅ Book quickstart shows `raylib = "6.0"`.
- ✅ CHANGELOG `## 6.0.0 (unreleased)` intact (date flip deferred).
- ✅ `release-sys.yml` + `release-safe.yml` exist; `act`-validated.
- ✅ `scripts/check-release-sync.sh` works for both crates without `--require-date`; fails with `--require-date`.
- ✅ All 5 existing CI workflows + 2 release workflows present.
- ✅ Node.js 24 actions across all 5 legacy workflows.
- ✅ `deny.toml` has rationale'd `paste` ignore entry; `cargo deny check` clean.
- ✅ `samples/` removed; `structopt` dev-dep gone; README/CLAUDE.md/CHANGELOG updated.
- ✅ `docs/superpowers/notes/ws8c-validation.md` exists (no placeholders).
- ✅ PR #<N> merged with "Create a merge commit"; fork's `unstable` HEAD is the merge commit.
- ✅ Tracked-deferred list updated; `paste` rewrite/swap on it.
- ✅ `CLAUDE.md` status line flipped: WS8 ✅, WS9 ← NEXT.
- ✅ This document (`ws8-complete.md`) written.
**Out of done-criteria (future)**: crates.io publication, `v6.0.0` tag, canonical merge, GitHub release.
---
## Tracked-deferred follow-ups (carried out of WS8)
From WS6/WS7 (unchanged):
1. Full PR #277 wrapper-soundness refactor.
2. `get_gamepad_button_pressed` transmute (`raylib/src/core/input.rs`).
3. `raylib-test` delete-or-fix — revisit at WS9.
4. `rlsw` on wasm32.
5. UBSAN through the FFI boundary.
6. Full rustdoc rewrite of remaining stub-level items.
7. Public Pages deploy → WS9.
8. Custom book theme / brand styling → WS9.
9. Safe abstractions for `GuiGetIcons`/`GuiLoadIcons` + PR #296.
10. End-to-end showcase examples → WS9.
New from WS8:
11. **`paste` macro rewrite or library swap** — investigate (a) rewriting macros without `paste` or (b) swapping to `pastey`/`paste2`. Stopgap is the WS8d `deny.toml` ignore entry.
Out of WS8, queued for future final-release workstream:
- CHANGELOG date flip.
- `v6.0.0` tag.
- Manual `release-sys.yml` + `release-safe.yml` real-publish runs.
- PR #2 `Dacode45:unstable` → `raylib-rs:unstable`.
- GitHub release creation.
---
WS8 done. Next: **WS9 (showcase)** — port all of raylib's examples to Rust, build them on desktop + wasm where applicable, assemble the GitHub Pages site (book + showcase gallery), and deploy it.
Fill in <SHA> / <SHA-1> / <SHA-2> / <SHA-3> / <N> with the actual values from the merge.
<SHA> / <N> placeholders remainRun: grep -n '<SHA\|<N>' docs/superpowers/notes/ws8-complete.md
Expected: zero matches.
Files:
Modify: CLAUDE.md
Create: docs/superpowers/notes/ws8-complete.md
Step 1: Confirm working-tree contents
Run: git status --short
Expected: M CLAUDE.md and ?? docs/superpowers/notes/ws8-complete.md. No other modifications. Untracked working files (TODO.md, prompt.md, next-session-prompt.md) remain untracked.
git add CLAUDE.md docs/superpowers/notes/ws8-complete.md
Run: git diff --cached --stat
Expected: 2 files; one modification, one new file.
git commit -m "$(cat <<'EOF'
docs(ws8): mark WS8 complete in CLAUDE.md + done-note
WS8 (release prep + checkpoint) is done on the fork's unstable branch.
PR #<N> merged with merge-commit at <SHA>.
CLAUDE.md status line: WS7 ✅ -> WS8 ✅; WS9 showcase <- NEXT.
docs/superpowers/notes/ws8-complete.md is the WS8 done-note,
modeled on the prior wsN-complete.md files; records the five
sub-step commits, the CI surface at the merge HEAD, and the
done-criteria sign-off.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
EOF
)"
Replace <N> and <SHA> with the actual values.
Run: git push fork 6.0-rc:unstable (if working from 6.0-rc)
or: git push fork post-ws8:unstable (if working from the post-ws8 branch from Task 7)
Expected: clean push to Dacode45:unstable with the final commit.
Run: git log fork/unstable --oneline -8
Expected: top entry is the docs(ws8): mark WS8 complete commit, then the merge commit (two parents), then the WS8d–WS8a commits, then the WS7 ones, etc.
Dacode45:6.0-rc → Dacode45:unstable) opened with the WS1–WS8 summary body.6.0-rc).unstable HEAD is a two-parent merge commit.docs/superpowers/notes/ws8-complete.md written with no <SHA> / <N> placeholders.Dacode45:unstable.The fork's Dacode45:unstable HEAD is the "ready for evaluation" snapshot. From here: