Three repos ship one toolchain. This is the shared version axis for all of them. GitHub milestones mirror what is listed here — add a version here first, then create the milestone, never the other way round.
Canonical copy lives in alplabai/tan-cli. alp-sdk and alp-sdk-vscode
should link here rather than keep their own divergent copies.
alp-sdk ──────────► tan ──────────► Alp IDE (alp-sdk-vscode)
the SoM SDK the CLI the VS Code extension
board.yaml, metadata, plans and a GUI over tan; runs it and
schemas, examples executes builds parses its JSON envelope
Dependencies run left to right, and only left to right. alp-sdk does not
know tan exists. tan reads an alp-sdk checkout. The extension shells tan
and never re-implements it.
That shape is why the three release on different cadences and why only two couplings actually matter:
| Coupling | Where it lives | What it controls |
|---|---|---|
SUPPORTED_CLI_VERSION |
alp-sdk-vscode, src/alpCli/service.ts |
which tan release the extension downloads |
PINNED_SDK_TAG |
tan-cli, .github/workflows/parity.yml |
which alp-sdk tan is tested against |
A tan release that no extension pin names reaches no IDE user. That is
deliberate — it is how a release candidate stays opt-in.
The stable line and the current opt-in pre-release line:
| Component | Stable | Opt-in / pre-release |
|---|---|---|
| alp-sdk | v0.17.0 (2026-10-13) | none live |
| tan | v0.6.0 | v0.7.0-rc1 (2026-10-10); 0.7.0 is dated on dev and tags next |
| Alp IDE | v0.6.0 | none live |
tan 0.7.0 requires alp-sdk v0.17.0, the first stable release that
contains its floor commit 4b206c8a60d56af318741491a6fecdc499dfcd36
(alp-sdk#2047, tan-cli#1258). docs/release-contract.md has the decision.
The Python tan line is now stable: v0.5.0 shipped general availability and
SUPPORTED_CLI_VERSION moved to it (now 0.5.1) on alp-sdk-vscode's main
branch — but main is not that repo's default branch; dev is
(alp-sdk-vscode's own default-branch setting), and dev has already moved
past it to SUPPORTED_CLI_VERSION = "0.6.0" (src/alpCli/service.ts:98
on dev, measured 2026-08-31 -- v0.6.0-rc1 graduated to the stable v0.6.0
tag and dev's pin followed it; see docs/release-contract.md for the live
authority on both). v0.6.0 (2026-08-24) is the newest tagged release — and,
as of that tag, GitHub's latest. main's stable pin has not moved onto it
yet, so a default (non-pre-release) IDE install still downloads v0.5.1. Alp
IDE's own stable cutover to consume it by default remains v0.6.x.
The minor version's parity is the channel. Odd minor (v0.3.x, v0.5.x) is
the pre-release channel; even minor (v0.4.x, v0.6.x) is stable. There is no
separate label to read — the number is the switch.
So a customer who does not opt into VS Code pre-releases stays on an even-minor
extension pinned to a stable tan, and is never moved onto the Python tan by
an automatic update.
Two things not to misread on the releases page: cli-rs-v* tags are a retired
in-repo Rust CLI, a dead lineage — never read them as the extension. And GitHub's
"Latest" badge marks the newest non-prerelease, so while the extension sits on
an odd minor, "Latest" is not the newest build.
Everything below is scaffolding for these. Neither is a checkbox — both are measured, on real hardware or against the real released binary.
Target 1 — a customer's first hour works. A fresh scaffold builds, flashes,
and blinks an LED on an E1M-AEN801, and survives a cold power-cycle. All of it
through tan, not through west build and JLinkExe. Only a cold power cycle
validly exercises this chain; RSetType 2; r; g mis-boots it.
Target 2 — a v0.4.1 user upgrades with no manual migration. Their existing
project, their existing .alp/sdk-path, their existing scripts and exit codes.
Established by RUNNING the v0.4.1 oracle while one existed; crates/ is now
deleted (tan-cli#269), so what remains of that measurement is the frozen
capture store at python/tests/fixtures/oracle_captures/ and the
contract/envelopes/* goldens. Cite those; do not reconstruct an answer from
memory or from a doc.
One arc: the Rust tan becomes a Python tan, and tan becomes the only
planner and executor. The release path, the planner and now the repo itself
have moved: python/tan/planner/ plans in-process, the Python executor runs
the plan, and crates/ is deleted. The remaining end state is alp-sdk dropping
its original planner copy.
The first tag at which a Python tan existed. It shipped the essential command
surface — build, generate/emit, doctor, sdk, kconfig, init,
flash, bootstrap, validate — plus the
{command, ok, exitCode, project, data, issues} envelope the extension parses,
and the v0.4.1 compatibility floor.
Assets are PyInstaller freezes of python/, on four targets. Windows arm64 and
Linux arm64 get no asset in this release: PyInstaller cannot cross-compile and
adding the available hosted arm64 runners was out of scope, not impossible.
Those hosts stay on the stable line or install the Python package from a
checkout.
Gated on Target 1 green on silicon.
SUPPORTED_CLI_VERSION moved; the Python tan is what customers get.
v0.5.1 (2026-08-05) followed as a patch on the same GA line and remains
alp-sdk-vscode's main-branch (stable) pin, SUPPORTED_CLI_VERSION = "0.5.1" — but is no longer GitHub's latest, and main is not
alp-sdk-vscode's default branch; see the v0.6.0 entry below and
docs/release-contract.md for both.
The command-surface work once planned for this milestone SHIPPED AS 0.5.0
and its issues moved to that milestone — all but #253, which moved to
v0.7.0 instead. So v0.6.0 carried a narrower remainder: the known
oracle divergences filed during the port (see the deferred label) and the
oracle's own retirement, which landed here rather than at v0.7.0. Most of
the full command surface had already landed inside the v0.5.0 RC cycle
instead of waiting for a later one — but it landed on three different
schedules, not one, so no single ref covers all of it:
- Eight verbs went from stub to real between rc4 and GA. The seven that
shipped as uniform stubs at rc1 (
scaffold,completion,diff,pinmux,inspect,trace,support-bundle— tan-cli#260, #257) plusrenode --sim-mode(#77) were first ported byac79d4c7(#352), which landed 2026-08-03T14:55:04Z — after rc4 published (2026-08-02T22:58:11Z) and before GA published (2026-08-04T18:50:19Z).git ls-treeatv0.5.0-rc4:python/tan/commandsstill has none of the seven verb modules, and rc4's owndeferred_cmd.pydocstring enumerates exactly those seven. new-som,monitor,faultdecodewere already real at rc1, ported by729234ad, whichgit tag --containsplaces insidev0.5.0-rc1. Run against the rc4 binary:tan faultdecode --cfsr 0x00000082 --hfsr 0x40000000returns a real DACCVIOL/MMARVALID/FORCED decode, andtan monitor --help/tan new-som --helpprint full option sets (Usage: tan monitor [OPTIONS]), not the stubs'[OPTIONS] [ARGS...]shape.ac79d4c7touched these three files too (new_som_cmd.py69 lines,faultdecode_cmd.py24,monitor_cmd.py528) but only to close #254/#255/#256 — gap-closure issues ("oracle parity, test coverage… Must keep existing and not error in 0.5.0", the same shape as #253) — not to port them.tan model buildwas also real at rc1, ported by the same729234ad. #253 (tracking its oracle-parity and test-coverage gap-closure) stayed open and moved tov0.7.0(where it has since closed) rather than closing with #254/#255/#256; the eightmodellifecycle subcommands beyondbuildremain unported (#674,v0.8.0).
tan renode is removed. The verb, tan/core/renode_plan.py,
tan/core/renode_sim.py and the 27 published renode.* issue codes are all
gone (verified: none of the three files exist on disk, tan registers 31
commands, not 32); renode --sim-mode (#77) shipped in the v0.5.0 RC cycle
and did not survive into v0.6.0. Renode is retired repo-wide, not paused:
alp-sdk#1539 re-instated and widened ADR-0022's retirement, deleting its four
pr-renode-* workflows and the examples/aen/aen-sim-vision example, and
parity.yml's seam2 job now stops at the ARM-ELF assertion instead of
booting the artefact. Dropping registered codes shrank the
envelope-contract.json release asset, so this was a breaking CLI-surface
change carried by v0.6.0, not an additive one.
Deferred is not a bug backlog — the deferred label means chosen, and each
issue records what the oracle does so the choice can be re-read later.
crates/ is deleted (tan-cli#269): the two crates, Cargo.toml,
Cargo.lock, the Rust-oracle parity suite under python/tests/parity/, and
the five cargo CI jobs. tan is the sole planner and executor, which is the
end state ADR-0020 names bar one item — alp-sdk still carries its own Python
planner copy.
The three-step order this was gated on, all now done:
- Out of the release path — at
v0.5.0-rc1. The RC ships PyInstaller freezes and no cargo build at all. After that tag, nothing customers receive is Rust. - Out of the correctness path. The oracle's observed behaviour was frozen
into committed captures while a working binary still existed — the only
item that got harder the longer it waited, since anything never captured is
unrecoverable once the binary is gone. Those captures survive the deletion,
at
python/tests/fixtures/oracle_captures/; a frozen answer also beats a live oracle, which can itself drift. - Out of the repo.
contract/was the one thing that could have been silently un-enforced by the deletion, sincecrates/tan-cli/tests/ contract.rswas one of its two gates. It is not:python/tests/ conformance/test_contract_envelopes.pyAUTO-DISCOVERS every case viaCONTRACT.iterdir(), where the Rust side listed 17 by hand — same 17 cases, and a new one is gated with nothing to remember.
Issues whose fix lived in crates/ and was never ported die with it. Each
carries a comment saying what survived the port and what did not.
The extension's pre-release line already consumes Python tan candidates; its
stable cutover remains the delivery mechanism. The remaining work is the stable
pin, the Linux target triple/archive handling, and an honest message on the two
platforms a Python tan release does not publish.
alp-sdk is upstream and independent; none of the above changes how it ships.
Its only obligations to the rest of the toolchain are to keep firing the
alp-sdk-planner-change dispatch when its contract surface moves, and —
once tan v0.7.0 is released — to drop its own Python planner now that
tan owns planning outright (alp-sdk still carries scripts/alp_orchestrate/
and tan-cli#270 is open).
Internal. Each is here because it has already cost a round.
- The oracle is gone; cite a CAPTURE, never a recollection. The rule used
to be "measure the oracle by RUNNING
target/debug/tan.exe, never by readingcrates/or docs" — source-reading gave a wrong answer twice.crates/is deleted (tan-cli#269), so there is nothing left to run: what an oracle behaviour WAS is now answerable only frompython/tests/fixtures/ oracle_captures/,contract/envelopes/*, or a provenance comment inpython/tan/**that records the original measurement. If none of those covers the question, the answer is not recoverable — decide the behaviour on its merits and say so, rather than asserting what the oracle "would have" done. - A conclusion is not a measurement. Two full rounds of wrong work came from believing a stale comment over the adjacent example.
- Parity is measured against
PINNED_SDK_TAG, on a clean LF-native clone. A dirty or differently-reffedalp-sdkproduces confident nonsense in both directions. contract/is live shared API data — edit it when the Python emit-site gates and consumer compatibility rules require the change. Its sole enforcer is nowpython/tests/conformance/test_contract_envelopes.py; keep that suite's auto-discovery intact rather than replacing it with a hand-written case list.- A frozen tree is measured at its own freeze vendor point; a shipped
Python surface is measured at
PINNED_SDK_TAG. Every parity gate has to pick one of the two — never compare a frozen tree against a moving pin. That is what makes the freeze absolute rather than something each pin bump re-negotiates. - LF is the convention. Check both
git diff --numstatand--ignore-cr-at-eol; a CRLF-only diff is invisible to one of them. - No exit codes behind pipes.
cmd | tailreportstail's status. - The bench is serial and reservation-gated. Verify
acquired:before every write and every reportable read. - tan-cli gates on its own pytest suite (
python -m pytest tests -qfrompython/) — the cargo checks that used to sit beside it are gone withcrates/. alp-sdk gates onbash scripts/test-all.sh. alp-sdk-vscode gates on its pnpm suite. They are not interchangeable.