diff --git a/README.md b/README.md
index 616a333..8dc1dbd 100644
--- a/README.md
+++ b/README.md
@@ -1,265 +1,356 @@
# Anvil
-**Arduino project generator and build tool -- forges clean embedded projects.**
+**Forge clean, testable Arduino projects from a single command.**
-A single binary that scaffolds self-contained Arduino projects with hardware
-abstraction, Google Mock infrastructure, and a streamlined build/upload/monitor
-workflow. Works on Linux and Windows.
+Anvil generates self-contained Arduino projects with hardware abstraction,
+test infrastructure, sensor libraries, and a complete build/upload/test
+workflow. Once generated, the project stands alone -- Anvil is a scaffolding
+tool, not a runtime dependency.
-Generated projects are fully standalone -- they only need `arduino-cli` in
-PATH. The Anvil binary is a scaffolding and diagnostic tool, not a runtime
-dependency.
+
+
+
-Anvil is a [Nexus Workshops](https://nxlearn.net) project.
+Anvil is a [Nexus Workshops](https://nxlearn.net) project, built for FTC
+robotics teams and embedded systems students.
-## Install
+---
-Download the latest release binary for your platform:
+## Getting Started
-```bash
-# Linux
-chmod +x anvil
-sudo mv anvil /usr/local/bin/
-
-# Windows -- add anvil.exe to a directory in your PATH
-```
+### Install
+Download the release binary for your platform and add it to your PATH.
Then run first-time setup:
```bash
anvil setup
```
-This checks for `arduino-cli`, installs the `arduino:avr` core, and verifies
-your system is ready. If something is missing, Anvil tells you exactly what
-to do.
+This installs `arduino-cli` and the `arduino:avr` core. If something is
+already installed, Anvil skips it. Run `anvil doctor` at any time to
+check your environment.
-## Your First Project
-
-Create a project, plug in your board, and upload:
+### Create a project
```bash
anvil new blink
cd blink
```
-On **Linux/macOS**:
-```bash
-./build.sh # compile only (verify)
-./upload.sh # compile + upload to board
-./upload.sh --monitor # compile, upload, open serial monitor
-./monitor.sh # serial monitor (no compile)
-./test/run_tests.sh # host-side unit tests (no board needed)
-```
-
-On **Windows**:
-```bat
-build REM compile only
-upload REM compile + upload
-upload --monitor REM compile, upload, open serial monitor
-monitor REM serial monitor
-test\run_tests REM host-side unit tests
-```
-
-Every script reads its settings from `.anvil.toml` in the project root.
-You edit that file to change the board, baud rate, include paths, or
-compiler flags. No Anvil binary required for any of this -- students clone
-the repo, plug in a board, and run `upload`.
-
-## Telling Anvil Which Board to Use
-
-When you run `upload` without specifying a port, Anvil's scripts auto-detect
-the board. They prefer USB serial ports over legacy motherboard COM ports,
-which is usually the right thing. But if you have multiple boards plugged in,
-or you want a consistent setup, you can pin a specific device.
-
-### See what's connected
+That's it. You have a complete project with build scripts, a HAL interface,
+mock infrastructure, and a starter test file. Plug in your board and run:
```bash
-anvil devices
+./build.sh # compile (verify it builds)
+./upload.sh # compile + upload to board
+./monitor.sh # serial monitor
+./test.sh # host-side tests (no board needed)
```
-This lists every serial port with its board name, protocol, and -- for USB
-devices -- its VID:PID identifier. The VID:PID is a pair of hex codes that
-uniquely identifies the USB chip on your board (e.g. `0403:6001` for an FTDI,
-`1a86:7523` for a CH340).
+On Windows, use `build.bat`, `upload.bat`, `monitor.bat`, `test.bat`.
+Every script reads settings from `.anvil.toml` -- no Anvil binary required.
-### Save a default device
+---
+
+## Templates
+
+The default `basic` template gives you a blank canvas. For a richer starting
+point, use a composed template:
```bash
+anvil new weather_station --template weather --board uno
+```
+
+The weather template builds on basic, adding a `WeatherApp` with a TMP36
+temperature sensor driver, managed example tests demonstrating both mock and
+simulator patterns, and student test starters. To see available templates:
+
+```bash
+anvil new --list-templates
+```
+
+Templates are pure data -- each is a directory with a `template.toml`
+declaring its base, required libraries, and per-board pin defaults. Adding
+a new template requires zero Rust code changes.
+
+---
+
+## Libraries
+
+Anvil ships sensor and actuator libraries, each with four files: an abstract
+interface, a hardware implementation, a test mock, and a deterministic
+simulator.
+
+```bash
+anvil add tmp36 --pin A0 # analog temperature sensor
+anvil add button --pin 2 # digital pushbutton with debounce sim
+```
+
+See what's available and what's installed:
+
+```bash
+anvil lib --available # all libraries in the registry
+anvil lib # libraries installed in this project
+```
+
+Remove a library:
+
+```bash
+anvil remove tmp36
+```
+
+Each library installs to `lib/drivers//` with its test file in `test/`.
+The `CMakeLists.txt` auto-discovers driver directories, so adding a library
+immediately makes it available to your tests.
+
+### The mock/sim split
+
+Every library provides two test doubles:
+
+- **Mock** -- Returns exact values you set. Use in unit tests to verify your
+ application logic calls the sensor correctly and responds to specific values.
+- **Simulator** -- Returns realistic values with configurable noise, bounce,
+ or drift. Use in system tests to verify your code handles real-world
+ sensor behavior (jitter, debounce timing, averaging).
+
+This split teaches the difference between interaction testing and behavioral
+testing -- a concept that transfers directly to professional software
+development.
+
+---
+
+## Pin Management
+
+Anvil knows the pinout of every supported board. Assignments are validated
+at the command line, not when you discover a wiring bug at 9 PM.
+
+```bash
+anvil pin --assign led 13 --mode output
+anvil pin --assign tmp36_data A0 --mode analog
+anvil pin --assign spi --cs 10 # SPI bus with chip-select
+anvil pin --assign i2c # I2C (pins auto-resolved)
+```
+
+Generate a `pins.h` header with `#define` constants:
+
+```bash
+anvil pin --generate
+```
+
+Audit your wiring against library requirements:
+
+```bash
+anvil pin --audit
+```
+
+Pin assignments are stored per-board in `.anvil.toml`, so switching between
+an Uno and a Mega doesn't lose your wiring for either.
+
+---
+
+## Board Profiles
+
+A single project can target multiple boards:
+
+```bash
+anvil board --add mega
+anvil board --add nano --baud 57600
+anvil board --default mega
+```
+
+Each board gets its own `[boards.]` section in `.anvil.toml` with FQBN,
+baud rate, and independent pin assignments. Scripts use the default board
+unless you pass `--board`:
+
+```bash
+./upload.sh --board nano
+```
+
+List available presets:
+
+```bash
+anvil new --list-boards
+```
+
+---
+
+## Device Detection
+
+Anvil's scripts auto-detect your board, but you can pin a specific device:
+
+```bash
+anvil devices # list connected boards
+anvil devices --set # auto-detect and save to .anvil.local
anvil devices --set COM3 # save a specific port
-anvil devices --set # auto-detect the best port and save it
```
-Both forms write a `.anvil.local` file in your project directory. This file
-stores the port name *and* the VID:PID of the device on that port. It looks
-like this:
+The `.anvil.local` file stores both the port name and the USB VID:PID. If
+your board moves to a different port (common on Windows), the scripts find
+it by VID:PID automatically.
-```toml
-# Machine-specific Anvil config (not tracked by git)
-port = "COM3"
-vid_pid = "0403:6001"
-```
+---
-The `.anvil.local` file is gitignored by default. Each machine that works on
-the project keeps its own copy with its own port assignment.
+## Refresh and .anvilignore
-### Why VID:PID matters
-
-On Windows, if you unplug a USB device and plug it back in to a different
-port, it often gets a new COM number (COM3 becomes COM5). The VID:PID
-doesn't change -- it's baked into the USB chip. When the scripts see a
-VID:PID in `.anvil.local`, they search for that device on whatever port
-it's currently on. If it moved, you'll see:
-
-```
-info Device 0403:6001 found on COM5 (moved from COM3)
-```
-
-No manual intervention needed.
-
-### Check what's saved
+When you upgrade Anvil, existing projects still have old infrastructure.
+Refresh updates managed files without touching your code:
```bash
-anvil devices --get
+anvil refresh # dry run -- shows what would change
+anvil refresh --force # update managed files
```
-This shows the saved port and VID:PID for the current project, whether the
-device is currently connected, and whether it has moved to a different port.
+### What's protected
-### Port resolution priority
+An `.anvilignore` file (generated automatically) protects student-authored
+files from refresh. The defaults protect:
-When the scripts need a port, they check in this order:
+- Your test files (`test/test_unit.cpp`, `test/test_system.cpp`)
+- Your application code (`lib/app/*`)
+- Your sketch (`*/*.ino`)
+- Your config (`.anvil.toml`)
+- Your project files (`.gitignore`, `README.md`, `.editorconfig`, etc.)
-1. **`-p` flag** -- `upload.bat -p COM3` always wins
-2. **VID:PID** from `.anvil.local` -- finds the device regardless of port number
-3. **Saved port** from `.anvil.local` -- fallback if VID:PID lookup fails
-4. **Auto-detect** -- prefers USB serial over legacy COM ports
+Managed infrastructure (build scripts, mock headers, CMakeLists.txt, library
+drivers, template example tests) gets updated. Missing files are always
+recreated, even without `--force`.
-### Specifying a project directory
-
-Both `--set` and `--get` default to the current directory, but you can point
-them at another project:
+### Fine-grained control
```bash
-anvil devices --set COM3 -d ../other-project
-anvil devices --get -d ../other-project
+anvil refresh --ignore "test/my_helper.h" # protect a custom file
+anvil refresh --unignore "test/test_unit.cpp" # allow refresh to update it
+anvil refresh --force --file test/test_unit.cpp # one-time override
```
-## Updating Project Scripts
+Patterns support globs: `test/*.cpp`, `lib/app/*`, `*.h`.
-When you upgrade Anvil, your existing projects still have the old build
-scripts. The `refresh` command updates them to the latest versions without
-touching your source code, configuration, or test files:
-
-```bash
-anvil refresh # check current project (dry run)
-anvil refresh --force # overwrite differing scripts
-anvil refresh ../other-project # check a different project
-```
-
-Refresh only replaces build infrastructure: `build.sh`, `build.bat`,
-`upload.sh`, `upload.bat`, `monitor.sh`, `monitor.bat`, `_detect_port.ps1`,
-and `test/run_tests.sh`, `test/run_tests.bat`. It never touches `.anvil.toml`,
-`.anvil.local`, your `.ino` files, HAL headers, app code, mocks, or test
-sources.
-
-## What Anvil Does vs. What the Project Does
-
-| Need Anvil for | Don't need Anvil for |
-|---------------------------------|-------------------------------|
-| `anvil new` (create project) | `./build.sh` (compile) |
-| `anvil setup` (install core) | `./upload.sh` (flash) |
-| `anvil doctor` (diagnose) | `./monitor.sh` (serial) |
-| `anvil devices` (port scan) | `./test/run_tests.sh` (test) |
-| `anvil refresh` (update scripts)| |
-
-Once a project is created, Anvil is optional for daily work.
-
-## Commands
-
-| Command | Description |
-|----------------------------------|------------------------------------------------|
-| `anvil new NAME` | Create a new project with HAL and test scaffold |
-| `anvil setup` | Install arduino-cli and AVR core |
-| `anvil doctor` | Check system prerequisites |
-| `anvil devices` | List connected boards and serial ports |
-| `anvil devices --set [PORT]` | Save default port + VID:PID to .anvil.local |
-| `anvil devices --get` | Show saved port for this project |
-| `anvil refresh [DIR] [--force]` | Update project scripts to latest templates |
+---
## Project Structure
-Every generated project follows the same layout:
-
```
your-project/
- your-project/your-project.ino Entry point (setup + loop)
+ your-project/your-project.ino Sketch (thin shell, no logic)
lib/
hal/
- hal.h Hardware abstraction interface
- hal_arduino.h Real hardware implementation
+ hal.h Hardware abstraction (pure virtual)
+ hal_arduino.h Real Arduino implementation
app/
- your-project_app.h Application logic (testable)
+ your-project_app.h Your application logic (testable)
+ drivers/
+ tmp36/ Sensor driver (interface/impl/mock/sim)
+ button/ Actuator driver (same pattern)
test/
mocks/
- mock_hal.h Google Mock HAL
- sim_hal.h Stateful simulator HAL
- test_unit.cpp Unit tests
- CMakeLists.txt Test build system
- run_tests.sh / .bat Test runner
- build.sh / build.bat Compile sketch
- upload.sh / upload.bat Compile + upload to board
- monitor.sh / monitor.bat Serial monitor
- _detect_port.ps1 Port detection helper (Windows)
- .anvil.toml Project config (tracked by git)
- .anvil.local Machine-specific config (gitignored)
+ mock_arduino.h Arduino API shims for host compile
+ mock_hal.h Google Mock HAL
+ sim_hal.h Stateful simulator HAL
+ test_weather.cpp Managed example tests (refreshable)
+ test_unit.cpp Your unit tests (protected)
+ test_system.cpp Your system tests (protected)
+ test_tmp36.cpp Library driver tests
+ CMakeLists.txt Fetches Google Test, compiles tests
+ build.sh / build.bat Compile sketch
+ upload.sh / upload.bat Compile + upload to board
+ monitor.sh / monitor.bat Serial monitor
+ test.sh / test.bat Run host-side tests
+ .anvil.toml Project config (tracked by git)
+ .anvil.local Machine-specific port (gitignored)
+ .anvilignore File protection rules for refresh
```
-All hardware access goes through the `Hal` interface in `lib/hal/hal.h`.
-Application code in `lib/app/` depends only on `Hal`, never on `Arduino.h`
-directly. This means the app logic compiles and runs on the host for testing
-with Google Mock -- no board required.
+The key architectural rule: application code in `lib/app/` depends only on
+the `Hal` interface, never on `Arduino.h`. The sketch creates the real HAL
+and passes it in. Tests create a mock or simulator HAL instead. This is
+constructor injection -- the simplest form of dependency inversion.
+
+---
+
+## Commands
+
+| Command | Description |
+|---|---|
+| `anvil new NAME [--template T] [--board B]` | Create a new project |
+| `anvil new --list-templates` | Show available templates |
+| `anvil new --list-boards` | Show available board presets |
+| `anvil setup` | Install arduino-cli and AVR core |
+| `anvil doctor [--fix]` | Check system prerequisites |
+| `anvil devices [--set] [--get] [--clear]` | Manage serial port assignment |
+| `anvil add NAME [--pin P]` | Install a device library |
+| `anvil remove NAME` | Remove a device library |
+| `anvil lib [--available]` | List installed or available libraries |
+| `anvil pin --assign NAME PIN [--mode M]` | Assign a pin |
+| `anvil pin --generate` | Generate pins.h header |
+| `anvil pin --audit [--brief]` | Check wiring against library requirements |
+| `anvil pin --capabilities` | Show board pin capabilities |
+| `anvil pin --init-from BOARD` | Copy pin assignments from another board |
+| `anvil board --add NAME [--id FQBN] [--baud N]` | Add a board profile |
+| `anvil board --remove NAME` | Remove a board profile |
+| `anvil board --default NAME` | Set the default board |
+| `anvil refresh [--force] [--file P] [--ignore P] [--unignore P]` | Update project infrastructure |
+
+---
## Configuration
### .anvil.toml (tracked by git)
-Shared project settings. Edit this to change the board, baud rate, or
-compiler flags:
-
```toml
[project]
-name = "blink"
+name = "weather_station"
anvil_version = "1.0.0"
+template = "weather"
[build]
-fqbn = "arduino:avr:uno"
+default = "uno"
warnings = "more"
-include_dirs = ["lib/hal", "lib/app"]
+include_dirs = ["lib/hal", "lib/app", "lib/drivers/tmp36"]
extra_flags = ["-Werror"]
-[monitor]
+[boards.uno]
+fqbn = "arduino:avr:uno"
baud = 115200
+
+[boards.mega]
+fqbn = "arduino:avr:mega:cpu=atmega2560"
+baud = 115200
+
+[libraries]
+tmp36 = "0.1.0"
+
+[pins.uno]
+tmp36_data = { pin = 14, mode = "analog" }
```
-### .anvil.local (not tracked by git)
-
-Machine-specific settings, created by `anvil devices --set`:
+### .anvil.local (gitignored)
```toml
port = "COM3"
vid_pid = "0403:6001"
```
+---
+
## Building from Source
```bash
cargo build --release
```
-The release binary is at `target/release/anvil` (Linux) or
-`target\release\anvil.exe` (Windows).
+Binary at `target/release/anvil` (Linux) or `target\release\anvil.exe`
+(Windows). Requires Rust 2021 edition.
+
+The test suite:
+
+```bash
+cargo test
+```
+
+615 tests (137 unit + 478 integration), ~4 seconds, zero warnings.
+
+---
## License
diff --git a/docs/terminal-demo.svg b/docs/terminal-demo.svg
new file mode 100644
index 0000000..5a8cfb2
--- /dev/null
+++ b/docs/terminal-demo.svg
@@ -0,0 +1,90 @@
+