@turing-machine-js/machine 7.0.0-alpha.2 → 7.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,96 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  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).
6
6
 
7
+ ## [7.0.0-alpha.4] - 2026-05-23
8
+
9
+ Fourth v7 pre-release. Adds an id-keyed lookup helper for the State graph ([#195](https://github.com/mellonis/turing-machine-js/issues/195)), fixes two upstream issues surfaced while wiring the new helper into downstream tooling — a `toMermaid` label-grammar bug ([#194](https://github.com/mellonis/turing-machine-js/issues/194)) and a halt-stack lifetime bug in `runStepByStep` ([#196](https://github.com/mellonis/turing-machine-js/issues/196)) — and extracts graph serialization into its own module without API change ([#180](https://github.com/mellonis/turing-machine-js/issues/180)). Published under the `next` dist-tag: `npm install @turing-machine-js/machine@next`.
10
+
11
+ **Pre-release — the API surface may still shift before stable v7.0.0.** Pin to a specific alpha for reproducibility: `@turing-machine-js/machine@7.0.0-alpha.4`.
12
+
13
+ ### Added
14
+
15
+ - **`State.collectStates(initialState, tapeBlock)`** ([#195](https://github.com/mellonis/turing-machine-js/issues/195)). Static helper that returns a `Map<number, {state: State; transitionSymbols: symbol[]}>` keyed by engine `GraphNode.id`. Lets downstream tooling (graph renderers, debugger panels) mutate `state.debug` on a specific State by numeric id, and set per-pattern breakpoints by `GraphTransition.id`. The K-th `transitionSymbols` slot is positionally aligned with the GraphTransition whose id is `${stateId}-${K}`, so a consumer holding `(stateId, patternIx)` from the rendered graph reaches the firing Symbol with no walk.
16
+
17
+ ```ts
18
+ const stateMap = State.collectStates(initial, tapeBlock);
19
+
20
+ // State-level breakpoint by id (any pattern fires).
21
+ stateMap.get(clickedStateId)!.state.debug.before = true;
22
+
23
+ // Per-pattern breakpoint by GraphTransition.id ("${stateId}-${patternIx}").
24
+ const [n, k] = clickedEdgeId.split('-').map(Number);
25
+ const entry = stateMap.get(n)!;
26
+ entry.state.debug.before = [entry.transitionSymbols[k]!];
27
+ ```
28
+
29
+ Coverage: regular / bare states get the full `[...#symbolToDataMap.keys()]` including `ifOtherSymbol` at its natural slot; wrappers and the halt singleton get empty `transitionSymbols`; synthetic halt markers (`isHaltMarker: true`, id `= -frameId`) are excluded — they all collapse to `haltState` at runtime, and the named consumer surfaces halt-pause via a separate UI control, not via clicks on halt glyphs. The halt singleton entry at id `0` is the process-wide `haltState` — toggling its `.debug` affects every machine in the runtime, same caveat as direct `haltState.debug` writes.
30
+
31
+ - **`StateMap` and `StateMapEntry` types** ([#195](https://github.com/mellonis/turing-machine-js/issues/195)). Exported from the package's public surface so TypeScript consumers can annotate `collectStates` results without re-deriving the shape.
32
+
33
+ ### Changed
34
+
35
+ - **Graph serialization extracted to `utilities/stateGraph.ts`** ([#180](https://github.com/mellonis/turing-machine-js/issues/180)). `State.toGraph` and `State.fromGraph` move out of the State class into a sibling module, alongside the new `collectStates`. Public surface preserved — `State.toGraph` / `State.fromGraph` remain as thin static delegates. State.ts shrank ~440 lines and now focuses on the runtime machinery (transitions, debug, halt-stack composition) rather than mixing in serialization concerns. A new `@internal` `STATE_INTERNAL` Symbol-keyed accessor on `State` gives sibling modules in `packages/machine/src` getter/setter access to private fields (`id`, `name`, `bareState`, `overriddenHaltState`, `symbolToDataMap`, `tags`); not re-exported from the public `index.ts`, so external consumers can't observe it.
36
+
37
+ ### Fixed
38
+
39
+ - **`toMermaid` edge labels containing literal `"` now parse correctly** ([#194](https://github.com/mellonis/turing-machine-js/issues/194)). Before: an alphabet that includes printable ASCII as literal symbols (e.g. a Brainfuck-flavored UTM whose data alphabet covers U+0020–U+007E) would emit an edge label like `s1 -- "['a'] → ['"']/[R]" --> s0`; Mermaid's parser terminated the string early on the inner `"`. After: user-supplied content (alphabet symbols, state names, tag names, frame bare names) is HTML-entity-escaped at the leaf — `&`, `"`, `<`, `>` to named entities; statement terminators (`\n`, `\r`), C0 controls minus `\t`, DEL, bidi controls, and lone UTF-16 surrogates to numeric entities. Printable Unicode (Cyrillic, CJK, accented Latin, etc.) passes through unchanged so non-ASCII alphabets stay readable in the emitted `.mmd`. `fromMermaid` mirrors with a single-pass entity decoder applied at the leaf, after structural parsing.
40
+
41
+ - **`runStepByStep` halt-stack is now run-scoped, not machine-scoped** ([#196](https://github.com/mellonis/turing-machine-js/issues/196)). Before: the `#stack` field on `TuringMachine` was an instance field; a build-time peek that didn't drain the generator (e.g. graph-construction utilities that ask for one yield to inspect the initial state) left leftover entries in the stack that were popped during the NEXT halt-bound transition, producing a "ghost iteration" and silently leaking memory across consecutive `runStepByStep` calls on the same machine. After: the halt stack is a local `const stack: State[] = []` declared inside `runStepByStep`, so each generator call starts with a clean stack and entries can't survive into the next call.
42
+
43
+ ### Compatibility
44
+
45
+ - Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.3` → `^7.0.0-alpha.4` on `@turing-machine-js/builder`, `@turing-machine-js/library-binary-numbers`, `@turing-machine-js/library-binary-numbers-bare`.
46
+
47
+ ### Migration from alpha.3
48
+
49
+ Purely additive — no breaking changes. Existing code that doesn't call `State.collectStates` continues to work identically. `State.toGraph` / `State.fromGraph` behave identically (the delegate-to-stateGraph wiring is internal); no consumer changes needed.
50
+
51
+ The `STATE_INTERNAL` accessor is `@internal` and not part of the supported surface; ignore it unless you're authoring a sibling module inside `packages/machine/src`.
52
+
53
+ ### Out of v7-alpha.4 (still pending for stable v7.0.0)
54
+
55
+ - **[#102](https://github.com/mellonis/turing-machine-js/issues/102)** — debugger step-in / step-out / step-over primitives.
56
+
57
+ ## [7.0.0-alpha.3] - 2026-05-21
58
+
59
+ Third v7 pre-release. Adds first-class out-of-band tags on `State` ([#186](https://github.com/mellonis/turing-machine-js/issues/186)) — a metadata channel for visualization grouping and debugger labels that survives `toGraph` / `fromGraph` / `toMermaid` / `fromMermaid` round-trips. Driven by downstream [post-machine-js#86](https://github.com/mellonis/post-machine-js/issues/86), which will build a path-based registry and inline pseudo-command on top once this ships. Published under the `next` dist-tag: `npm install @turing-machine-js/machine@next`.
60
+
61
+ **Pre-release — the API surface may still shift before stable v7.0.0.** Pin to a specific alpha for reproducibility: `@turing-machine-js/machine@7.0.0-alpha.3`.
62
+
63
+ ### Added
64
+
65
+ - **`State.tag(...) / .untag(...) / .tags` API** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). Fluent post-construct API for attaching string tags to a State. Tags are out-of-band metadata — they don't affect runtime transition lookup, `equivalentOn` comparisons, or any structural identity. Storage lives on the State INSTANCE (not on the shared `#symbolToDataMap`), so engine [#175](https://github.com/mellonis/turing-machine-js/issues/175) memoization doesn't leak tags across wrappers that share a bare: `A.wohs(t1).tag('hot')` does NOT propagate to `A.wohs(t2)`.
66
+
67
+ ```ts
68
+ const s = new State({...}, 'foo')
69
+ .tag('hot', 'sampled')
70
+ .untag('sampled');
71
+ s.tags; // ['hot'] ← frozen snapshot, in insertion order
72
+ ```
73
+
74
+ - **`GraphNode.tags: string[]`** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). New field on the serialized graph node. Survives `State.toGraph` / `State.fromGraph` round-trip; empty array for untagged states.
75
+
76
+ - **`toMermaid` tag rendering** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). Two surfaces:
77
+ - **Visible labels via `<br>`**: tagged node labels include the tag names inline (`s5["A<br>hot, sampled"]`). Uses Mermaid's universal `<br>` line break — works across mermaid.js, the live editor, machines-demo, and any other renderer; no CSS-pseudo-element tricks.
78
+ - **Color grouping via `classDef` + `class`**: each unique tag gets a `classDef tag_<sanitized> fill:#...,stroke:#...` line (palette of 6 colors selected by tag-name hash) and a `class s5,s6 tag_<sanitized>` line listing every node carrying the tag.
79
+
80
+ - **`fromMermaid` tag parse** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). Splits the node label on `<br>` to extract tags as the source of truth; `class` lines are decorative and discarded on parse (they regenerate on the next `toMermaid` emit from the tag set).
81
+
82
+ ### Compatibility
83
+
84
+ - Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.2` → `^7.0.0-alpha.3` on `@turing-machine-js/builder`, `@turing-machine-js/library-binary-numbers`, `@turing-machine-js/library-binary-numbers-bare`.
85
+
86
+ ### Migration from alpha.2
87
+
88
+ Purely additive — no breaking changes. Existing code that doesn't call `state.tag(...)` or read `state.tags` / `GraphNode.tags` continues to work identically. Mermaid emit for untagged states is bytewise unchanged.
89
+
90
+ If you serialize `GraphNode` JSON, note that the new `tags: string[]` field is now required by the type (always emitted; empty array if no tags).
91
+
92
+ ### Out of v7-alpha.3 (still pending for stable v7.0.0)
93
+
94
+ - **[#102](https://github.com/mellonis/turing-machine-js/issues/102)** — debugger step-in / step-out / step-over primitives.
95
+ - **[#180](https://github.com/mellonis/turing-machine-js/issues/180)** — extract `State.toGraph`/`fromGraph` to its own module.
96
+
7
97
  ## [7.0.0-alpha.2] - 2026-05-21
8
98
 
9
99
  Second v7 pre-release. Refines alpha.1's `toMermaid` wrapped-state emit into the function-call model ([#174](https://github.com/mellonis/turing-machine-js/issues/174)) and adds two construction-time improvements to `withOverriddenHaltState` ([#175](https://github.com/mellonis/turing-machine-js/issues/175), [#176](https://github.com/mellonis/turing-machine-js/issues/176)). Published to npm under the `next` dist-tag: `npm install @turing-machine-js/machine@next`.
package/README.md CHANGED
@@ -13,6 +13,7 @@ A composable Turing-machine engine for JavaScript: multi-tape, subroutine compos
13
13
  - [Building from a state table](#building-from-a-state-table)
14
14
  - [Classes](#classes) — [`Alphabet`](#alphabet) · [`Tape`](#tape) · [`TapeBlock`](#tapeblock) · [`TapeCommand`](#tapecommand) · [`Command`](#command) · [`State`](#state) · [`Reference`](#reference) · [`TuringMachine`](#turingmachine)
15
15
  - [Subroutine composition with `withOverriddenHaltState`](#subroutine-composition-with-withoverriddenhaltstate)
16
+ - [State tags](#state-tags)
16
17
  - [Debugging breakpoints](#debugging-breakpoints)
17
18
  - [Special objects](#special-objects) — [`haltState`](#haltstate) · [`ifOtherSymbol`](#ifothersymbol) · [`movements`](#movements) · [`symbolCommands`](#symbolcommands)
18
19
  - [Introspection and testing](#introspection-and-testing)
@@ -241,6 +242,7 @@ Notable members and statics:
241
242
  - **`state.withOverriddenHaltState(other)`** — returns a copy whose would-be halt transitions fall through to `other`. The subroutine-call composition mechanism (see `library-binary-numbers/src/index.ts` for examples).
242
243
  - **`State.toGraph(state, tapeBlock)`** — walks the reachable graph from `state` and returns a serializable `Graph` (states, transitions, alphabets).
243
244
  - **`State.fromGraph(graph)`** — inverse of `toGraph`: rebuilds `State` instances + a fresh `TapeBlock` from a `Graph`. Round-trips together with `toMermaid` / `fromMermaid`.
245
+ - **`State.collectStates(state, tapeBlock)`** — walks the same graph and returns a `Map<number, {state, transitionSymbols}>` keyed by `GraphNode.id`. Use when downstream tooling holds a numeric id (e.g. a clicked node in a rendered graph) and needs the live `State` instance or the per-pattern `Symbol` for breakpoint setup. See [Setting breakpoints by graph id](#setting-breakpoints-by-graph-id).
244
246
 
245
247
  For visualization, pair `State.toGraph` with `toMermaid` to render the graph in any Mermaid-aware viewer (GitHub, VS Code, mermaid.live):
246
248
 
@@ -427,6 +429,25 @@ flowchart TD
427
429
 
428
430
  Wrappers nest: `inner.withOverriddenHaltState(middle).withOverriddenHaltState(outer)` chains halt-redirects through `middle → outer → halt`. `library-binary-numbers/src/index.ts`'s `minusOne` (the `~(~x + 1)` composition) uses a 4-deep nest of wrappers.
429
431
 
432
+ ## State tags
433
+
434
+ A State carries an optional set of string tags — out-of-band metadata for visualization grouping and debugger labels. Tags don't affect runtime transition lookup, `equivalentOn` comparisons, or any structural identity; they ride alongside the State.
435
+
436
+ ```ts
437
+ const s = new State({...}, 'walkToBlank::1')
438
+ .tag('hot', 'subroutine-entry');
439
+
440
+ s.tags; // readonly ['hot', 'subroutine-entry'] — frozen snapshot
441
+ s.untag('hot');
442
+ s.tags; // readonly ['subroutine-entry']
443
+ ```
444
+
445
+ **Scoped to the wrapper instance.** Under [`withOverriddenHaltState` memoization (#175)](https://github.com/mellonis/turing-machine-js/issues/175), `A.wohs(t1)` and `A.wohs(t2)` are distinct wrapper instances even though they share `A`'s `#symbolToDataMap`. Tags live on the instance, so tagging one wrapper doesn't propagate to siblings sharing the same bare. Wrappers from `withOverriddenHaltState` start with an empty tag set (do not inherit from bare); the caller tags explicitly as needed.
446
+
447
+ **Round-trip preserved.** `state.toGraph` writes the tag set to `GraphNode.tags`; `state.fromGraph` reads it back and reapplies. `toMermaid` renders tags two ways: inline in the node label (`sN["name<br>tag1, tag2"]`, universal Mermaid line break) and as `classDef tag_<sanitized>` + `class sN tag_<sanitized>` lines for color grouping. `fromMermaid` splits the label on `<br>` as source of truth; the `class` lines are decorative and discarded on parse.
448
+
449
+ See [§Diagram conventions § Tags](#tags) for the full emit shape.
450
+
430
451
  ## Debugging breakpoints
431
452
 
432
453
  Any `State` can carry a runtime-mutable `debug` config that pauses execution at chosen points.
@@ -479,6 +500,36 @@ If `onPause` is not provided, breaks fire-and-resume invisibly — the trajector
479
500
 
480
501
  **Caveat:** `haltState` is a module-level singleton. Setting `haltState.debug` affects every machine in the process; clear in `afterEach` / `finally` for test isolation.
481
502
 
503
+ ### Setting breakpoints by graph id
504
+
505
+ Downstream UIs (graph renderers, debugger panels) often have only a numeric `GraphNode.id` — the user clicked a state node, or a transition edge in a rendered SVG. `State.collectStates(initial, tapeBlock)` returns a `Map` keyed by that numeric id, with the live `State` instance and the per-pattern `Symbol` array as its value:
506
+
507
+ ```ts
508
+ import { State, ifOtherSymbol } from '@turing-machine-js/machine';
509
+
510
+ const stateMap = State.collectStates(initial, tapeBlock);
511
+
512
+ // Toggle a state-level breakpoint by id (any pattern triggers).
513
+ const entry = stateMap.get(clickedStateId);
514
+ if (entry) {
515
+ entry.state.debug.before = true;
516
+ }
517
+
518
+ // Per-pattern breakpoint by GraphTransition.id — the contract is
519
+ // positional: `transitionSymbols[K]` is the Symbol that the
520
+ // `${stateId}-${K}` GraphTransition fires on.
521
+ const [n, k] = clickedEdgeId.split('-').map(Number);
522
+ const e = stateMap.get(n);
523
+ const sym = e?.transitionSymbols[k];
524
+ if (e && sym) {
525
+ e.state.debug.before = [sym];
526
+ }
527
+ ```
528
+
529
+ **Coverage rules:** regular / bare states get the full `[...#symbolToDataMap.keys()]` including `ifOtherSymbol` at its natural slot; wrappers and the halt singleton get empty `transitionSymbols`; synthetic halt markers (Graph nodes with `id = -frameId`, one per callable-subtree frame) are excluded from the map. See `State.collectStates` JSDoc for the full contract.
530
+
531
+ > ⚠️ `stateMap.get(0)!.state === haltState` — the entry at id `0` is the process-wide halt singleton. Toggling its `debug` affects every machine in the runtime, same caveat as direct `haltState.debug` writes.
532
+
482
533
  ### Throttle pattern
483
534
 
484
535
  For per-iter throttle / animation / "wait between steps" UIs, use the **`onIter`** hook — an awaited callback that fires once at the end of every iter, after both `onPause` dispatches on the same yield. It's the engine-native shape for per-iter coordination:
@@ -599,6 +650,15 @@ The `&` ribbon syntax (`s_W1 & s_W2 == "call" ==> s_A`) collapses multiple wrapp
599
650
 
600
651
  `subgraph w_N["callable subtree of NAME"] … end` wraps a bare + its body + a halt marker — the callable scope of code that runs when a wrapper "calls" the bare. Multi-bare frames (union-find merged from shared body states) use the label `"callable scope: A ∪ B"`.
601
652
 
653
+ ### Tags
654
+
655
+ Tagged states (via `state.tag('hot', 'sampled')` — see [§State tags](#state-tags)) render two ways simultaneously:
656
+
657
+ - **Inline in the node label**: `sN["name<br>tag1, tag2"]` — the `<br>` is Mermaid's universal line break, so the tags display as a second line under the state name in any renderer.
658
+ - **As a color class**: `classDef tag_<sanitized> fill:#...,stroke:#...` per unique tag (6-color palette selected by tag-name hash), plus `class sN,sM tag_<sanitized>` listing all nodes carrying the tag. Lets the eye group related states by color even when their names are scattered across the diagram.
659
+
660
+ The `<br>`-embedded label is the source of truth for `fromMermaid` round-trip; the `classDef`/`class` lines are decorative and regenerate on the next `toMermaid` emit. Tag-name sanitization in `classDef` identifiers: any char outside `[A-Za-z0-9_-]` is replaced with `_`. Labels preserve the raw tag names.
661
+
602
662
  ### Edge label format
603
663
 
604
664
  `[reads] → [writes]/[moves]`. Each bracketed list is a tape-block reading — one entry per tape; brackets always present, even single-tape.
@@ -654,7 +714,15 @@ API surface changes since v3, in past tense so the timing of each piece is expli
654
714
  - **v6.2** *(superseded by v6.3.0)* — widened `onStep`'s signature to `(m) => void | Promise<void>` 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.
655
715
  - **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.
656
716
  - **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.
657
- - **v7** *(alpha 1, 2026-05-21)* — Composition-representation overhaul. **First pre-release on the `next` dist-tag:** `npm install @turing-machine-js/machine@next` (or pin `@7.0.0-alpha.1`). Stable v7.0.0 still pending [#102](https://github.com/mellonis/turing-machine-js/issues/102) (debugger step-in/over/out primitives). Landed in alpha.1:
717
+ - **v7** *(latest alpha: alpha.4, 2026-05-23)* — Composition-representation overhaul + first-class state tags + id-keyed `State.collectStates` lookup. **Pre-release on the `next` dist-tag:** `npm install @turing-machine-js/machine@next` (or pin `@7.0.0-alpha.4`). Stable v7.0.0 still pending [#102](https://github.com/mellonis/turing-machine-js/issues/102) (debugger step-in/over/out primitives). Highlights across alphas:
718
+
719
+ **alpha.4** — **`State.collectStates(initial, tapeBlock)`** ([#195](https://github.com/mellonis/turing-machine-js/issues/195)) returns a `Map<number, {state, transitionSymbols}>` keyed by `GraphNode.id` so downstream tooling can mutate `state.debug` by numeric id and set per-pattern breakpoints by `GraphTransition.id`. Graph serialization extracted to `utilities/stateGraph.ts` with a Symbol-keyed `@internal` accessor on `State` ([#180](https://github.com/mellonis/turing-machine-js/issues/180); no public-API change — the `State.toGraph` / `.fromGraph` statics remain as thin delegates). Two upstream fixes: `toMermaid` HTML-entity-escapes user content in labels so alphabets containing `"`, `<`, etc. parse correctly ([#194](https://github.com/mellonis/turing-machine-js/issues/194)); `runStepByStep`'s halt stack is now run-scoped, fixing a memory leak / ghost-iteration when the same `TuringMachine` instance is reused across calls ([#196](https://github.com/mellonis/turing-machine-js/issues/196)). See [§Setting breakpoints by graph id](#setting-breakpoints-by-graph-id).
720
+
721
+ **alpha.3** — first-class **State tags** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). `state.tag(...) / .untag(...) / .tags` API; `GraphNode.tags: string[]` round-trips through `toGraph`/`fromGraph`; `toMermaid` emits tags two ways simultaneously — inline via `<br>` in node labels (`sN["name<br>tag1, tag2"]`) and as `classDef`/`class` for color grouping. Tags live on the State instance (not on the shared `#symbolToDataMap`), so engine [#175](https://github.com/mellonis/turing-machine-js/issues/175) memoization doesn't leak tags across wrappers sharing a bare. See [§State tags](#state-tags).
722
+
723
+ **alpha.2** — callable-subtree `toMermaid` emit refinement ([#174](https://github.com/mellonis/turing-machine-js/issues/174)). The wrapper is a separate `[[composite-name]]` node OUTSIDE the subgraph; the bare's reachable subtree becomes a `subgraph w_${frameId}["callable subtree of NAME"]` block. Frames computed via union-find — shared bares dedupe with `&` ribbons on call arrows. Bold `==> "call"` reserved for wrapper-to-bare; dotted `-.->` for frame dispatch (`return` / `halt` / `enter`). Plus engine memoization ([#175](https://github.com/mellonis/turing-machine-js/issues/175)) and nested-chain collapse ([#176](https://github.com/mellonis/turing-machine-js/issues/176)) for `.wohs()`.
724
+
725
+ **alpha.1** — initial v7 composition-representation overhaul:
658
726
  - **`withOverrodeHaltState` → `withOverriddenHaltState`** ([#149](https://github.com/mellonis/turing-machine-js/issues/149)). Grammar fix on a name introduced in 2019: the past-participle `overridden` fits the "with a halt-state that has been ___" naming idiom; `overrode` (simple past) didn't. Hard cutover — no deprecated alias. The getter (`state.overrodeHaltState` → `state.overriddenHaltState`) and the serialized `Graph` data field (`node.overrodeHaltStateId` → `node.overriddenHaltStateId`) rename in lockstep. Consumer migration: global find/replace `OverrodeHaltState` → `OverriddenHaltState` and `overrodeHaltState` → `overriddenHaltState`. Persisted `State.toGraph` JSON dumps would need the same field-rename treatment, but persistence isn't a known consumer pattern.
659
727
  - **Paren-based wrapped-state naming** ([#148](https://github.com/mellonis/turing-machine-js/issues/148)). `withOverriddenHaltState`'s composite name format changed from flat `bare>override` to nested `bare(override)`. Same nesting depth reads as `A(B(A))` (bare = `A`, override = `B(A)`) versus `A(B)(A)` (bare = `A(B)`, override = `A`) — two structurally-different wrap-trees that the old `>`-flat notation collided into the single string `A>B>A`. As a consequence, **user-provided state names must not contain `(` or `)`** — `State` now throws at construction time if a user passes such a name. The `>` character stays valid in user names (no longer reserved). The `inspect()` / `toGraph` / `toMermaid` outputs carry the new format. `states.md` files in `library-binary-numbers` regenerate accordingly.
660
728
  - **`toMermaid` callable-subtree emit** ([#174](https://github.com/mellonis/turing-machine-js/issues/174), supersedes the alpha.1 collapsed-bare shape from #138/#139). `withOverriddenHaltState` is modeled as a function call: the wrapper is a `[[composite-name]]` call site OUTSIDE any subgraph, the bare's reachable subtree becomes a `subgraph w_${frameId}["callable subtree of NAME"] … end` block containing the bare + body states + a per-frame halt marker `c${frameId}(((halt)))`. Frames are computed via union-find on bare-reachability — overlapping reach sets merge into a single union frame, so shared bares (`library-binary-numbers/minusOne`'s `invertNumber`) appear ONCE with `& `-joined call arrows from each wrapper. Bold `==> "call"` arrows are reserved for the wrapper-to-bare call; dotted `-.->` is reserved for frame-level dispatch (`return` / `halt` / `enter`). The retired `-. onHalt .->` keyword is replaced by a solid `--> override` arrow (just an ordinary transition under the call/return mental model). `GraphNode` gains `isWrapper`, `bareStateId`, `frameId` fields (and drops `isWrapped`). Bytewise round-trip stability now holds for all wrapped states including shared-bare cases (no per-context duplication).
@@ -3,8 +3,31 @@ import Reference from './Reference';
3
3
  import TapeBlock from './TapeBlock';
4
4
  import TapeCommand from './TapeCommand';
5
5
  import { type Graph } from '../utilities/graph';
6
+ import { type StateMap } from '../utilities/stateGraph';
6
7
  export declare const ifOtherSymbol: unique symbol;
7
8
  declare const validateDebugFilter: unique symbol;
9
+ /**
10
+ * @internal
11
+ *
12
+ * Package-private accessor key for sibling modules in
13
+ * `packages/machine/src` (e.g. `utilities/stateGraph.ts`, and the planned
14
+ * `utilities/stateCollect.ts` for #195). Re-exported from this module so
15
+ * sibling files can import it; intentionally NOT re-exported from the
16
+ * package's public `index.ts`, so downstream consumers don't see it on
17
+ * the supported surface.
18
+ *
19
+ * Calling `state[STATE_INTERNAL]()` returns a getter/setter view onto the
20
+ * State's private fields. Reads are live (they close over `this`), so the
21
+ * view stays in sync with subsequent mutations on the State. There's one
22
+ * mutating setter on the view — `name` — used exclusively by
23
+ * `fromGraph` to assign graph-sourced composite names (e.g. `A(target)`)
24
+ * that the public name validator would reject; see the JSDoc on the
25
+ * accessor itself.
26
+ *
27
+ * Designed in #180 with #195 in mind so its surface doesn't need to grow
28
+ * when `collectStates` lands.
29
+ */
30
+ export declare const STATE_INTERNAL: unique symbol;
8
31
  export declare class DebugConfig {
9
32
  #private;
10
33
  constructor(ownerState: State, initial?: {
@@ -32,12 +55,65 @@ export default class State {
32
55
  before?: symbol[] | readonly symbol[] | true;
33
56
  after?: symbol[] | readonly symbol[] | true;
34
57
  } | null);
58
+ /**
59
+ * Add one or more tags to this State (#186). Tags are out-of-band metadata
60
+ * used by visualization (`toMermaid` emits `classDef`/`class` lines) and
61
+ * debugger tooling — they don't affect runtime transition lookup,
62
+ * `equivalentOn` comparisons, or any structural identity. Chainable.
63
+ */
64
+ tag(...tags: string[]): this;
65
+ /**
66
+ * Remove one or more tags from this State (#186). Untagging a tag the
67
+ * State doesn't carry is a no-op. Chainable.
68
+ */
69
+ untag(...tags: string[]): this;
70
+ /**
71
+ * Frozen snapshot of this State's current tags (#186). The returned array
72
+ * is `Object.freeze`d — mutating it throws in strict mode (which TS-emitted
73
+ * code uses). Order matches insertion order of the underlying Set.
74
+ */
75
+ get tags(): readonly string[];
35
76
  /** @internal — invoked by DebugConfig setters via module-private symbol. */
36
77
  [validateDebugFilter](fieldName: 'before' | 'after', filter: readonly symbol[] | true | undefined): void;
37
78
  getSymbol(tapeBlock: TapeBlock): symbol;
38
79
  getCommand(symbol: symbol): Command;
39
80
  getNextState(symbol: symbol): State | Reference;
40
81
  withOverriddenHaltState(overriddenHaltState: State): State;
82
+ /**
83
+ * @internal
84
+ *
85
+ * Package-private getter/setter view onto this State's private fields,
86
+ * for sibling modules in `packages/machine/src` (currently `stateGraph.ts`
87
+ * for `toGraph` / `fromGraph`, and the planned `stateCollect.ts` for
88
+ * #195's `collectStates`).
89
+ *
90
+ * Read access is live — the getters close over `this`, so the view
91
+ * stays in sync with subsequent mutations on this State. There's a
92
+ * single mutating setter on the view, `name`, which exists to let
93
+ * `fromGraph` assign graph-sourced composite names (e.g. `A(target)`)
94
+ * to freshly-constructed bare States. The constructor's name validator
95
+ * rejects parens (reserved as wrapper-composition delimiters in
96
+ * `withOverriddenHaltState`); the setter intentionally bypasses that
97
+ * check because the same delimiters appear in legitimate wrapper-bare
98
+ * names round-tripped through the graph.
99
+ *
100
+ * Returns a fresh view object on every call — cheap enough for the
101
+ * BFS-once-per-build callers, and avoids holding a reference object on
102
+ * every State instance. Keep this surface tight: callers should only
103
+ * read what they need. Adding fields here is a deliberate decision —
104
+ * each adds to the implicit contract sibling modules can rely on.
105
+ */
106
+ [STATE_INTERNAL](): {
107
+ readonly id: number;
108
+ name: string;
109
+ readonly bareState: State | null;
110
+ readonly overriddenHaltState: State | null;
111
+ readonly symbolToDataMap: Map<symbol, {
112
+ command: Command;
113
+ nextState: State | Reference;
114
+ }>;
115
+ readonly tags: ReadonlySet<string>;
116
+ };
41
117
  static inspect(state: State): {
42
118
  id: number;
43
119
  name: string;
@@ -58,12 +134,35 @@ export default class State {
58
134
  } | null;
59
135
  }>;
60
136
  };
137
+ /**
138
+ * Walks the reachable State graph from `initialState` and returns a
139
+ * serializable `Graph`. Thin delegate to `utilities/stateGraph.ts`'s
140
+ * `toGraph` (extracted in #180); see that module for the BFS shape and
141
+ * v7 callable-subtree emit semantics.
142
+ */
61
143
  static toGraph(initialState: State, tapeBlock: TapeBlock): Graph;
144
+ /**
145
+ * Inverse of `toGraph`: rebuilds a State graph and a fresh TapeBlock
146
+ * from a serialized `Graph`. Thin delegate to `utilities/stateGraph.ts`'s
147
+ * `fromGraph` (extracted in #180); see that module for the
148
+ * reconstruction pass shape (Reference pre-create, bare build, wrapper
149
+ * resolution via `withOverriddenHaltState`, ref binding).
150
+ */
62
151
  static fromGraph(graph: Graph): {
63
152
  start: State;
64
153
  tapeBlock: TapeBlock;
65
154
  states: Record<number, State>;
66
155
  };
156
+ /**
157
+ * Returns a `Map<number, {state, transitionSymbols}>` keyed by engine
158
+ * `GraphNode.id`, exposing the live `State` instance + per-pattern
159
+ * Symbol references for each node so downstream tooling can mutate
160
+ * `state.debug` by numeric id and set per-pattern breakpoints by
161
+ * `GraphTransition.id` (#195). Thin delegate to
162
+ * `utilities/stateGraph.ts`'s `collectStates`; see that module for
163
+ * the alignment contract, coverage rules, and halt-singleton warning.
164
+ */
165
+ static collectStates(initialState: State, tapeBlock: TapeBlock): StateMap;
67
166
  }
68
167
  export declare const haltState: State;
69
168
  export {};