Status: COMPLETE — HANDOFF ACCEPTED 2026-08-29. All seven gates passed; Phases 5 and 6 skipped by decision at Gate 5; Phase 7's runbooks, maintenance calendar, quarterly cold-eyes review, and final evidence pack are in place (evidence pack in the private workspace anchor). Brad accepted the handoff on 2026-08-29; the program is closed. Ongoing operation is governed by the operations runbook and maintenance calendar, not this document.
Owner: Brad / PyDevices
Canonical workspace: /home/brad/gh/pydevices
Revision: 2026-08-27, amended the same day for cloud execution (section 2.1). This file supersedes the 2026-08-26 draft in full and is self-contained. No amendment files exist; any reference to "Amendment A" or "Amendment C" is stale and must be ignored.
The goal of this program is that an expert maintainer inspecting any PyDevices repository cold — or the organization as a whole — finds no evidence of neglect and no dependence on Brad's private environment, and concludes they could not have done it materially better.
That bar has two lenses, and every phase must satisfy both:
- The rigor lens (think: a MicroPython core developer). Reads the code, the build glue, the patch queue, and the git log before the README. Values clean-clone builds, minimal and purposeful diffs, disciplined commit messages, and downstream patches maintained the way a Linux distribution maintains them: versioned, attributed, tested, individually justified.
- The user lens (think: an Adafruit maintainer). Opens the README first, then tries the quickstart. Values first-try success, examples that actually run, consistent patterns across repositories so learning one teaches all of them, predictable releases, and an organization that feels like one product.
The operational standard is deliberate everywhere: not flawless — no organization is — but nothing a visitor touches shows accident or neglect. Every rough edge is a labeled decision. Anything that works only in Brad's workspace is a defect by definition.
The end state, in priority order:
- Releases are boring. One reviewed pull request produces the tag, the GitHub Release with assets, the package publication, and the downstream updates — verified by an always-current health dashboard.
- Every supported public build stands alone. A user builds a component from its own repository, upstream MicroPython/CircuitPython, and declared dependencies. cmods is Brad's convenience, never a prerequisite.
- Downstream runtime changes have a real home. Patches and variants live in a versioned, tested overlay with provenance — credibility that does not depend on upstream pull requests.
- A stranger succeeds. README to working example on a supported target, first try, for every active repository.
- The organization runs hands-off between decisions. Bots prepare, verify, and report; Brad reviews and merges. Nothing publishes itself beyond explicitly approved policy.
- Optionally, a central cockpit (Codespaces/devcontainer) for Brad and attached agents — a convenience surface, never a hidden requirement.
Explicit non-goals: no rename or rebrand (names are frozen for this program); no long-lived MicroPython fork; upstream pull requests are possible outcomes, never prerequisites.
Execution is a cloud handoff: the executing agent runs in Claude cloud sessions (section 2.1), not on Brad's workstation. The agent must not change repositories, GitHub settings, package registries, or other external state until all of the following hold:
-
The in-flight instrument-library program on
audiodspandmicropython-vst3(rule 9) is completely finished,micropython-vst3(currently the only workspace repository with no GitHub remote, verified 2026-08-27) has been published, and both repositories' remotes are in sync with Brad's local checkouts. -
The pre-handoff checklist (section 2.2) is complete.
-
Brad gives the exact instruction:
GO
Before GO, the agent may read this roadmap, read the pushed repositories, and answer questions, but must not begin discovery probes or implementation.
Amended at GO (2026-08-28, per Brad): the executor is a local Claude session on Brad's workstation, working in the canonical workspace — not a cloud session. The cloud-handoff design below is retained as an optional future mode; where it says "cloud session", read "executing session". The proof surface, checkpoint-packet, and gate rules are unchanged, except that on-hardware and workstation-only verification no longer needs a separate session.
- The original design: a Claude cloud session authenticated through the Claude
GitHub app, installed org-wide with all-repository access (verified
2026-08-27) — covering the private
workspaceanchor repository; the rest of the organization is public. - The session anchor is the private
workspacerepository: a session starts there and runs itsbootstrap.sh, which carries the list of every PyDevices repository itself and clones or fetches each into the anchor checkout, git-ignored there. The anchor also holds private program documents (gate packets, audit findings, in itsdocs/) and maintainer tooling — and, by its boundary rule, never anything a public build needs. Every session begins by bootstrapping and reading this roadmap. - The cloud sees only what is pushed. Local working trees, uncommitted changes, and workstation state are invisible; anything the program needs must reach a remote first.
- The proof surface is CI. GitHub Actions runs (including Windows runners) are the clean-environment evidence for the scenarios; the browser simulator may run headless in-session. On-hardware verification is owned by Brad or an explicitly requested workstation session and is recorded as such (scenario S4).
- Work proceeds session by session: checkpoint packets land as PR
descriptions, issues, or the anchor repository's private
docs/; gates map to session boundaries; nothing may depend on memory that is not in a repository.
Completed from the workstation before the handoff; the cloud agent verifies rather than performs these:
- Capture the uncommitted Adafruit_MP3 Windows patch — done 2026-08-27:
cmods/patches/adafruit_mp3/0001-windows-msvc-inline-assembly.patch. - Push every in-scope repository so remotes match local. (
audiodspandmicropython-vst3sync is owned by the in-flight program's completion, condition 1 above.) - Grant the Claude GitHub app access to the in-scope repositories.
- Rebuild and commit the distributed interpreter binaries (
pydevices/bin, the site and workbench wasm) from merged, pushed sources only — never from an unmerged branch.
- Treat this roadmap as the controlling plan. If a material architectural change emerges, update this file in a reviewable change and obtain approval before continuing.
- Inspect before changing anything.
- Never expose, print, copy, or commit secrets. In particular, never read,
move, or upload the
pydevices-release-automationGitHub App private key; Brad installs credentials himself. - Never rewrite public history.
- Never delete a repository, release, package, tag, branch, or user asset without specific approval for that exact destructive action.
- Never rename or transfer a repository or organization.
- Never publish to production PyPI or another production registry without a separate explicit approval naming the package.
- Keep changes reviewable and rollbackable. Default to at most five repositories per approval batch.
- Preserve user work and unrelated local changes. In-flight work outranks
this program: repositories with an active plan or feature branch (at
revision time:
audiodsponinstrument-library-tier,micropython-vst3oninstrument-library-cutover, per the audiodsp instrument-library refactor) must not be modified without checking with Brad first. - Prefer reversible changes: draft PRs, dry runs, previews, TestPyPI.
- Use least-privilege credentials and pinned GitHub Action revisions.
- Do not broaden scope because a convenient adjacent change is available.
- Do not create or name a new repository (including the runtime overlay) until Brad approves its ownership boundary and name.
- Stop at every gate. Approval at one gate does not imply approval at the
next. Continue only on the exact phrase
APPROVE GATE n. - Deliberate exceptions (section 4.2) are design decisions, not defects. Do not "fix" them; record them in the exceptions ledger and design around them.
A prior audit (2026-08-26) and working history have already established the baseline below. The agent must spot-check these cheaply, not re-derive them. Anything that fails a spot-check is a finding; everything else is carried forward as fact.
- Workflow drift — DONE (2026-08-29): every caller now pins the
immutable
publishing-v6contract (v5 lived hours before a live MIP race fix superseded it; v3/v4/v5 tags untouched, now ruleset-protected). Release PRs, tag-on-merge, assets, pre-releases, and the health dashboard shipped with it. - workbench release contract broken: its release workflow fires only on
mcp-v*tags, but the repo carriesv0.6.xtags — tags exist with no GitHub Releases. - lvgl release race — FIXED by design (2026-08-28 generator
overhaul): the trigger workflow is retired; consumers sync via their
own scripts pinning the exact source commit in
LVGL_BINDINGS_COMMIT(branch refs rejected), and releases are explicit dispatches. The single-writer rule (edit only inlvgl-bindings) still needs stating in the READMEs where a contributor would trip (Phase 4). - No branch protection or rulesets on the publishing repositories.
- Production PyPI names unregistered. All eight distributions exist only on TestPyPI; the production names were free when checked. This is a name-squatting exposure and the most urgent external action in the program (Gate 1 decision).
- Releases ship no assets — FIXED by publishing-v6 (every publish attaches its distributions; interpreters ship as checksummed release assets on pydevices releases).
- Retired PyDevices URLs 404 in search results. Add redirects; this is also the standing argument for the name freeze.
- audiodsp resolves dependencies by sibling path (ulab, Adafruit_MP3).
The Windows compatibility patch to Adafruit_MP3 (
assembly.h) was captured pre-handoff tocmods/patches/adafruit_mp3/(checklist 2.2); in Phase 2 its ownership moves to the native pilot's patch queue. - Runtime patches and variants live only in cmods: MicroPython Windows
networking/SSL/select mailbox patches applied by
build_mp.sh, Windows FFI, WebAssembly port additions and the external wasmvariantsdirectory, wasmbridge, fetch-backed requests, wasm locking. No public home, no versioned application process. - The CircuitPython oracle pin is not a mechanism:
cmods/circuitpythonis a detached HEAD at tag 10.2.1 declared in no repository; a fresh clone could land elsewhere and silently invalidate every parity golden. Declare the pin in a checked-in location with a status warning. (Do not move the pin — see section 4.2.) - Generated artifacts without drift checks:
lvgl-bindings/fonts/*.binare committed outputs no CI regenerates; a moved lvgl pin silently diverges them. The generated-code contract (section 6, scenario S2 family) must cover this class. - Interpreter binaries tracked in git:
pydevices/bincarries ~34 MB of rebuilt interpreter binaries withcore.fileMode false, producing the recurring lost-execute-bit failure class (rc=126). Propose moving binary distribution to release assets with a fetch script; Brad decides. - Stale TestPyPI leaf packages (
pydevices-multimer,-displaydev,-events,-keysat 0.1.3) look like broken releases to a stranger. They are retired (section 4.2); label or clean up so the appearance matches the decision.
These are decisions, not drift. The Definition of Done (section 9) is satisfied with them in place, provided each is recorded and visible where a stranger would otherwise misread it.
- Versions are chosen by a human. Nothing computes the next version. Release automation must preserve this: the release PR is where the human reads, edits, and approves the version before merging.
- Only
pydevices,pydevices-desktop,pygraphics,palettes,pdwidgets, andaudiodsppublish (audiodsp ships three distributions:pydevices-audiodsp,-audioinstruments,-audioeffects— added post-handoff, 2026-08). The pydevices leaf distributions are retired. - lvgl-python is a sync target. Helpers and bindings are edited only
in
lvgl-bindings; propagation is explicit consumer sync pinned byLVGL_BINDINGS_COMMIT(since the 2026-08-28 overhaul — formerly a trigger workflow). Onlylvgl-pythonpublishes. - The CircuitPython oracle stays pinned at 10.2.1 while audiodsp parity work is live. Moving it is a re-port and its own future phase, owned by the audiodsp plan, not this program.
- micropython-vst3 publishes nothing and carries no hosted CI, by decision (restated 2026-08-29, now that it is public: its gate is the local 14-test ctest suite, lint included; hosted CI arrives with the post-program rename/refactor of this repo, not before). Its engine build also still routes through cmods — an accepted, documented §5.2 exception scoped to this repo until that same refactor.
Additions to this ledger require Brad's approval and must state what a stranger will see and where the decision is documented.
The original priority stands: the highest-value move is making releases boring and dependable. Release automation is Phase 1, not a reward at the end. It is also what makes every later phase cheap to verify — each subsequent change rides an automated, observable release path instead of a manual one.
Target: cmods ──▶ component's public build contract ──▶ MicroPython
└── declared, pinned dependencies
Standalone user: MicroPython + component repo + declared dependencies
cmods may aggregate, pin known-good portfolios, cache toolchains, and compose
advanced profiles — as a consumer of public contracts. No supported public
build may require knowledge or files that exist only in cmods. MicroPython's
external C module mechanism (micropython.mk, micropython.cmake, manifests)
makes this achievable without upstream changes.
A meticulously maintained downstream patch queue — mailbox format, provenance,
compatible-version range, ordered series, a test per patch, scheduled
application checks against upstream — is rarer and more persuasive to the
rigor lens than upstream PRs. Upstreaming becomes an explicit per-patch
decision made from strength. (audiodsp has already found and fixed real
upstream bugs — the synthio oscillator wrap, the Mixer reset, the biquad
peaking sign — documented in its docs/upstream-diff.md; reporting those
upstream proceeds on the audiodsp plan's own schedule.)
Bots prepare changes, validate releases, merge narrowly defined low-risk updates, and keep a health dashboard current. They never silently publish, never make architectural decisions, and never paper over a broken contract.
Checklists reach "nothing is broken"; the bar in section 1 is reached by evidence of care. When a rule in this document fights clarity or quality in a specific case, the agent proposes the deviation at the next checkpoint instead of complying badly or deviating silently.
Roughly twenty repositories need attention. audiodsp and the runtime patches are evidence of portfolio-wide problems, not the center of the program. The pilot set is chosen at Gate 1 for archetype coverage; audiodsp may be in it on merit, but nothing is designed around any single repository.
Scenarios are the currency of this program: each gate is defined by named scenarios passing with recorded evidence (exact commands, transcripts, links), not by checklist completion. A scenario passes only from a clean environment with no cmods present unless the scenario says otherwise.
- S1 — The cold build (rigor): On a clean machine, using only a component repository's documentation, clone it plus upstream MicroPython/CircuitPython plus its declared dependencies; build the documented profile; run its tests. Missing dependencies fail early with a useful message. Evidence from a GitHub Actions run on a clean runner (Linux or Windows) satisfies the clean-machine requirement.
- S2 — The patch reader (rigor): Open the patch queue cold. Every patch states purpose, provenance, upstream-version range, and order; the series applies cleanly to the pinned upstream; each patch's effect is demonstrated by a test; scheduled CI proves the series against supported upstream versions and fails loudly when it stops applying. Generated committed artifacts (bindings, font bins) have drift checks in the same spirit.
- S3 — The log reader (rigor):
git logfrom program start onward reads as disciplined: scoped, single-purpose commits with honest messages. History before the program is what it is (rule 4); the line where the log becomes disciplined is itself the statement. - S4 — The quickstart (user): A stranger with a supported target follows the README from the top and reaches a working example on the first try. Examples are exercised in CI where feasible (the browser simulator makes this practical for display-oriented repos).
- S5 — The boring release (operations): A release is: review one generated PR (human edits/approves the version — section 4.2), merge. Tag, GitHub Release with assets, package publication, MIP lock bump, and docs follow automatically; the Release Health dashboard turns green or says exactly what failed. No unwatched manual steps.
- S6 — The org walk (both): From the organization landing page into any repository: accurate description, topics, status label, license, support boundary, live links, no retired-URL 404s, and nothing that looks abandoned without saying so.
- S7 — The absent maintainer: For thirty days Brad only reviews and merges. Dependency updates keep flowing, health stays visible, nothing publishes beyond approved policy, and the runbooks are sufficient for an agent to triage a failure without improvisation.
After GO:
- Bootstrap the workspace with the anchor's
bootstrap.sh(section 2.1); report anything its list missed as a finding, and extend the list as the inventory completes. - Spot-check every section 4.1 finding; record deltas. Verify the pre-handoff checklist (section 2.2) actually holds.
- Complete the inventory: every local and GitHub repository including the newly published one(s) — role, audience, archetype, lifecycle, release path, dependency set, cmods assumptions. Focus effort on what is unknown; do not re-derive section 4.
- Map every publisher's exact release path end to end (VERSION file, tag match enforcement, TestPyPI, MIP lock bump).
- Propose: the Phase 1 release pilot (default:
palettes, the lowest-risk pure-Python publisher); the Phase 2 native pilot; the runtime overlay boundary and name (provisionallymicropython-pydevices); the production PyPI registration list; the workbench tag policy.
Present: inventory and delta report; archetype/lifecycle table; release-path
map; pilot proposals; overlay proposal; and at most five decisions, which
must include production PyPI name registration (finding 5 — urgent) and
the overlay name/boundary. Continue on APPROVE GATE 1.
- Define
publishing-v5as an immutable shared-workflow contract; leave v3/v4 tags in place; migrate callers batch by batch. - Introduce release-PR automation (Release Please or equivalent) using the
existing
pydevices-release-automationGitHub App token (required so the created release triggers downstream workflows; plainGITHUB_TOKENdoes not). The release PR carriesVERSIONandCHANGELOG.md; the human version choice happens in PR review (section 4.2). - Pilot on the Gate 1 pilot repo as a dry run; nothing publishes until Brad merges the generated PR.
- Add branch protection and required checks to the publishing repositories before any auto-merge behavior exists.
- Configure production PyPI pending Trusted Publishers for the approved
names, with a protected
pypienvironment. Production publication itself still requires explicit per-package approval (rule 7). - Stand up the Release Health dashboard (one maintained issue or page: version, tag, Release, assets, TestPyPI/PyPI, MIP, downstream run status) before any automatic publishing is enabled.
- Attach release assets. Fix the workbench tag contract per the Gate 1 decision. Serialize the lvgl-bindings sync (finding 3) and document the single-writer rule where a contributor would trip on it. Label or clean up the stale TestPyPI leaves (finding 13).
- Roll out to the remaining publishers (
pdwidgets,pygraphics,pydevices+pydevices-desktop,mpftp,audiodsp— the last only in coordination with its in-flight plan, rule 9) in batches of at most five.
Present: S5 evidence on the pilots (dry-run transcript, dashboard live, a real
TestPyPI release end to end), protection/ruleset previews, rollback notes.
Continue on APPROVE GATE 2.
- Make the native pilot own its public build glue:
micropython.mk/micropython.cmake/ manifest, declared and pinned dependencies (ulab, Adafruit_MP3), feature flags, dependency checks with early failures, and a clean-build test. Its patch queue owns the Adafruit_MP3 Windows patch. - Create the runtime overlay repository (only as approved at Gate 1):
the versioned home for the Windows networking/SSL/select patches, Windows
FFI, the WebAssembly port additions and external variant, wasmbridge,
fetch support, and wasm locking. Pinned upstream release; ordered mailbox
series; disposable-worktree builds; per-profile tests; provenance recording
patch checksums and source revisions; scheduled compatibility checks
against supported upstreams. Initial profiles:
windows-networked,windows-full,desktop-pydevices,webassembly-pydevices. Release ids of the formmp-v1.28.0-pydevices.1. Publishing overlay binaries is a separate approval from publishing the overlay source. - Declare the CircuitPython oracle pin in a checked-in location with an
apply_cp_patches.sh --statusmismatch warning (finding 10) — without moving the pin (section 4.2) and in coordination with the audiodsp plan. - Add drift checks for committed generated artifacts (finding 11).
- Present the interpreter-binary distribution proposal (finding 12); execute only Brad's decision.
- Convert cmods to delegate: its build commands call the public contracts. Prove every pilot build passes with cmods absent from the machine.
Present: S1 and S2 passing with evidence on the pilots; cmods-absent proof;
cmods delegation demo; the overlay's first green scheduled compatibility run;
patch provenance table. Continue on APPROVE GATE 3.
- README and quickstart pass for every active repository, governed by the
existing
doc-style.md; support status and boundary stated; feature flags and optional dependencies documented where they gate behavior. - Examples exercised in CI where the simulator or unix port makes it feasible; the rest marked as hardware-verified with the board named.
- Organization presence: landing page, descriptions, topics, status labels, pinned repositories, consistent lifecycle signals; redirects for the retired URLs (finding 7); PyDevices.github.io aligned with all of it.
Present: S4 evidence (a cold-run transcript per pilot-class repo), the S6
walk recorded, before/after of the organization landing. Continue on
APPROVE GATE 4.
Apply the proven contracts to every remaining in-scope repository in batches of at most five, choosing per-archetype what applies (a template repo does not get a patch queue; an app gets a dependency manifest and bootstrap). Every repository ends with an explicit role, lifecycle label, dependency contract, clean validation path, and release status — or a recorded exception with an owner and exit criteria. Standard checkpoint packet at each batch boundary.
Present: the contract matrix (done / remaining / excepted), final ownership
maps for patches and integration assets, cmods scope statement, dashboard
showing the whole portfolio. Continue on APPROVE GATE 5.
With execution in the cloud, no agent needs a Codespace: the executor brings
its own sandbox and bootstraps from the workspace anchor's bootstrap.sh,
which exists from Phase 0 (section 2.1). This phase covers only Brad's
optional hands-on browser environment; his default cockpit is GitHub itself —
PR review plus the Release Health dashboard.
- If wanted: a devcontainer that runs the same
bootstrap.sh— one environment definition with two consumers, never a parallel procedure. - Health checks for missing tools, repositories, and credentials.
- Document agent and credential boundaries: what each agent kind (cloud executor, workstation session, review bots) can see, what is deliberately withheld, and how its output enters review. Agents never hold release credentials in any environment.
- Local WSL/Linux use remains first-class. If a repository build works only inside the cockpit, that is a Phase 2 regression.
Present: rebuild-from-bootstrap evidence, local-parity evidence, credential
boundary documentation, recurring cost estimate. Continue on APPROVE GATE 6.
Only on explicit fresh approval, executed one pilot action at a time with a stop after the first: production-publication automation policy beyond the human merge gate; any archival; any rename or transfer (also requires lifting the section 1 name freeze). Approval of one action never authorizes another.
- Runbooks: release, rollback, incident, bot-failure recovery, onboarding another maintainer or agent.
- A concise recurring-maintenance calendar (upstream pin reviews, patch compatibility, dependency policy, dashboard review).
- The standing cold-eyes review: a scheduled (quarterly, or per major release) re-run of scenarios S1, S4, S5, and S6 on a rotating sample of repositories, filed as an issue with findings. The bar decays without enforcement; this is the enforcement.
- Final evidence pack: S1–S7 current, exceptions ledger current, list of actions that still require Brad personally.
The program is complete only when Brad accepts the handoff.
- Component-specific integration assets live in the component repository.
- Runtime-wide patches and variants live in the overlay.
- Shared tooling is extracted only when it has multiple real consumers and a stable interface.
- Maintainer-only experiments may stay in cmods if labeled and not required by any supported public build.
- No patch, variant, or glue file has two undocumented source-of-truth
copies.
lvgl-bindingsremains the single writer for its sync targets.
The program is done when the seven scenarios pass with current evidence across the portfolio (per-archetype applicability decided at Gate 5), the exceptions ledger is complete and each exception is documented where a stranger would otherwise misread it, the standing review is scheduled, and Brad has approved every gate and the handoff.
Equivalently: an expert applying either lens to any in-scope repository, or to the organization entire, finds only decisions — never accidents.
At every gate and batch boundary:
- Outcome first.
- Scope completed, scope remaining.
- Files, PRs, settings, or external objects changed.
- Scenario evidence (which scenarios, exact commands, results).
- Failures, exceptions, and uncertainty — stated plainly.
- Security, publication, and cost implications.
- Rollback procedure.
- Exceptions-ledger changes proposed.
- At most five decisions requested from Brad, and the exact approval phrase required to continue.