diff --git a/CMakeLists.txt b/CMakeLists.txt
index ff165ef..997b720 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,6 +25,7 @@ add_library(attolang STATIC
src/atto/graph_index.cpp
src/atto/shadow.cpp
src/atto/symbol_table.cpp
+ src/atto/graph_builder.cpp
)
target_include_directories(attolang PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
@@ -56,13 +57,35 @@ if(ATTOLANG_BUILD_EDITOR)
set(ATTO_NEEDS_IMGUI ON)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/AttoDeps.cmake)
+ # Embed Liberation Mono font as C array
+ set(FONT_TTF "${CMAKE_CURRENT_SOURCE_DIR}/src/attoflow/fonts/LiberationMono-Regular.ttf")
+ set(FONT_HDR "${CMAKE_CURRENT_BINARY_DIR}/generated/LiberationMono_Regular.h")
+ file(READ "${FONT_TTF}" FONT_HEX HEX)
+ string(LENGTH "${FONT_HEX}" FONT_HEX_LEN)
+ math(EXPR FONT_SIZE "${FONT_HEX_LEN} / 2")
+ string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," FONT_BYTES "${FONT_HEX}")
+ file(WRITE "${FONT_HDR}"
+ "// Liberation Mono Regular - SIL Open Font License (auto-generated)\n"
+ "#pragma once\n"
+ "static const unsigned int LiberationMono_Regular_size = ${FONT_SIZE};\n"
+ "static const unsigned char LiberationMono_Regular_data[] = {\n"
+ "${FONT_BYTES}\n};\n"
+ )
+
add_executable(attoflow
src/attoflow/main.cpp
- src/attoflow/editor.cpp
+ src/attoflow/window.cpp
+ src/attoflow/editor2.cpp
+ src/attoflow/nets_editor.cpp
+ src/attoflow/visual_editor.cpp
+ src/attoflow/node_renderer.cpp
+ src/attoflow/tooltip_renderer.cpp
+ src/attoflow/editor_style.cpp
)
target_include_directories(attoflow PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/attoflow
+ ${CMAKE_CURRENT_BINARY_DIR}/generated
)
if(WIN32)
target_link_libraries(attoflow PRIVATE attolang SDL3::SDL3 imgui::imgui)
diff --git a/README.md b/README.md
index f985432..a6885b7 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,7 @@ Each instrument is a self-contained `.atto` program that defines its Functionali
+Listen to the [introductory podcast](docs/podcasts/introducing-orgasm.md) — *Growing Instruments with the Organic Assembler* ([SoundCloud](https://soundcloud.com/poiitidis/growing-instruments-with-the), [YouTube](https://youtu.be/ymzuD-oekFM)).
See an [example instrument](scenes/klavier/main.atto) and the full [language specification](docs/attolang.md).
@@ -53,6 +54,12 @@ cmake -B build -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake
cmake --build build --parallel --config Release
```
+## Instructions
+
+New to the project? Start with the [Instructions](docs/instructions.md) — a guide to interpreting and following instructions, operating on the codebase, and building instruments with the Organic Assembler. It covers everything from the build system and architectural layers to the anatomy of an instrument and the audio callback pattern.
+
+For naming philosophy, see [Names](docs/names.md). For the full documentation suite: [Architecture](docs/architecture.md), [Language Spec](docs/attolang.md), [Patterns](docs/patterns.md), [Thinking](docs/thinking.md), [Style](docs/style.md), [Coding](docs/coding.md), [Changelog](docs/changelog.md).
+
## License
MIT — see [LICENSE](LICENSE).
diff --git a/docs/architecture.md b/docs/architecture.md
new file mode 100644
index 0000000..e02c0d9
--- /dev/null
+++ b/docs/architecture.md
@@ -0,0 +1,400 @@
+# Organic Assembler — Architecture
+
+## Overview
+
+Organic Assembler (orgasm) is an **Operating System for Instruments**. An "instrument" is a
+multimodal dataflow program authored as a node graph in a `.atto` file using the attolang
+language. The system comprises four major subsystems: the core language library, the compiler,
+the visual editor, and the runtime. Each is independently buildable and has well-defined
+boundaries.
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ User / Developer │
+│ │
+│ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │
+│ │ attoflow │ │ attoc │ │ attohost │ │
+│ │ (editor) │───>│ (compiler) │───>│ (runtime) │ │
+│ └──────┬──────┘ └──────┬──────┘ └──────┬─────┘ │
+│ │ │ │ │
+│ └──────────────────┼──────────────────┘ │
+│ │ │
+│ ┌────────┴────────┐ │
+│ │ attolang │ │
+│ │ (core library) │ │
+│ └─────────────────┘ │
+└─────────────────────────────────────────────────────────┘
+```
+
+## Build Targets
+
+The project uses CMake 3.25+ with C++20. There are four build targets:
+
+| Target | Type | Description | Dependencies |
+|-------------------|------------|------------------------------------------|----------------------|
+| `attolang` | Static lib | Core language: types, parsing, inference | C++20 stdlib only |
+| `attoc` | Executable | Standalone compiler (.atto → C++) | attolang |
+| `test_inference` | Executable | Unit tests for type inference | attolang |
+| `attoflow` | Executable | Visual node editor (optional) | attolang, SDL3, ImGui|
+
+The editor (`attoflow`) is optional and gated behind `ATTOLANG_BUILD_EDITOR`. The core
+library and compiler have zero external dependencies beyond the C++20 standard library.
+
+## Directory Layout
+
+```
+orgasm/
+├── CMakeLists.txt # Top-level build configuration
+├── cmake/AttoDeps.cmake # SDL3 + ImGui fetch (vcpkg on Windows, FetchContent otherwise)
+├── vcpkg.json # Windows dependency manifest
+│
+├── src/
+│ ├── atto/ # Core language library (attolang)
+│ │ ├── model.h # FlowGraph, FlowNode, FlowLink, FlowPin
+│ │ ├── types.h / types.cpp # Type system: TypeExpr, TypeParser, TypePool
+│ │ ├── expr.h / expr.cpp # Expression AST: tokenizer, parser, ExprNode
+│ │ ├── inference.h / .cpp # Bidirectional type inference engine
+│ │ ├── graph_builder.h / .cpp # Structured graph editing with dirty tracking
+│ │ ├── graph_index.h / .cpp # Fast pin/node lookup by ID
+│ │ ├── serial.h / serial.cpp # .atto file serialization (TOML-like)
+│ │ ├── shadow.h / shadow.cpp # Shadow expression nodes (deref, internal)
+│ │ ├── symbol_table.h / .cpp # Compile-time symbol resolution
+│ │ ├── type_utils.h / .cpp # Type compatibility, upcasting rules
+│ │ ├── args.h / args.cpp # Argument tokenization utilities
+│ │ ├── node_types.h / .cpp # Node type descriptors (v1)
+│ │ ├── node_types2.h # Extended node type descriptors (v2)
+│ │ └── graph_editor_interfaces.h # Observer interfaces for editor integration
+│ │
+│ ├── attoc/ # Standalone compiler
+│ │ ├── main.cpp # Entry point: load → infer → codegen
+│ │ ├── codegen.h / .cpp # C++ code generation (types, header, impl, cmake)
+│ │
+│ ├── attoflow/ # Visual editor
+│ │ ├── main.cpp # SDL3 window init, 60 FPS event loop
+│ │ ├── window.h / .cpp # Tab management, project browser, build toolbar
+│ │ ├── editor2.h / .cpp # Editor2Pane: wraps GraphBuilder, wire connection
+│ │ ├── visual_editor.h / .cpp # Canvas: pan/zoom, grid, hover detection
+│ │ ├── node_renderer.h / .cpp # Node visualization: pins, args, errors
+│ │ ├── nets_editor.h / .cpp # Named nets panel
+│ │ ├── editor_style.h / .cpp # Colors, fonts, spacing constants
+│ │ ├── tooltip_renderer.h/.cpp# Hover tooltip rendering
+│ │ ├── sdl_imgui_window.h # SDL3 + ImGui integration layer
+│ │ └── fonts/ # Liberation Mono (embedded via CMake hex read)
+│ │
+│ ├── attoruntime/ # Instrument runtime
+│ │ ├── attoruntime.h # Runtime API declarations
+│ │ ├── atto_gui.cpp # GUI bindings (imgui calls)
+│ │ ├── nano_imgui.cpp # High-level ImGui wrappers
+│ │ └── main.cpp # Stub entry point
+│ │
+│ └── legacy/ # Deprecated editor1 code
+│
+├── attostd/ # Standard library (.atto modules)
+│ ├── gui.atto # AV/audio FFI: av_create_window, audio_tick
+│ └── imgui.atto # ImGui FFI: buttons, sliders, text, windows
+│
+├── scenes/ # Example instruments
+│ ├── klavier/main.atto # Piano/synthesizer (29KB, 100+ nodes)
+│ └── multifader/main.atto # Fader mixing instrument (25KB)
+│
+├── tests/
+│ └── test_inference.cpp # Type inference unit tests
+│
+└── docs/
+ └── attolang.md # Full language specification (44KB)
+```
+
+## Subsystem Deep Dives
+
+### 1. attolang — Core Language Library
+
+The core library is a static C++ library with **zero external dependencies**. It defines
+the data model, type system, expression parser, inference engine, serialization, and
+graph builder. Everything in `src/atto/` belongs here.
+
+#### Data Model (`model.h`)
+
+The fundamental representation is a **FlowGraph**: a directed graph of nodes connected
+by typed links.
+
+```
+FlowGraph
+├── nodes: vector
+│ ├── guid: string (stable identifier)
+│ ├── type: NodeTypeID (one of 38 built-in types)
+│ ├── args: string (inline arguments, legacy)
+│ ├── position: vec2 (editor layout)
+│ ├── inputs: vector (pin IDs for incoming connections)
+│ └── outputs: vector (pin IDs for outgoing connections)
+│
+└── links: vector
+ ├── from: string (output pin ID)
+ ├── to: string (input pin ID)
+ └── net_name: string (optional named net)
+```
+
+Nodes are passive data containers. All semantic meaning comes from the node's `type`
+field, which indexes into the node type registry.
+
+#### Type System (`types.h`, `type_utils.h`)
+
+The type system is the backbone of attolang. Every value has a **TypeExpr** with two
+orthogonal dimensions:
+
+1. **TypeKind** — what the value is (Void, Bool, Scalar, Container, Function, Symbol, etc.)
+2. **TypeCategory** — how the value is accessed (Data, Reference, Iterator, Lambda, etc.)
+
+Categories are denoted by sigils in the wire format:
+
+| Sigil | Category | Meaning |
+|-------|------------|-----------------------------------|
+| `%` | Data | Plain value (default) |
+| `&` | Reference | Reference to a mutable location |
+| `^` | Iterator | Iterator into a container |
+| `@` | Lambda | Callable function reference |
+| `#` | Enum | Enumeration value |
+| `!` | Bang | Trigger signal (carries no data) |
+| `~` | Event | Event source |
+
+Type expressions are parsed by `TypeParser` (recursive descent) and interned in
+`TypePool` for deduplication. The pool caches every parsed type string → `TypePtr`
+mapping.
+
+Key type features:
+- **Literal types**: `literal` — compile-time constants with type domain T and value V
+- **Symbol types**: `symbol` — first-class named references with decay
+- **Meta types**: `type` — types as compile-time values
+- **Generic types**: `unsigned>`, `float` — resolved via backpropagation
+- **Automatic upcasting**: u8 → u16 → u32 → u64, s8 → s32 → s64, integer → float
+
+#### Expression Parser (`expr.h`, `expr.cpp`)
+
+Expressions are inline code within nodes (e.g., `$0 + $1 * sin($2)`). The parser is a
+recursive-descent parser producing an `ExprNode` AST.
+
+Expression kinds include:
+- `Literal` — all constants (integers, floats, booleans, strings)
+- `PinRef` — `$N` references to input pins
+- `BinaryOp` — arithmetic, comparison, logical operators
+- `UnaryOp` — negation, logical not
+- `FunctionCall` — `name(args...)` including type constructors
+- `FieldAccess` — `expr.field`
+- `IndexOp` — `expr[index]`
+- `TypeApply` — `$0<$1,$2>` speculative generic application
+
+The tokenizer handles TOML string escaping, nested parentheses, and the distinction
+between struct types (`{x:f32 y:f32}`) and struct literals (`{x:1.0f, y:2.0f}`) via
+comma detection.
+
+#### Inference Engine (`inference.h`, `inference.cpp`)
+
+Type inference is **bidirectional** and **multi-pass**:
+
+```
+Phase 1: clear_all() — Reset all pin types
+Phase 2: resolve_pin_type_names() — Parse type annotations via TypePool
+Phase 3: propagate_connections() — Forward: flow types through wires
+Phase 4: infer_expr_nodes() — Recursive inference on expression ASTs
+Phase 5: propagate_pin_ref_types()— Backward: backprop from consumers
+Phase 6: resolve_lambdas() — Validate lambda capture and parameters
+Phase 7: fixup_expr_derefs() — Insert Deref nodes for iterators
+Phase 8: insert_deref_nodes() — Materialize shadow deref nodes
+```
+
+The engine runs to a fixed point for phases 3-5, iterating until no types change.
+This handles mutual dependencies between nodes (A depends on B depends on A).
+
+#### Graph Builder (`graph_builder.h`, `graph_builder.cpp`)
+
+The GraphBuilder is the **high-level editing API** layered over the raw FlowGraph model.
+It provides:
+
+- **Structured entries**: `FlowNodeBuilder` and `NetBuilder` with `BuilderEntry` base
+- **Pin model**: `FlowArg2` hierarchy (ArgNet2, ArgNumber2, ArgString2, ArgExpr2)
+- **Dirty tracking**: mutations bubble up from arg → node → graph
+- **Observer pattern**: `IGraphEditor` / `INodeEditor` / `INetEditor` interfaces
+- **Mutation batching**: `edit_start()` / `edit_commit()` for transactional edits
+- **Sentinels**: `$empty` (node) and `$unconnected` (net) — always valid, never null
+
+The builder also handles v0→v1 format migration, auto-generated IDs (`$auto-xxx` → `$a-N`),
+and shadow node folding during import.
+
+#### Serialization (`serial.h`, `serial.cpp`)
+
+The `.atto` file format is TOML-like with section headers:
+
+```toml
+version = "instrument@atto:0"
+
+[viewport]
+x = -6131.11
+y = -1999.86
+zoom = 1.4641
+
+[[node]]
+guid = "934e3b98bb914e95"
+type = "decl_type"
+args = ["osc_res", "s:f32", "e:bool"]
+position = [757.077, 266.729]
+
+[[link]]
+from = "$auto-df6e4aa3d0d8d2bc-out0"
+to = "$auto-831e483b4e4602dc_s1-out0"
+net_name = "$osc-signal"
+```
+
+Version history: `nanoprog@0` → `nanoprog@1` → `attoprog@0` → `attoprog@1` → `instrument@atto:0`.
+Auto-migration on load handles all legacy formats.
+
+### 2. attoc — Compiler
+
+The compiler is a standalone executable that transforms `.atto` files into self-contained
+C++ projects. The pipeline:
+
+```
+.atto file
+ │
+ ▼
+load_atto() ──────────────── Parse TOML → FlowGraph
+ │
+ ▼
+resolve_type_based_pins() ── Populate dynamic pins for new/event! nodes
+ │
+ ▼
+GraphInference::run() ────── Multi-pass bidirectional type inference
+ │
+ ▼
+validate() ──────────────── Collect type errors, pin mismatches
+ │
+ ▼
+CodeGenerator::generate_*()
+ ├── _types.h ───── Struct definitions from decl_type nodes
+ ├── _program.h ─── Class declaration with event handlers
+ ├── _program.cpp ─ Implementation: node logic, expression eval
+ ├── CMakeLists.txt ───── Build script for the instrument
+ └── vcpkg.json ───────── Dependency manifest
+```
+
+The generated C++ is a complete, standalone project. Each instrument compiles
+independently with its own CMakeLists.txt.
+
+### 3. attoflow — Visual Editor
+
+The editor has a three-layer architecture:
+
+```
+FlowEditorWindow (top-level)
+├── Tab management (multiple .atto files)
+├── Project file browser
+├── Build/run toolbar
+├── Child process management
+│
+└── Editor2Pane (per-tab)
+ ├── Wraps GraphBuilder (semantic model)
+ ├── Implements IGraphEditor observer
+ ├── Wire connection logic (drag, connect, reconnect)
+ ├── NodeEditorImpl per node (layout cache)
+ ├── NetEditorImpl per net
+ │
+ └── VisualEditor (rendering base)
+ ├── Canvas state (pan, zoom, grid)
+ ├── Hover detection (wires → nodes → pins)
+ ├── Node rendering (pins, inline args, errors)
+ └── Wire drawing (bezier curves, WireInfo)
+```
+
+The hover system uses a distance-based priority: wires → nodes → pins, with pins
+getting a priority bias for easier selection.
+
+Pin shapes encode type categories:
+- **Circle** — Data
+- **Square** — Bang
+- **Triangle** — Lambda
+- **Diamond** — Variadic args / optional
+
+### 4. attohost — Runtime (Planned)
+
+The runtime architecture separates the editor from user program execution:
+
+```
+attoflow.exe attohost.exe
+(editor process) (host process)
+ │ │
+ │ spawn + pipe name arg │
+ ├──────────────────────────────>│
+ │ │ LoadLibrary(instrument.dll)
+ │ │ resolve on_start()
+ │ IPC: wire table │ run on_start()
+ │<──────────────────────────────┤
+ │ │
+ │ IPC: dirty wire updates │ per-frame/tick updates
+ │<──────────────────────────────┤
+ │ │
+ │ IPC: value override │
+ ├──────────────────────────────>│ inject values for debugging
+ │ │
+ │ IPC: reload signal │
+ ├──────────────────────────────>│ unload dll, load new, restart
+```
+
+Key design decisions:
+- **Process isolation**: user program crash doesn't take down the editor
+- **Hot-reload**: unload DLL, load new one, call `on_start` again
+- **wire**: zero-cost in release (typedef to T), inspectable in debug mode
+- **Named pipes**: editor is always the server, host is always the client
+
+## Cross-Cutting Concerns
+
+### Node Type System
+
+There are 38 built-in node types organized into categories:
+
+| Category | Nodes |
+|---------------|----------------------------------------------------------|
+| Data | expr, new, dup, str, select, cast, void |
+| Collections | append/append!, erase/erase!, iterate/iterate!, next |
+| Control | select!, lock!, iterate!, store!, output_mix! |
+| Declarations | decl_type, decl_var, decl_event, decl_import, ffi, decl |
+| Events | on_key_down!, on_key_up!, event! |
+| Special | call/call!, lock/lock!, label, error |
+
+Bang (`!`) nodes have execution ordering — they fire in the order of their bang chain,
+providing deterministic side-effect sequencing in a dataflow graph.
+
+### Standard Library
+
+The `attostd/` directory contains `.atto` module files that declare FFI bindings:
+
+- **gui.atto**: `av_create_window(title, audio_tick, sample_rate, channels, video_tick, width, height, on_close)` — creates a window with audio and video callbacks
+- **imgui.atto**: 25+ ImGui bindings (buttons, sliders, text, windows, layout)
+
+These are imported via `decl_import` nodes in user instruments.
+
+### Web Target (Planned)
+
+Future web deployment via a compile server:
+
+```
+Browser (Emscripten build of editor)
+ │
+ │ POST /compile (sends .atto)
+ ▼
+Compile Server (hardened Pi)
+ │ attoc → C++ → emcc → .wasm
+ ▼
+Browser loads result as side module
+```
+
+The server is containerized with no network access inside, 60s timeout, 512MB RAM limit,
+read-only rootfs. Only `.atto` goes in (small attack surface), controlled C++ comes out.
+
+## Design Principles
+
+1. **Zero-dependency core**: attolang has no external dependencies. Only the editor needs SDL3/ImGui.
+2. **Graph IS the program**: no separate AST. The FlowGraph is the canonical representation, serialized directly.
+3. **Type-driven everything**: inference, validation, codegen, and editor rendering all flow from the type system.
+4. **Bidirectional inference**: types propagate both forward (producers → consumers) and backward (consumers → producers).
+5. **Observer not polling**: GraphBuilder notifies editors of changes via interfaces, not polling for dirty state.
+6. **Sentinels over nulls**: `$empty` and `$unconnected` are always-valid stand-ins, eliminating null checks.
+7. **Process isolation**: the editor and runtime are separate processes communicating via IPC.
+8. **Instruments are self-contained**: each compiled instrument is a standalone C++ project with its own build system.
diff --git a/docs/attolang.md b/docs/attolang.md
index 61f58cd..3517e99 100644
--- a/docs/attolang.md
+++ b/docs/attolang.md
@@ -457,27 +457,31 @@ Nodes with input or output bangs:
## Inline Expressions
-All non-declaration nodes support **inline expressions** in their arguments. Each space-separated arg token replaces the corresponding descriptor input. If an arg is an inline expression (a literal, symbol, or complex expression), that input slot is "filled" and does not require a pin connection. Only `$N` references within inline expressions create actual input pins.
+All non-declaration nodes support **inline expressions** in their arguments. Each arg maps 1:1 to a descriptor input port. An arg can be:
+
+- **Net reference** (`$net-name`): connects to a named net — produces a visible input pin
+- **Expression** (`sin($0)+1`): inline expression — displayed in node text, no pin
+- **Literal** (`42`, `"hello"`, `true`): inline constant — displayed in node text, no pin
+- **Symbol** (`oscs`, `sin`): bare identifier resolved via symbol table — displayed in text, no pin
+
+Only net references produce visible input pins. All other arg types fill the slot inline.
### Rules
-1. Each arg token (space-separated, respecting parentheses and quotes) maps to a descriptor input left-to-right
-2. The number of arg tokens must not exceed the node's descriptor input count (error otherwise)
-3. `$N` references within inline args create input pins; symbol references (bare names) do not
-4. Pin indices must be contiguous starting from 0 — gaps (e.g. `$0` and `$2` without `$1`) are errors
-5. Descriptor inputs beyond the number of inline args remain as pin connections
+1. Each arg maps to a descriptor input left-to-right
+2. The number of args must not exceed the node's descriptor input count (plus va-args if applicable)
+3. `$N` references within expressions create **remap pins** (mapped via the `remaps` array)
+4. Remap indices must be contiguous starting from 0 — gaps produce errors
+5. `$name` (non-numeric, starting with `$`) references a named net and produces a visible pin
+6. Bare names (no `$` prefix) are symbols resolved via the symbol table, not pins
-### Examples (store! has 2 descriptor inputs: target, value)
+### Examples (store! has 3 descriptor inputs: bang_in, target, value)
-| Node text | Pins | Explanation |
-|-----------|------|-------------|
-| `store!` | target, value | No inline args — both inputs are pins |
-| `store! oscs` | value | target filled by symbol `oscs` (resolves via symbol table to `&T`) |
-| `store! oscs 42` | (none) | Both filled inline |
-| `store! oscs $0` | $0 | target = symbol, value = pin $0 |
-| `store! $1 $0` | $0, $1 | Both inline but reference pins |
-| `store! $0 $1 $2` | error | Too many args (store! takes 2) |
-| `store! $0 $2` | error | Missing pin $1 |
+| Args | Visible pins | Explanation |
+|------|-------------|-------------|
+| `["$bang-src", "$var-ref", "$val-net"]` | 3 pins | All net refs — all visible |
+| `["$bang-src", "oscs", "$0"]` | 2 pins (bang + remap $0) | target filled by symbol `oscs` |
+| `["$bang-src", "oscs", "42"]` | 1 pin (bang only) | Both target and value filled inline |
## Expression Language
@@ -627,8 +631,7 @@ When a lambda's data dependency traces back to a node in the caller scope, that
Bang pins represent `() -> void` callable connections for control flow:
- **BangTrigger** (top square): The node's callable entry point. When invoked, the node executes. Typed as `() -> void`. Can be used as a value source — connecting a BangTrigger to a data Input passes the `() -> void` callable as a value (e.g., to store it in a variable).
-- **BangNext** (bottom square): The node's continuation. After execution, the node calls whatever is connected here. Typed as `() -> void`. Links go FROM BangNext TO BangTrigger.
-- **Post-bang** (side): Fires after the node's inline expressions are evaluated. Same semantics as BangNext.
+- **BangNext** (bottom square): The node's continuation output. After execution, the node calls whatever is connected here. Typed as `() -> void`. Links go FROM BangNext TO BangTrigger. The first output pin on bang nodes is always a `BangNext` named `next` — this replaces the old `post_bang` pseudo-pin and is rendered at the same visual position.
**Link direction:** BangNext → BangTrigger. The "next" pin calls the "trigger" pin.
@@ -636,44 +639,205 @@ Bang pins represent `() -> void` callable connections for control flow:
**Bidirectional BangTrigger:** A BangTrigger pin can be both a link destination (receiving bang chain flow from BangNext) and a link source (providing its `() -> void` value to a data Input pin).
-## File Format (.atto)
+## File Format (instrument@atto:0)
-TOML-like format:
+TOML-like format with named nets instead of explicit pin-to-pin connections.
```
-version = "attoprog@0"
-
-[viewport]
-x = -500.0
-y = -200.0
-zoom = 1.5
+# version instrument@atto:0
[[node]]
-guid = "a3f7c1b2e9d04856"
+id = "$gen-expr"
type = "expr"
-args = ["$0+$1"]
-position = [100, 200]
-connections = ["a3f7c1b2e9d04856.out0->b4c8d9e0f1a23456.0"]
+args = ["sin($0.p)*$1/32.f"]
+remaps = ["$iter-item", "$iter-amp"]
+position = [1866.25, 1443.11]
+
+[[node]]
+id = "$store-p"
+type = "store!"
+args = ["$0.p"]
+remaps = ["$iter-item"]
+position = [2133.84, 1421.55]
```
-### Viewport Section
+### Version Header
+
+First line: `# version instrument@atto:0` (comment-style).
+
+Legacy formats (`nanoprog@0`, `nanoprog@1`, `attoprog@0`, `attoprog@1`) are loaded via a legacy parser and auto-migrated. Saving always writes `instrument@atto:0`.
+
+### Node IDs and Net Names
+
+Node IDs and net names share the same namespace:
+- Format: `$[a-zA-Z_-][a-zA-Z0-9_-]*`
+- Auto-generated on import: `$a-0`, `$a-1`, ... `$a-f`, `$a-10`, ... (compact hex, migrated from old `$auto-`)
+- `$0`, `$1`, ... `$N` are reserved for expression pin inputs (remaps)
+- `$unconnected` is a reserved sentinel net for unconnected pins
+- `$empty` is a reserved sentinel node for unassigned pin ownership
+- The `$` prefix is stored in the file
+
+### Sentinel Entries
+
+| Sentinel | Type | Purpose |
+|---|---|---|
+| `$unconnected` | Net | Default wire for unconnected pins |
+| `$empty` | Node | Default node owner for pins not yet assigned to a node |
+
+Both are pre-registered by `GraphBuilder::ensure_sentinels()`. Direct `find()` or `find_or_create_net()` calls with these names throw — use `gb->unconnected_net()` / `gb->empty_node()` instead.
+
+### Node Structure
+
+```toml
+[[node]]
+id = "$a-5" # compact hex identifier
+type = "store!" # node type name
+args = ["oscs", "$0"] # inline arguments (expressions, literals, net refs)
+remaps = ["$a-3-out0"] # $N → net mapping for expression pin inputs
+position = [100, 200] # canvas coordinates
+```
-The optional `[viewport]` section stores the editor's camera state. It must appear after `version` and before any `[[node]]` entries.
+### Node Kinds
-| Field | Type | Description |
-|--------|-------|--------------------------------|
-| `x` | float | Horizontal scroll offset |
-| `y` | float | Vertical scroll offset |
-| `zoom` | float | Zoom level (1.0 = default) |
+| Kind | Bang input | Bang output | Side-bang | Description |
+|---|---|---|---|---|
+| `Flow` | No | Yes (side-bang, right) | Yes | Dataflow node — all flow nodes have a side-bang |
+| `Banged` | Yes (top) | Yes (bottom) | No | Imperative node with bang trigger |
+| `Event` | No | Yes (bottom) | No | Event source |
+| `Declaration` | Yes (top) | Yes (bottom) | No | Compile-time declaration |
+| `Special` | No | No | No | Label or Error |
+
+Flow nodes always have `outputs[0]` as the side-bang (BangNext). It is rendered on the right side, not at the bottom.
+
+### Arguments (`args`)
+
+Each entry in the `args` array is a singular expression (space-delimited in the source, already split in the file). Arguments map 1:1 to the node's descriptor input ports.
+
+An argument can be:
+- **Net reference** (`$name`): connects to a named net — produces a visible input pin
+- **Expression** (`sin($0)+1`): inline expression with `$N` pin refs — displayed in node text, not a pin
+- **Number** (`42`, `3.14f`): inline constant — displayed in node text
+- **String** (`"hello"`): inline string literal — displayed in node text
+
+Only net reference entries produce visible input pins. Inline values are displayed in the node's label text.
+
+### Remaps (`remaps`)
+
+The `remaps` array maps `$N` expression pin inputs to named nets:
+
+```toml
+remaps = ["$a-2-out0", "$a-2-out1"]
+```
+
+- `remaps[0]` = net for `$0`, `remaps[1]` = net for `$1`, etc.
+- `$unconnected` for unconnected expression inputs
+- Remaps are always net references
+
+### Pin Model
+
+Pins are graph entities (`FlowArg2` hierarchy: `ArgNet2`, `ArgNumber2`, `ArgString2`, `ArgExpr2`). Each pin has:
+- **`node()`** — owning FlowNodeBuilder (always valid, `$empty` if unassigned)
+- **`wire()`** / **`net()`** — associated NetBuilder (always valid, `$unconnected` if unassigned)
+- **`port()`** — PortDesc2 descriptor (null for remaps)
+- **`name()`** — computed: `"port_name"` or `"va_name[idx]"` or `"remaps[idx]"`
+- **`is_remap()`** — true if port is null (remap pin)
+
+#### Input pins (top of node, left to right)
+
+Only net reference (`$name`) arguments produce visible pins. The visible pin count is:
+
+| Section | Visible pins | Source |
+|---|---|---|
+| **Base args** | Only net refs in `args` | 1:1 with descriptor input ports |
+| **Input va-args** | Only net refs in va-args | Named `va_name[0]`, `va_name[1]`, ... |
+| **+diamond** | Add button (if node has input va-args) | Rendered as ◇ with + |
+| **Remaps** | All entries | `$0`, `$1`, ... from expressions |
-### Connection Format
+#### Output pins (bottom of node)
-Connections use pin IDs: `".->."`
+Fixed descriptor output ports are rendered at the bottom, EXCEPT for flow nodes where `outputs[0]` (side-bang) is rendered on the right side. Output va-args follow fixed outputs.
+
+| Section | Pins | Source |
+|---|---|---|
+| **Fixed outputs** | Descriptor output ports (skip side-bang for flow) | `outputs[skip_sb..]` |
+| **Output va-args** | Dynamic outputs | `outputs_va_args[]` |
+
+Expr/expr! have output va-args sized to match expression count. Event! has output va-args for spillover outputs.
+
+#### Pin kinds
+
+| Kind | Visual | Description |
+|---|---|---|
+| `BangTrigger` | Square (top) | Trigger input |
+| `Data` | Circle | Data value |
+| `Lambda` | Down-pointing triangle | Lambda capture (accepts node refs) |
+| `BangNext` | Square (bottom/right) | Bang continuation output |
+| `Va-args` | Diamond (◇) | Variable-length input/output |
+| `Optional` | Diamond with ? | Optional input (trailing) |
+
+#### Special pins
+
+| Pin | Position | Visual | Description |
+|---|---|---|---|
+| **Lambda grab** | Left center | Left-pointing triangle (purple) | Capture this node as lambda |
+| **Side-bang** | Right center | Square (yellow) | Post-bang output (flow nodes only) |
+
+### Lambda Captures via Node ID
+
+When a `$id` in an argument resolves to a **node** (not a net), it is a lambda capture. The wire renders from the source node's lambda grab (left side) to the destination's lambda pin.
+
+- Node reference → lambda capture (wire from grab)
+- Net reference → data wire (wire from output pin)
+- `Lambda` pins accept only node refs
+- `Data` pins can accept either
+
+### Input Va-args
+
+Some node types accept a variable number of additional inputs. The va-args template is defined on `NodeType2::input_ports_va_args`.
+
+| Node | Va-args template | Description |
+|---|---|---|
+| `new` | `field` | Constructor fields (`field[0]`, `field[1]`, ...) |
+| `call` / `call!` | `arg` | Function arguments (`arg[0]`, `arg[1]`, ...) |
+| `lock` / `lock!` | `param` | Lambda parameters (`param[0]`, `param[1]`, ...) |
+
+### Output Va-args
+
+Some node types have dynamic output counts. The template is defined on `NodeType2::output_ports_va_args`.
+
+| Node | Va-args template | Description |
+|---|---|---|
+| `expr` | `expr` | One output per expression (`expr[0]`, `expr[1]`, ...) |
+| `expr!` | `expr` | Same, after the fixed `next` output |
+| `event!` | `args` | Event argument outputs |
+
+### Optional Ports
+
+Optional ports are always trailing in the descriptor. They are split into separate `input_optional_ports` / `num_inputs_optional` on NodeType2. If not connected, they are omitted from `parsed_args` (shorter array). The editor shows absent optionals as ◇ with ? inside.
+
+Currently only `decl_var` has an optional port (`initial`).
+
+### Viewport (Meta File)
+
+Viewport state is stored in `.atto/.yaml`, not in the `.atto` file:
+
+```yaml
+# Editor metadata for main.atto
+viewport_x: -1504.32
+viewport_y: -551.573
+viewport_zoom: 4.17725
+```
+
+The `.atto/` directory is gitignored. Node positions remain in the `.atto` file.
+
+### Labels and Errors
+
+```toml
+[[node]]
+id = "$lbl-types"
+type = "label"
+args = ["Types"]
+position = [766, 335]
+```
-Pin names:
-- Data/lambda inputs: `0`, `1`, `2`, ... or named (e.g. `gen`, `stop`)
-- Bang inputs: `bang_in0`, `bang_in1`, ...
-- Data outputs: `out0`, `out1`, ...
-- Bang outputs: `bang0`, `bang1`, ...
-- Lambda grab: `as_lambda`
-- Post-bang: `post_bang`
+Labels have exactly 1 argument (the display text). Error nodes are the same — they display the original args when parsing failed.
diff --git a/docs/changelog.md b/docs/changelog.md
new file mode 100644
index 0000000..ffcc74f
--- /dev/null
+++ b/docs/changelog.md
@@ -0,0 +1,339 @@
+# Organic Assembler — Changelog
+
+This document traces the evolution of the Organic Assembler project through its major
+development phases, architectural milestones, and design turning points.
+
+## Phase 1: Nanolang Foundation (March 23, 2026)
+
+The project began as "nanolang" — a visual programming language for building instruments.
+This phase established the core architecture that all subsequent work built upon.
+
+### Language Core
+- Created the FlowGraph data model: nodes, links, pins
+- Built a recursive-descent expression parser for inline node expressions
+- Implemented the type system with scalar types (u8–u64, s8–s64, f32, f64)
+- Added container types: vector, map, set, list, queue, ordered_map, ordered_set
+- Established the value category system: Data, Reference, Iterator, Lambda, Enum, Bang, Event
+- Implemented category sigils: `%`, `&`, `^`, `@`, `#`, `!`, `~`
+
+### Type Inference
+- Built the bidirectional type inference engine (GraphInference)
+- Multi-pass propagation: forward through connections, backward from consumers
+- Automatic integer upcasting (u8 → u16 → u32 → u64 → f32 → f64)
+- Expression node inference with operator type resolution
+
+### Compiler (nanoc)
+- Created the code generator: .atto → C++ with struct definitions, class declarations, implementations
+- Generated self-contained CMake projects for each instrument
+- FFI support for external C functions (gui, imgui bindings)
+
+### Visual Editor (nanoflow)
+- SDL3 + ImGui-based node graph editor
+- Canvas with pan/zoom, grid rendering
+- Node rendering with pins, inline arguments
+- Wire drawing with bezier curves
+- Project file browser with build/run toolbar
+- DPI scaling and retina display support
+
+### First Instruments
+- **klavier**: Piano/synthesizer instrument with oscillator synthesis, key events, audio mixing
+- **multifader**: Fader mixing instrument with vector operations
+
+### Standard Library
+- `gui.atto`: AV runtime FFI — `av_create_window` with audio_tick (48kHz) and video_tick callbacks
+- `imgui.atto`: 25+ ImGui bindings for UI construction
+
+### Serialization
+- TOML-like `.atto` format with `[[node]]` sections and `[[link]]` connections
+- Viewport persistence in metafiles (`.atto/.yaml`)
+- Version markers: `nanoprog@0`, `nanoprog@1`
+
+---
+
+## Phase 2: Type System and Lambda Development (March 24, 2026)
+
+A major deepening of the type system and introduction of lambda/closure support.
+
+### Internal Refactoring (5 phases)
+- **Phase 1–2**: Structural reorganization and code consolidation
+- **Phase 3–3b**: Pin pointer stability improvements
+- **Phase 4a–4b**: Large-scale restructuring of node type handling
+- **Phase 5**: Final cleanup and stabilization
+
+### Literal Types
+- Introduced `literal` — unified compile-time value representation
+- All constants (integers, floats, booleans, strings) represented as literals
+- `is_generic` flag for unresolved type parameters (e.g., `0` could be u8/u32/f32)
+- `is_unvalued_literal` flag for input pins expecting values (e.g., `literal`)
+- Literal decay: operations consume literals and produce runtime types
+- Backpropagation of literal types resolves generics from context
+- `strip_literal()` utility for removing literal annotations from operation results
+
+### Symbol Types
+- `symbol` — first-class named references carrying their decay type
+- `undefined_symbol` — bare identifiers not yet in the symbol table
+- Automatic symbol decay when consumed by operations
+- Symbol table populated by declaration nodes and built-in entries
+- Reserved keywords: `symbol`, `undefined_symbol`, `literal`
+
+### Shadow Nodes
+- Canonical shadow node system for expression handling
+- Auto-generated expression nodes for inline arguments
+- Shadow nodes transparent to users (not rendered in editor)
+- Removed before serialization
+
+### Lambda System
+- Lambda capture and parameter collection
+- `as_lambda` pin for lambda grab rendering
+- Stored lambda support (`store! $name` captures lambda)
+- Lambda boundary detection (scope tracking)
+- Lambda parameter identification via unconnected pins
+
+### Bang/Trigger System
+- Renamed BangInput → BangTrigger, BangOutput → BangNext (clearer semantics)
+- Bang connections to input pins
+- `select!` supporting three bang outputs (true/false/done)
+- Deterministic execution ordering via bang chains
+
+### Expression System Enhancements
+- Function types: `(x:f32)->f32` parsed as type expressions
+- Struct types: `{x:f32 y:f32}` parsed with space-separated fields
+- Struct literals: `{x:1.0f, y:2.0f}` with comma-separated values
+- `TypeApply`: `$0<$1,$2>` speculative generic application
+- Validation functions per builtin type (array, vector, map) with error messages
+
+### Declaration Refactor
+- All nodes except `label` get shadow nodes
+- `decl_var` descriptor: 2 inputs (name + type), optional 3rd (init)
+- `build_context` and `build_registry` removed — replaced by `decl` bang chain
+- `decl_import` pin type: `literal`
+- Helpers: `get_decl_name(node, graph)`, `get_decl_type_str(node, graph)`
+
+### UI/UX Improvements
+- Bottom panel with tabbed error and build log display
+- DPI scaling refinements
+- Search bar for node discovery
+- Improved error reporting with per-node and per-link messages
+
+---
+
+## Phase 2b: The Great Rename (March 24, 2026)
+
+**Commit f65b7c0**: Complete rename from nanolang to attolang.
+
+- All source directories: `nano` → `atto`, `nanoc` → `attoc`, `nanoflow` → `attoflow`
+- File extensions: `.nano` → `.atto`
+- Documentation: `nanolang.md` → `attolang.md`
+- CMake targets and internal references updated
+- Format versions: `attoprog@0`, `attoprog@1`
+
+The rename reflected the project's evolution from a "nano" (small) language to
+"atto" (even smaller, but also a pun on the attosecond — the smallest measurable
+unit of time, fitting for a real-time instrument platform).
+
+---
+
+## Phase 3: Instrument Specification (March 25, 2026)
+
+### instrument@atto:0
+- **Commit 3498b4c**: Formal version specification `instrument@atto:0`
+- Established the canonical file format for instruments
+- Large-scale klavier scene updates (703 lines changed)
+- Serial.cpp rewritten (1128 line changes) for the new format
+
+### Test Suite
+- `test_inference.cpp` with 94 unit tests for type inference
+- Custom test framework with TEST/ASSERT/ASSERT_EQ/ASSERT_TYPE macros
+- Static test registry pattern for auto-discovery
+- Programmatic graph construction via test helper utilities
+
+### Named Styles
+- Visual consistency system for editor rendering
+- Centralized color and spacing definitions
+
+### Wire Management
+- Link name editing in the editor
+- Auto-wire functionality for common connection patterns
+- Named nets for organizing complex wiring
+
+---
+
+## Phase 4: GraphBuilder Architecture (March 25–26, 2026)
+
+A fundamental restructuring of how graphs are constructed and edited.
+
+### BuilderEntry Hierarchy
+- `BuilderEntry` base class with `IdCategory` (Node/Net)
+- `FlowNodeBuilder` and `NetBuilder` as concrete types
+- `as_node()` / `as_net()` accessors with `shared_from_this()`
+- Every entry has a stable ID, category, and dirty flag
+
+### FlowArg2 Pin Model
+- Inheritance hierarchy replacing variants: `ArgNet2`, `ArgNumber2`, `ArgString2`, `ArgExpr2`
+- Each arg always has valid `node()`, `net()`, `port()` — sentinels instead of null
+- Computed names: `"port_name"`, `"va_name[idx]"`, `"remaps[idx]"`
+- Remap tracking: `is_remap()`, `remap_idx()`, `input_pin_idx()`, etc.
+- Private constructors — only `GraphBuilder::build_arg_*()` can create args
+
+### Dirty Tracking
+- Three levels: arg → node → graph
+- `mark_dirty()` on any arg bubbles to its owning node, then to the graph
+- Layout-only dirty (position changes) tracked separately, doesn't trigger re-inference
+- `is_dirty()` queries at every level
+
+### Sentinel Pattern
+- `$empty` (FlowNodeBuilder) — default for unassigned node references
+- `$unconnected` (NetBuilder) — default for unassigned net references
+- Pre-registered via `ensure_sentinels()`, never destroyed
+- Eliminates null pointer checks throughout the codebase
+
+### Mutation Batching
+- `edit_start()` / `edit_commit()` for transactional graph edits
+- Mutations queued as callbacks, fired in order on commit
+- Prevents partial updates from reaching observers
+- Throws if mutations pending from a previous uncommitted edit
+
+### Format Migration
+- v0→v1 migration: name-based port mapping using old/new descriptors
+- Shadow folding during import
+- Lambda `-as_lambda` stripping
+- `$auto-xxx` → `$a-N` compact hex re-ID on import
+
+### NodeKind2 Classification
+- `Flow` — standard data processing nodes
+- `Banged` — nodes with execution ordering (bang chains)
+- `Event` — event source nodes
+- `Declaration` — compile-time declaration nodes
+- `Special` — label, error, and other non-data nodes
+
+---
+
+## Phase 5: Editor2 Architecture (March 26, 2026)
+
+The next-generation editor built on top of the GraphBuilder.
+
+### Editor2Pane
+- Wraps GraphBuilder as the semantic model
+- Implements `IGraphEditor` observer for reactive updates
+- `NodeEditorImpl` per node — caches layout and visual state
+- `NetEditorImpl` per named net
+- Wire connection logic: drag from pin, snap to target, reconnect
+
+### Hover System
+- `hover_item_` = `variant`
+- `detect_hover()` returns best match by distance priority
+- Priority order: wires → nodes → pins (pins get priority bias)
+- `draw_hover_effects()` renders highlights and tooltips
+- All elements hoverable: pins, nodes, wires, lambda grabs, side-bangs, +diamonds
+
+### Selection System
+- `set` for multi-selection
+- Ctrl+click toggles individual selection
+- Selection rectangle drag for area selection
+- Node dragging with overlap prevention and padding
+
+### Pin Shapes
+- Circle → Data pins
+- Square → Bang pins
+- Triangle → Lambda pins
+- Diamond → Variadic args / optional pins
+
+### Visual Refinements
+- Liberation Mono font embedded via CMake `file(READ HEX)`
+- Editor style struct `S` centralizing all colors, sizes, thresholds
+- Tooltip scaling and positioning
+- Non-overlapping node layout algorithm
+- Auto string renumbering for wire IDs
+
+### Code Organization
+- Extracted `node_renderer.cpp` from monolithic editor
+- Extracted `tooltip_renderer.cpp`
+- Separated `window.cpp/h` from editor logic
+- Legacy editor1 moved to `src/legacy/`
+- Removed `#if LEGACY_EDITOR` conditionals
+
+---
+
+## Phase 6: Wiring and Networking (March 26–27, 2026)
+
+Current active development on branch `skmp/wirring-and-networking`.
+
+### Wire Connection System
+- Pin grabbing and dragging interaction
+- Visual wire preview during drag
+- Snap-to-pin connection on release
+- Wire reconnection (drag existing wire to new target)
+- Wire grab undo (remembers previous connection state)
+
+### Named Nets
+- Nets editor panel for viewing all named connections
+- `$unconnected` sentinel replacing removed nets
+- Net name display and management
+
+### Multi-Output Nodes
+- `num_outputs` increased from 1 to 2 for applicable nodes
+- Variadic output args support
+- Output pin mapping and indexing
+
+### Visual Polish
+- Diamond pin hover detection for +pins
+- Unified highlight system across all hoverable elements
+- Wire hover: highlights all wires sharing the same entry
+- Lambda wire highlighting for connected scope
+- Scroll pan speed controls in editor style
+- Shortened node IDs for cleaner display
+
+### Delete Functionality
+- Delete hovered items (nodes, wires, nets)
+- Cleanup of orphaned connections on deletion
+
+---
+
+## Planned Future Work
+
+### args Elimination
+Replace `node.args` string with structured pre-parsed fields on `FlowNode`:
+- Pre-extract ALL metadata at load time (type fields, variable names, function refs)
+- `tokenize_args()` (~40+ call sites) and `scan_slots()` (~10 sites) to be removed
+- Migration path: codegen first (highest call count), then inference, then editor
+
+### Nested Lambda Scope Fix
+Proper lambda boundary detection for nested lambdas:
+- Graph analysis pass identifying all lambda boundaries
+- Node-to-lambda-scope assignment ("lambda ownership")
+- `collect_lambda_params` respects scope boundaries
+- Prevents outer stored lambda params from leaking into inner lock/iterate lambdas
+
+### DLL Host Architecture
+- `attohost.exe` — separate host process for running instruments
+- `attoc` generates `.dll` (SHARED) instead of `.exe`
+- Hot-reload via DLL unload/load cycle
+- `wire` — zero-cost in release, inspectable in debug
+- IPC via named pipes for live value inspection
+
+### Web Deployment
+- Editor compiled to Emscripten (SDL3 + ImGui in browser)
+- Compile server on hardened Pi
+- `POST /compile` endpoint: .atto → C++ → emcc → .wasm
+- Containerized with no network, 60s timeout, 512MB RAM
+
+### Compile-Time Phase
+- `decl` bang chain evaluation (compile-time interpreter)
+- `decl_type` outputs `type` for metaprogramming
+- Event names without `~` prefix
+- Namespace `::` operator in expressions
+- Type construction via calling (e.g., `f32(42)` as cast)
+
+---
+
+## Version History
+
+| Version | Period | Key Change |
+|--------------------|-------------|------------------------------------------|
+| `nanoprog@0` | Mar 23 | Initial format |
+| `nanoprog@1` | Mar 23 | Pin structure changes |
+| `attoprog@0` | Mar 24 | Post-rename format |
+| `attoprog@1` | Mar 24–25 | Structured args, extended pins |
+| `instrument@atto:0`| Mar 25+ | Formal instrument specification |
+
+Each version is auto-migrated on load. The serializer always writes the latest format.
diff --git a/docs/coding.md b/docs/coding.md
new file mode 100644
index 0000000..14aa7a3
--- /dev/null
+++ b/docs/coding.md
@@ -0,0 +1,451 @@
+# Organic Assembler — Coding Conventions
+
+This document describes the C++ coding conventions, idioms, and practices used throughout
+the Organic Assembler codebase.
+
+## Language Standard
+
+The project uses **C++20** (`CMAKE_CXX_STANDARD 20`). Features actively used:
+
+- `std::variant<>` for algebraic data types
+- `std::shared_ptr<>` / `std::weak_ptr<>` / `std::unique_ptr<>` for ownership
+- `std::enable_shared_from_this` for safe self-references
+- Range-based for loops everywhere
+- Lambda expressions with captures for callbacks and inline logic
+- `constexpr` for compile-time computation
+- Default member initializers
+- `auto` for type deduction (used judiciously)
+- Fold expressions: `((id == ids) || ...)` for variadic checks
+- Scoped enums (`enum class`) exclusively
+
+Features intentionally **not** used:
+- RTTI / `dynamic_cast` on raw pointers (only on `shared_ptr` casts)
+- Exceptions as control flow (used only for invariant violations)
+- Concepts / constraints
+- Modules (C++20)
+- Coroutines
+- `std::optional<>` (error strings or variants preferred)
+- Structured bindings (rarely used)
+
+## Naming Conventions
+
+### Types and Classes
+```cpp
+// PascalCase for all type names
+struct FlowGraph;
+struct GraphBuilder;
+struct TypeExpr;
+class CodeGenerator;
+enum class NodeTypeID;
+enum class TypeKind;
+```
+
+### Functions and Methods
+```cpp
+// snake_case for all functions and methods
+void add_node();
+void mark_dirty();
+void rebuild_pin_ids();
+TypePtr resolve_type();
+bool is_numeric(const TypePtr& t);
+```
+
+### Variables
+```cpp
+// snake_case for locals and parameters
+int result;
+FlowNode* source_node;
+int temp_counter;
+
+// snake_case with trailing underscore for private members
+ArgKind kind_;
+std::shared_ptr owner_;
+FlowNodeBuilderPtr node_;
+bool dirty_ = false;
+
+// snake_case without underscore for public members
+std::vector nodes;
+std::vector links;
+std::map entries;
+```
+
+### Enum Values
+```cpp
+// PascalCase for enum values
+enum class TypeKind { Void, Bool, String, Scalar, Named, Container, ... };
+enum class NodeTypeID : uint8_t { Expr, New, Dup, Str, Select, ... };
+enum class ArgKind : uint8_t { Net, Number, String, Expr };
+enum class IdCategory { Node, Net };
+```
+
+### Type Aliases
+```cpp
+// PascalCase with descriptive suffixes
+using TypePtr = std::shared_ptr;
+using ExprPtr = std::shared_ptr;
+using FlowArg2Ptr = std::shared_ptr;
+using FlowNodeBuilderPtr = std::shared_ptr;
+using NetBuilderPtr = std::shared_ptr;
+using BuilderEntryWeak = std::weak_ptr;
+using NodeId = std::string;
+using BuilderError = std::string;
+using Remaps = std::vector>;
+```
+
+### Constants and Statics
+```cpp
+// Static arrays for descriptors
+static const NodeType NODE_TYPES[] = { ... };
+static constexpr int NUM_NODE_TYPES = sizeof(...) / sizeof(...);
+
+// Inline helpers in headers
+inline bool is_numeric(const TypePtr& t);
+inline TypeCategory parse_category(char c);
+```
+
+## Header Guards
+
+Always `#pragma once`. No `#ifndef` guards are used anywhere in the codebase.
+
+```cpp
+#pragma once
+#include
+#include
+// ...
+```
+
+## Include Order
+
+1. Standard library headers
+2. Project headers
+
+```cpp
+#pragma once
+#include "model.h"
+#include "types.h"
+#include "node_types.h"
+#include "graph_editor_interfaces.h"
+#include
+#include
+#include