diff --git a/README.md b/README.md
index eda2e6e..506cc53 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ This repository contains the following packages:
* [@turing-machine-js/library-binary-numbers](https://github.com/mellonis/turing-machine-js/tree/master/packages/library-binary-numbers) — binary arithmetic on a 5-symbol alphabet (` ^$01`) supporting multiple numbers per tape, with `plusOne`, `minusOne`, `minusOneFast`, `invertNumber`, `normalizeNumber`, and inter-number navigation.
* [@turing-machine-js/library-binary-numbers-bare](https://github.com/mellonis/turing-machine-js/tree/master/packages/library-binary-numbers-bare) — the same arithmetic on a 3-symbol alphabet (` 01`), single-number-per-tape. Side-by-side with the marker-based library to make the alphabet-size vs state-graph-size trade-off visible.
* [@turing-machine-js/builder](https://github.com/mellonis/turing-machine-js/tree/master/packages/builder) — declarative state-table construction. Not actively developed; the same pattern is shown inline in `@turing-machine-js/machine`'s README.
-* [@turing-machine-js/visuals](https://github.com/mellonis/turing-machine-js/tree/v7/packages/visuals) (v7+, prerelease) — pure highlight + graph-indexing logic for the engine `Graph`: `applyHighlight` / `applyIndicator` against a renderer-agnostic `HighlightOps` interface, `bareIdOf` / `highlightExpand` for wrapper/bare canonicalization, `formatStepNotation` / `tokenizeStep` / `formatTape` for engine-edge-label rendering (byte-identical to `toMermaid`), `recordSnippet` for prerecorded-playback artifacts. No DOM, no Svelte, no Mermaid — consumers bring their own renderer.
+* [@turing-machine-js/visuals](https://github.com/mellonis/turing-machine-js/tree/master/packages/visuals) (v7+) — pure highlight + graph-indexing logic for the engine `Graph`: `applyHighlight` / `applyIndicator` against a renderer-agnostic `HighlightOps` interface, `bareIdOf` / `highlightExpand` for wrapper/bare canonicalization, `formatStepNotation` / `tokenizeStep` / `formatTape` for engine-edge-label rendering (byte-identical to `toMermaid`), `recordSnippet` for prerecorded-playback artifacts. No DOM, no Svelte, no Mermaid — consumers bring their own renderer.
# An example
diff --git a/lerna.json b/lerna.json
index a123db3..e9b371b 100644
--- a/lerna.json
+++ b/lerna.json
@@ -3,6 +3,6 @@
"packages": [
"packages/*"
],
- "version": "7.0.0-alpha.8",
+ "version": "7.0.0",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index db25187..21e0729 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10393,40 +10393,40 @@
},
"packages/builder": {
"name": "@turing-machine-js/builder",
- "version": "7.0.0-alpha.8",
+ "version": "7.0.0",
"license": "GPL-3.0-or-later",
"engines": {
"npm": ">=7.0.0"
},
"peerDependencies": {
- "@turing-machine-js/machine": "^7.0.0-alpha.8"
+ "@turing-machine-js/machine": "^7.0.0"
}
},
"packages/library-binary-numbers": {
"name": "@turing-machine-js/library-binary-numbers",
- "version": "7.0.0-alpha.8",
+ "version": "7.0.0",
"license": "GPL-3.0-or-later",
"engines": {
"npm": ">=7.0.0"
},
"peerDependencies": {
- "@turing-machine-js/machine": "^7.0.0-alpha.8"
+ "@turing-machine-js/machine": "^7.0.0"
}
},
"packages/library-binary-numbers-bare": {
"name": "@turing-machine-js/library-binary-numbers-bare",
- "version": "7.0.0-alpha.8",
+ "version": "7.0.0",
"license": "GPL-3.0-or-later",
"engines": {
"npm": ">=7.0.0"
},
"peerDependencies": {
- "@turing-machine-js/machine": "^7.0.0-alpha.8"
+ "@turing-machine-js/machine": "^7.0.0"
}
},
"packages/machine": {
"name": "@turing-machine-js/machine",
- "version": "7.0.0-alpha.8",
+ "version": "7.0.0",
"license": "GPL-3.0-or-later",
"engines": {
"npm": ">=7.0.0"
@@ -10434,13 +10434,13 @@
},
"packages/visuals": {
"name": "@turing-machine-js/visuals",
- "version": "7.0.0-alpha.8",
+ "version": "7.0.0",
"license": "GPL-3.0-or-later",
"engines": {
"npm": ">=7.0.0"
},
"peerDependencies": {
- "@turing-machine-js/machine": "^7.0.0-alpha.8"
+ "@turing-machine-js/machine": "^7.0.0"
}
}
}
diff --git a/packages/builder/CHANGELOG.md b/packages/builder/CHANGELOG.md
index 5cd086a..5055de6 100644
--- a/packages/builder/CHANGELOG.md
+++ b/packages/builder/CHANGELOG.md
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [7.0.0] - 2026-06-03
+
+Stable v7. Lockstep release with `@turing-machine-js/machine` 7.0.0. See the machine package CHANGELOG for the cumulative v7 trajectory.
+
+### Changed
+
+- Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.8` → `^7.0.0`.
+
## [7.0.0-alpha.8] - 2026-06-02
Released in lockstep with `@turing-machine-js/machine` 7.0.0-alpha.8 — lifts `TapeSnapshot` + `tapeViewport` from `@turing-machine-js/visuals` into the engine ([#227](https://github.com/mellonis/turing-machine-js/issues/227)). No source or behavior changes in this package. Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.7` → `^7.0.0-alpha.8`.
diff --git a/packages/builder/package.json b/packages/builder/package.json
index 1a8bd77..f92bf6c 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -1,6 +1,6 @@
{
"name": "@turing-machine-js/builder",
- "version": "7.0.0-alpha.8",
+ "version": "7.0.0",
"description": "A turing machine builder — declarative state-table construction. Not actively developed by the author; the same state-table pattern is also shown as an inline example in @turing-machine-js/machine's README. Contributions welcome.",
"engines": {
"npm": ">=7.0.0"
@@ -25,7 +25,7 @@
"builder"
],
"peerDependencies": {
- "@turing-machine-js/machine": "^7.0.0-alpha.8"
+ "@turing-machine-js/machine": "^7.0.0"
},
"scripts": {
"build": "tsc --build --verbose tsconfig.build.json && node ../../scripts/build-node-entries.mjs --package=@turing-machine-js/builder",
diff --git a/packages/library-binary-numbers-bare/CHANGELOG.md b/packages/library-binary-numbers-bare/CHANGELOG.md
index 9daa243..7cb2a5b 100644
--- a/packages/library-binary-numbers-bare/CHANGELOG.md
+++ b/packages/library-binary-numbers-bare/CHANGELOG.md
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [7.0.0] - 2026-06-03
+
+Stable v7. Lockstep release with `@turing-machine-js/machine` 7.0.0. See the machine package CHANGELOG for the cumulative v7 trajectory.
+
+### Changed
+
+- Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.8` → `^7.0.0`.
+
## [7.0.0-alpha.8] - 2026-06-02
Released in lockstep with `@turing-machine-js/machine` 7.0.0-alpha.8 — lifts `TapeSnapshot` + `tapeViewport` from `@turing-machine-js/visuals` into the engine ([#227](https://github.com/mellonis/turing-machine-js/issues/227)). No source or behavior changes in this package. Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.7` → `^7.0.0-alpha.8`.
diff --git a/packages/library-binary-numbers-bare/package.json b/packages/library-binary-numbers-bare/package.json
index f579b9c..6a70b47 100644
--- a/packages/library-binary-numbers-bare/package.json
+++ b/packages/library-binary-numbers-bare/package.json
@@ -1,6 +1,6 @@
{
"name": "@turing-machine-js/library-binary-numbers-bare",
- "version": "7.0.0-alpha.8",
+ "version": "7.0.0",
"description": "Single-number binary arithmetic on a 3-symbol alphabet (blank, 0, 1) — same operations as @turing-machine-js/library-binary-numbers but without ^/$ markers. Side-by-side with the marker-based library for learning the trade-off.",
"engines": {
"npm": ">=7.0.0"
@@ -28,7 +28,7 @@
"teaching"
],
"peerDependencies": {
- "@turing-machine-js/machine": "^7.0.0-alpha.8"
+ "@turing-machine-js/machine": "^7.0.0"
},
"scripts": {
"build": "tsc --build --verbose tsconfig.build.json && node ../../scripts/build-node-entries.mjs --package=@turing-machine-js/library-binary-numbers-bare",
diff --git a/packages/library-binary-numbers/CHANGELOG.md b/packages/library-binary-numbers/CHANGELOG.md
index 926499d..98c8c74 100644
--- a/packages/library-binary-numbers/CHANGELOG.md
+++ b/packages/library-binary-numbers/CHANGELOG.md
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [7.0.0] - 2026-06-03
+
+Stable v7. Lockstep release with `@turing-machine-js/machine` 7.0.0. See the machine package CHANGELOG for the cumulative v7 trajectory.
+
+### Changed
+
+- Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.8` → `^7.0.0`.
+
## [7.0.0-alpha.8] - 2026-06-02
Released in lockstep with `@turing-machine-js/machine` 7.0.0-alpha.8 — lifts `TapeSnapshot` + `tapeViewport` from `@turing-machine-js/visuals` into the engine ([#227](https://github.com/mellonis/turing-machine-js/issues/227)). No source or behavior changes in this package. Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.7` → `^7.0.0-alpha.8`.
diff --git a/packages/library-binary-numbers/package.json b/packages/library-binary-numbers/package.json
index a3960c3..bcb8fb7 100644
--- a/packages/library-binary-numbers/package.json
+++ b/packages/library-binary-numbers/package.json
@@ -1,6 +1,6 @@
{
"name": "@turing-machine-js/library-binary-numbers",
- "version": "7.0.0-alpha.8",
+ "version": "7.0.0",
"description": "A standard library for working with binary numbers",
"engines": {
"npm": ">=7.0.0"
@@ -27,7 +27,7 @@
"numbers"
],
"peerDependencies": {
- "@turing-machine-js/machine": "^7.0.0-alpha.8"
+ "@turing-machine-js/machine": "^7.0.0"
},
"scripts": {
"build": "tsc --build --verbose tsconfig.build.json && node ../../scripts/build-node-entries.mjs --package=@turing-machine-js/library-binary-numbers",
diff --git a/packages/machine/CHANGELOG.md b/packages/machine/CHANGELOG.md
index 42fb271..1e951ae 100644
--- a/packages/machine/CHANGELOG.md
+++ b/packages/machine/CHANGELOG.md
@@ -4,6 +4,87 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [7.0.0] - 2026-06-03
+
+Stable v7. The composition-representation overhaul. See alpha.1 through alpha.8 entries below for the detailed step-by-step trajectory; this entry summarizes the cumulative API changes from v6.4.0.
+
+### Added
+
+- **`DebugSession`** — `new DebugSession(machine, { initialState })`. The sole interactive-debugging surface (there is no `debugRun()` factory on `TuringMachine`). Emits `pause` / `step` / `iter` / `halt` events via `session.on(event, listener)`; `iter` listeners are awaited (the per-iter throttle / coordination point), `pause` / `step` / `halt` are fire-and-forget. Drive controls: `continue()`, `stepIn()`, `stepOver()`, `stepOut()`, external `pause()`, `stop()`, and `setRunInterval(ms)` for a per-iter throttle. Breakpoint detection (`state.debug` filters, `haltState.debug`) lives entirely here. Concurrent sessions on one machine are rejected (the underlying `TapeBlock` lock is single-active-run). Depth-based step controls mirror Chrome DevTools: `stepIn` = next iter at any depth; `stepOver` = next iter at `depth ≤ click-time depth`; `stepOut` = next iter at `depth < click-time depth` (throws at depth 0).
+- **`CallFrame extends State`** — `withOverriddenHaltState`'s wrapper is now a first-class `State` subclass instead of an instance aliasing the bare's private `#symbolToDataMap` / `#debugRef`. Transition lookups and `debug` access delegate to the bare. `instanceof State` is preserved; `instanceof CallFrame` is the wrapper discriminator. Exported additively from the package root.
+- **`TapeSnapshot` type + `tapeViewport` helper** — the per-tape wire-data shape `{ symbols: string[]; position: number }` and a pure helper providing a fixed-width centered window over it. Live next to the `Tape` class; `@turing-machine-js/visuals` re-exports both for consumer-import stability.
+- **First-class State tags** — `state.tag(...) / .untag(...) / .tags` for attaching string metadata. Out-of-band — no effect on transition lookup, `equivalentOn`, or runtime semantics. Live on the State instance, so memoized wrappers don't leak tags across sharers of a bare. `GraphNode.tags: string[]` survives `toGraph` / `fromGraph` round-trip; `toMermaid` emits tags inline (`
`) AND as `classDef` + `class` color-group lines.
+- **`withOverriddenHaltState` memoization** — calls with the same `(bare, override)` pair return the literally-same `State` instance. Backed by a `WeakMap` keyed by the bare with `WeakRef`-valued entries.
+- **`MachineState.matchedTransition`** — every yielded `MachineState` carries `{ id: string, matchKinds: ('wildcard' | 'literal')[] }`. `id` is `${stateId}.${transitionIx}` and resolves directly in `toGraph` output. `matchKinds` is per-tape; `'wildcard'` iff the winning alternative held `ifOtherSymbol` at that position. Eliminates `(source, nextState)` resolution ambiguity for exact-edge highlighting and per-transition coverage maps.
+- **`State.getMatchedTransition(symbol)`** — returns `{ nextState, matchedSymbol, ix }`, exposing the index used by `matchedTransition.id`.
+- **`State.collectStates(initialState, tapeBlock)`** — returns `Map` keyed by engine `GraphNode.id`. The K-th `transitionSymbols` slot is positionally aligned with the GraphTransition whose id is `${stateId}.${K}`. `StateMap` and `StateMapEntry` types exported.
+- **`TapeBlock.patternKinds(symbol, currentSymbols?)`** — per-tape `'wildcard' | 'literal'` for the matched alternative's selector.
+- **`HaltState` typed alias** — narrows `haltState.debug` to `boolean` at the canonical access path.
+- **`@turing-machine-js/visuals` companion package** — pure highlight + graph-indexing logic for the engine `Graph` (peer dep on `machine`, no runtime deps). Includes `applyHighlight` / `applyIndicator` / `indexGraph` / `bareIdOf` / `highlightExpand` / `equivalentIds` / `recordingOps`, the engine-edge-label formatter primitives (`formatStepNotation` / `tokenizeStep` / `formatTape`), the snippet-recording surface (`recordSnippet` + `SnippetPlayer`), and a 16-rule contract doc.
+
+### Changed
+
+- **`State.prototype.withOverrodeHaltState` renamed to `withOverriddenHaltState`** — grammar fix. Hard cutover, no deprecated alias. Renames in lockstep: public method, `state.overriddenHaltState` getter, the `#overriddenHaltState` private field, and the serialized `Graph` field `node.overriddenHaltStateId`.
+- **Wrapped-state composite name format flipped from `bare>override` to `bare(override)`** — paren-nested keeps structurally-distinct wrap-trees distinct (`A.with(B.with(A))` is `A(B(A))`; `A.with(B).with(A)` is `A(B)(A)`). As a consequence, **user-provided state names must not contain `(` or `)`** (the constructor throws). `>` is no longer reserved.
+- **`run()` is synchronous and callback-free** — signature is `run({ initialState, stepsLimit? }): void` (was `async … : Promise` with `onPause` / `onStep` / `onIter` callbacks). All callback machinery has moved to `DebugSession`. **Breaking** for callers awaiting `run()` or passing callbacks.
+- **`runStepByStep` is the minimal pure-iteration primitive** — advances the machine and yields a plain `MachineState`. Evaluates no `state.debug` filters and stamps no pause field. Breakpoint detection moved to `DebugSession`. **Breaking** for consumers that read a pause / breakpoint field off raw yields.
+- **`haltState.debug` is a `boolean`** — was `DebugConfig` with per-side `{ before?, after? }`. `haltState.debug = true` enables (pauses on every halt entry); `false` / `null` disables. Object-shaped writes throw. The pause fires on the AFTER side of the iter whose transition leads to halt — `m.state` is the triggering state. **Breaking** — pre-v7 consumers using `haltState.debug = { before: true }` / `{ after: true }` must switch to `haltState.debug = true`. ⚠️ Chained-form `haltState.debug.before = true` silently no-ops in non-strict mode (primitive-property assignment). Use the whole-object form.
+- **`toMermaid` callable-subtree emit** — wrapper and bare are separate graph nodes. Wrapper sits OUTSIDE any subgraph as `[[composite-name]]` (call site). Bare lives INSIDE its callable subtree subgraph as a regular `[name]` node. Subgraph label is `"callable subtree of NAME"` (single bare) or `"callable scope: A ∪ B"` (multi-bare union frame computed via union-find on bare-reachability). Halt marker is per-frame. Arrow vocabulary: solid `-->` for regular transitions AND wrapper's post-return `--> override`; bold `==> "call"` is RESERVED for wrapper-to-bare (`&` ribbon collapses multiple wrappers sharing a bare); dotted `-.->` is reserved for frame-level dispatch (`w_N -. "return" .-> wrapper`, `w_N -. "halt" .-> s0`, `idle -. enter .-> sN`). `GraphNode` field changes: `isWrapper: boolean`, `bareStateId: number | null`, `frameId: number | null`; `overriddenHaltStateId` lives on wrapper nodes only. Bytewise round-trip stability holds for all wrapped states, including shared-bare cases.
+- **`toMermaid` framed-wrapper emit fix** — `toGraph`'s reach-set now pushes the wrapper itself AND tunnels through `overriddenHaltStateId`, so framed-wrapper-continuations join the caller's frame instead of emitting at the top level.
+- **`toMermaid` user-content escape** — alphabet symbols, state names, tag names, and frame bare names are HTML-entity-escaped at the leaf (`&`, `"`, `<`, `>` to named entities; statement terminators, C0 controls, DEL, bidi controls, lone surrogates to numeric entities). `fromMermaid` mirrors with a single-pass entity decoder. Fixes parse errors on alphabets containing literal `"`, `<`, `>`, etc.
+- **Edge-label vocabulary rewritten** — `[reads] → [writes]/[moves]`, each role wrapped in `[…]` (tape-block indicator; always present, even single-tape). Read cells: `'X'` literal-quoted, `*` (ifOtherSymbol catch-all), `B` (tape's blank). Write cells: literal-quoted, `K` (keep), `E` (erase). Move cells: `L` / `R` / `S`. Alternation per-pattern bracket (`['^']|['1']`, never compact in-bracket form).
+- **`GraphTransition.id` separator: `-` → `.`** — was `${stateId}-${patternIx}`, now `${stateId}.${patternIx}`. Aligns with `matchedTransition.id` and makes the id splittable without colliding with user-defined state names containing `-`.
+- **Nested `.wohs()` chain collapse** — `A.wohs(t1).wohs(t2)` is equivalent to `A.wohs(t2)` (the inner override is dead at runtime). Composite name now reflects runtime behavior: `A(t2)`, not the misleading `A(t1)(t2)`.
+- **`runStepByStep` halt-stack is run-scoped, not machine-scoped** — fixes a "ghost iteration" / memory leak on consecutive `runStepByStep` calls when the previous generator wasn't drained to halt.
+- **Graph serialization extracted to `utilities/stateGraph.ts`** — `State.toGraph` / `State.fromGraph` / `State.collectStates` live in a sibling module. Public surface preserved via thin static delegates.
+- **`Tape.viewport` getter shares its centering loop with `tapeViewport`** via an internal `tapeViewportFromAccess` core — the centering math lives once.
+- **`summarize().stateCount` filters `isHaltMarker` nodes** — halt markers are visualization sentinels (all map back to singleton `haltState` at runtime).
+
+### Removed
+
+- **`MachineState.debugBreak`** — the per-yield `{ before?, after?, cause }` descriptor from the v4–v6 debugger. Replaced by the one-sided `m.pause: { side: 'before' | 'after', cause: 'breakpoint' | 'step' | 'manual' }` carried ONLY on a `DebugSession` `pause` event (`PausedMachineState`); raw `runStepByStep` yields carry no pause field.
+- **`withOverrodeHaltState` (the misspelled alias)** — no deprecated alias retained.
+- **The `-. onHalt .->` Mermaid arrow** — wrapper-to-override is now an ordinary solid `--> override` arrow; dotted `-.->` is reserved for frame-level dispatch.
+- **`GraphNode.isWrapped`** — replaced by `isWrapper: boolean`, `bareStateId: number | null`, `frameId: number | null`.
+- **`run()` callbacks (`onPause`, `onStep`, `onIter`)** — moved to `DebugSession` events.
+- **Hand-drawn pedagogical Mermaid blocks in READMEs** — the v7 `toMermaid` output is the primary illustration; no more vocabulary mismatch between hand-drawn and engine-rendered diagrams.
+
+### Migration
+
+Mechanical identifier rename:
+
+```sh
+git grep -l 'OverrodeHaltState\|overrodeHaltState' | xargs sed -i '' \
+ -e 's/OverrodeHaltState/OverriddenHaltState/g' \
+ -e 's/overrodeHaltState/overriddenHaltState/g'
+```
+
+`await`-on-`run()` and callback users move to `DebugSession`:
+
+```ts
+// before
+await machine.run({ initialState, onPause: (m) => { ... } });
+
+// after
+const session = new DebugSession(machine, { initialState });
+session.on('pause', (m) => { /* m.pause.side, m.pause.cause */ });
+await session.continue();
+```
+
+`m.debugBreak.before` / `.after` / `.cause` consumers move to `m.pause.side` / `m.pause.cause` on `DebugSession` `pause` events.
+
+`haltState.debug = { after: true }` writes throw — use `haltState.debug = true` (always after-side).
+
+Code that pattern-matches `state.name` for wrapper composites switches from `>`-split to paren-parse: `'A>B'` is now `'A(B)'`.
+
+If you render `toMermaid` output programmatically, the edge-label vocabulary changed completely — see the engine README's §Diagram conventions.
+
+If you store serialized `Graph` JSON: `node.overrodeHaltStateId` → `node.overriddenHaltStateId`, `GraphNode.isWrapped` → `isWrapper` + `bareStateId` + `frameId`, `GraphTransition.id` separator `-` → `.`.
+
+### Compatibility
+
+- Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.8` → `^7.0.0` on `@turing-machine-js/builder`, `@turing-machine-js/library-binary-numbers`, `@turing-machine-js/library-binary-numbers-bare`, `@turing-machine-js/visuals`.
+
## [7.0.0-alpha.8] - 2026-06-02
Eighth v7 pre-release. Lifts `TapeSnapshot` (the per-tape wire-data shape `{symbols, position}`) and `tapeViewport` (fixed-width centered window over a `TapeSnapshot`) from `@turing-machine-js/visuals` into the engine, next to the `Tape` class. Resolves [#227](https://github.com/mellonis/turing-machine-js/issues/227). Published under the `next` dist-tag: `npm install @turing-machine-js/machine@next`.
diff --git a/packages/machine/README.md b/packages/machine/README.md
index 68585a2..dd8ff76 100644
--- a/packages/machine/README.md
+++ b/packages/machine/README.md
@@ -852,7 +852,7 @@ API surface changes since v3, in past tense so the timing of each piece is expli
- **v6.2** *(superseded by v6.3.0)* — widened `onStep`'s signature to `(m) => void | Promise` and added an inline `await onStep(...)` in the run loop, enabling throttle-in-`onStep` patterns. This overturned the docstring-stated contract that `onStep` is sync (microtask-free); the right place for per-iter throttling is `onPause` with self-rearm (see [Throttle pattern](#throttle-pattern)). Restored in v6.3.0.
- **v6.3** — `onStep` reverted to its v6.0–v6.1 sync contract — `(m) => void`, called synchronously inside the run loop. The Throttle pattern section documents the engine-native shape for per-iter throttle / "wait between iters" UIs. No other API changes.
- **v6.4** — New **`onIter`** hook on `run()`: awaited, fires once at the end of every iter (after both `onPause` dispatches on the same yield), unaffected by the `debug` master switch. Use for per-iter throttle / animation / coordination needing a suspend point; complements the existing sync `onStep` (tracing) and conditional `onPause` (user breakpoints). Three-hook contract is now `onStep` (sync, mid-iter) / `onPause` (awaited, on `state.debug` match) / `onIter` (awaited, end-of-iter). Additive — peer-deps unchanged. The v6.3.0 README's `onPause`-rearm throttle workaround is superseded.
-- **v7** *(latest alpha: alpha.8, 2026-06-02)* — Composition-representation overhaul + first-class state tags + id-keyed `State.collectStates` lookup + `DebugSession` step controls + `CallFrame` + `tapeViewport`/`TapeSnapshot`. **Pre-release on the `next` dist-tag:** `npm install @turing-machine-js/machine@next` (or pin `@7.0.0-alpha.8`). **v7 milestone is feature-complete with #102**; the stable v7.0.0 cut is the only remaining step. Highlights across alphas:
+- **v7** *(2026-06-03)* — Composition-representation overhaul + first-class state tags + id-keyed `State.collectStates` lookup + `DebugSession` step controls + `CallFrame` + `tapeViewport`/`TapeSnapshot`. Stable release on the `latest` dist-tag: `npm install @turing-machine-js/machine`. Highlights across the v7 alpha cycle:
**alpha.8** — **`TapeSnapshot` type + `tapeViewport` helper** ([#227](https://github.com/mellonis/turing-machine-js/issues/227)) moved into the engine from `@turing-machine-js/visuals`, next to the live `Tape` class (visuals re-exports for consumer-import stability). `Tape.viewport` getter refactored to share its centering loop with `tapeViewport` via an internal core — the centering math now lives once; the two public surfaces supply their own data-shape-appropriate `cellAt` lambdas. See [§Tape](#tape).
diff --git a/packages/machine/package.json b/packages/machine/package.json
index d22c306..aa823f4 100644
--- a/packages/machine/package.json
+++ b/packages/machine/package.json
@@ -1,6 +1,6 @@
{
"name": "@turing-machine-js/machine",
- "version": "7.0.0-alpha.8",
+ "version": "7.0.0",
"description": "A convenient Turing machine",
"engines": {
"npm": ">=7.0.0"
diff --git a/packages/visuals/CHANGELOG.md b/packages/visuals/CHANGELOG.md
index 8eb8fd2..302b2ce 100644
--- a/packages/visuals/CHANGELOG.md
+++ b/packages/visuals/CHANGELOG.md
@@ -4,6 +4,21 @@ All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [7.0.0] - 2026-06-03
+
+Stable v7. Lockstep release with `@turing-machine-js/machine` 7.0.0. First stable cut of the visuals companion package. See the machine package CHANGELOG for the cumulative v7 trajectory.
+
+### Added (cumulative across the v7 alpha cycle)
+
+- **Highlight + graph-indexing surface** (alpha.6): `applyHighlight`, `applyIndicator`, `indexGraph`, `bareIdOf`, `highlightExpand`, `equivalentIds`, `recordingOps`, plus types `HighlightOps` / `IndicatorOps` / `RecordedOp` / `NodeKey` / `HighlightClass` / `GraphIndexes` / `GraphHighlight`. 16-rule contract doc at `docs/graph-highlight-and-breakpoints.md`.
+- **Snippet recording + playback** (alpha.6, alpha.7.1): `recordSnippet({ machine, initialState, graph, alphabets, name?, maxSteps?, log? }) => Snippet` (engine-agnostic — works with `@turing-machine-js/machine` or `@post-machine-js/machine`), `SnippetPlayer` class with `currentFrame` / `frameIndex` / `done` getters + `forward()` / `back()` / `reset()` / `goTo(idx)`. `Frame.commands` carries per-tape `{ movement, read, write }` so consumers step bi-directionally without recomputing deltas. Types: `Snippet`, `Frame`, `StepCommand`, `RecordSnippetOptions`.
+- **Engine-edge-label formatter primitives** (alpha.6.1): `formatStepNotation(reads, commands, blanks, matchKinds?)` (byte-identical to `toMermaid` edge labels, with `'X'` / `B` / `*='X'` / `K='X'` / `K=B` / `E` shortcuts), `tokenizeStep(...) → StepTokens` (renderer-agnostic structured tokens — discriminated-union `ReadToken` / `WriteToken` per cell, moves as letters; consumers wanting non-string rendering walk tokens themselves), `formatTape(snapshot)` (head bracketed in place, `a[b]c`). Earlier `formatCommand` / `formatStep` continue to work.
+- **`TapeSnapshot` + `tapeViewport` re-exports** (alpha.8) — moved into `@turing-machine-js/machine` and re-exported from visuals for consumer-import stability. `import { TapeSnapshot, tapeViewport } from '@turing-machine-js/visuals'` keeps working.
+
+### Changed
+
+- Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.8` → `^7.0.0`.
+
## [7.0.0-alpha.8] - 2026-06-02
`TapeSnapshot` type and `tapeViewport` helper moved to `@turing-machine-js/machine` ([#227](https://github.com/mellonis/turing-machine-js/issues/227)) — they live next to the live `Tape` class now. **Consumers importing them from `@turing-machine-js/visuals` are unaffected**: visuals re-exports both from the engine, so existing `import { TapeSnapshot, tapeViewport } from '@turing-machine-js/visuals'` continues to work.
diff --git a/packages/visuals/README.md b/packages/visuals/README.md
index b20e776..6b50ec9 100644
--- a/packages/visuals/README.md
+++ b/packages/visuals/README.md
@@ -14,13 +14,7 @@ Pure highlight + graph-indexing logic for [`@turing-machine-js/machine`](../mach
npm install @turing-machine-js/visuals @turing-machine-js/machine
```
-Both prereleases on npm `next`:
-
-```sh
-npm install @turing-machine-js/visuals@next @turing-machine-js/machine@next
-```
-
-Peer-deps on `@turing-machine-js/machine@^7.0.0-alpha.6` — visuals follows engine v7 alphas with occasional visuals-only patches (`7.0.0-alpha.6.1` added the formatter primitives + token surface on top of the lockstep alpha.6 engine release).
+Peer-deps on `@turing-machine-js/machine@^7.0.0`. First published as v7-only — released alongside the engine v7.0.0 stable cut. Visuals follows the engine in lockstep with occasional visuals-only patches (e.g. `7.0.0-alpha.6.1` and `7.0.0-alpha.7.1` during the v7 prerelease cycle).
## Public API
diff --git a/packages/visuals/package.json b/packages/visuals/package.json
index 728a4c0..baeef84 100644
--- a/packages/visuals/package.json
+++ b/packages/visuals/package.json
@@ -1,6 +1,6 @@
{
"name": "@turing-machine-js/visuals",
- "version": "7.0.0-alpha.8",
+ "version": "7.0.0",
"description": "Pure highlight + graph-indexing logic for @turing-machine-js/machine — no DOM, no renderer.",
"engines": {
"npm": ">=7.0.0"
@@ -25,7 +25,7 @@
"visualization"
],
"peerDependencies": {
- "@turing-machine-js/machine": "^7.0.0-alpha.8"
+ "@turing-machine-js/machine": "^7.0.0"
},
"scripts": {
"build": "tsc --build --verbose tsconfig.build.json && node ../../scripts/build-node-entries.mjs --package=@turing-machine-js/visuals",